Compare commits
10 Commits
bedrock_fi
...
update.fro
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f88ecf904a | ||
|
|
2ff04e3463 | ||
|
|
ed6aa03c8e | ||
|
|
705b01c68c | ||
|
|
9372110672 | ||
|
|
72502416c6 | ||
|
|
668b22e615 | ||
|
|
4901908f5d | ||
|
|
dc7a5414c0 | ||
|
|
40a52385ba |
77
.changeset/bright-windows-sing.md
Normal file
77
.changeset/bright-windows-sing.md
Normal file
@@ -0,0 +1,77 @@
|
||||
---
|
||||
"task-master-ai": minor
|
||||
---
|
||||
|
||||
Add comprehensive AI-powered research command with intelligent context gathering and interactive follow-ups.
|
||||
|
||||
The new `research` command provides AI-powered research capabilities that automatically gather relevant project context to answer your questions. The command intelligently selects context from multiple sources and supports interactive follow-up questions in CLI mode.
|
||||
|
||||
**Key Features:**
|
||||
|
||||
- **Intelligent Task Discovery**: Automatically finds relevant tasks and subtasks using fuzzy search based on your query keywords, supplementing any explicitly provided task IDs
|
||||
- **Multi-Source Context**: Gathers context from tasks, files, project structure, and custom text to provide comprehensive answers
|
||||
- **Interactive Follow-ups**: CLI users can ask follow-up questions that build on the conversation history while allowing fresh context discovery for each question
|
||||
- **Flexible Detail Levels**: Choose from low (concise), medium (balanced), or high (comprehensive) response detail levels
|
||||
- **Token Transparency**: Displays detailed token breakdown showing context size, sources, and estimated costs
|
||||
- **Enhanced Display**: Syntax-highlighted code blocks and structured output with clear visual separation
|
||||
|
||||
**Usage Examples:**
|
||||
|
||||
```bash
|
||||
# Basic research with auto-discovered context
|
||||
task-master research "How should I implement user authentication?"
|
||||
|
||||
# Research with specific task context
|
||||
task-master research "What's the best approach for this?" --id=15,23.2
|
||||
|
||||
# Research with file context and project tree
|
||||
task-master research "How does the current auth system work?" --files=src/auth.js,config/auth.json --tree
|
||||
|
||||
# Research with custom context and low detail
|
||||
task-master research "Quick implementation steps?" --context="Using JWT tokens" --detail=low
|
||||
```
|
||||
|
||||
**Context Sources:**
|
||||
|
||||
- **Tasks**: Automatically discovers relevant tasks/subtasks via fuzzy search, plus any explicitly specified via `--id`
|
||||
- **Files**: Include specific files via `--files` for code-aware responses
|
||||
- **Project Tree**: Add `--tree` to include project structure overview
|
||||
- **Custom Context**: Provide additional context via `--context` for domain-specific information
|
||||
|
||||
**Interactive Features (CLI only):**
|
||||
|
||||
- Follow-up questions that maintain conversation history
|
||||
- Fresh fuzzy search for each follow-up to discover newly relevant tasks
|
||||
- Cumulative context building across the conversation
|
||||
- Clean visual separation between exchanges
|
||||
- **Save to Tasks**: Save entire research conversations (including follow-ups) directly to task or subtask details with timestamps
|
||||
- **Clean Menu Interface**: Streamlined inquirer-based menu for follow-up actions without redundant UI elements
|
||||
|
||||
**Save Functionality:**
|
||||
|
||||
The research command now supports saving complete conversation threads to tasks or subtasks:
|
||||
|
||||
- Save research results and follow-up conversations to any task (e.g., "15") or subtask (e.g., "15.2")
|
||||
- Automatic timestamping and formatting of conversation history
|
||||
- Validation of task/subtask existence before saving
|
||||
- Appends to existing task details without overwriting content
|
||||
- Supports both CLI interactive mode and MCP programmatic access via `--save-to` flag
|
||||
|
||||
**Enhanced CLI Options:**
|
||||
|
||||
```bash
|
||||
# Auto-save research results to a task
|
||||
task-master research "Implementation approach?" --save-to=15
|
||||
|
||||
# Combine auto-save with context gathering
|
||||
task-master research "How to optimize this?" --id=23 --save-to=23.1
|
||||
```
|
||||
|
||||
**MCP Integration:**
|
||||
|
||||
- `saveTo` parameter for automatic saving to specified task/subtask ID
|
||||
- Structured response format with telemetry data
|
||||
- Silent operation mode for programmatic usage
|
||||
- Full feature parity with CLI except interactive follow-ups
|
||||
|
||||
The research command integrates with the existing AI service layer and supports all configured AI providers. Both CLI and MCP interfaces provide comprehensive research capabilities with intelligent context gathering and flexible output options.
|
||||
5
.changeset/chatty-rats-talk.md
Normal file
5
.changeset/chatty-rats-talk.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"task-master-ai": patch
|
||||
---
|
||||
|
||||
Fix Cursor deeplink installation by providing copy-paste instructions for GitHub compatibility
|
||||
13
.changeset/cold-pears-poke.md
Normal file
13
.changeset/cold-pears-poke.md
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
'task-master-ai': patch
|
||||
---
|
||||
|
||||
Fix critical bugs in task move functionality:
|
||||
|
||||
- **Fixed moving tasks to become subtasks of empty parents**: When moving a task to become a subtask of a parent that had no existing subtasks (e.g., task 89 → task 98.1), the operation would fail with validation errors.
|
||||
|
||||
- **Fixed moving subtasks between parents**: Subtasks can now be properly moved between different parent tasks, including to parents that previously had no subtasks.
|
||||
|
||||
- **Improved comma-separated batch moves**: Multiple tasks can now be moved simultaneously using comma-separated IDs (e.g., "88,90" → "92,93") with proper error handling and atomic operations.
|
||||
|
||||
These fixes enables proper task hierarchy reorganization for corner cases that were previously broken.
|
||||
5
.changeset/fluffy-waves-allow.md
Normal file
5
.changeset/fluffy-waves-allow.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"task-master-ai": patch
|
||||
---
|
||||
|
||||
Adds ability to automatically create/switch tags to match the current git branch. The configuration to enable the git workflow and then use the auto switching is in config.json."
|
||||
18
.changeset/free-pants-rescue.md
Normal file
18
.changeset/free-pants-rescue.md
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
"task-master-ai": minor
|
||||
---
|
||||
|
||||
Enhance update-task with --append flag for timestamped task updates
|
||||
|
||||
Adds the `--append` flag to `update-task` command, enabling it to behave like `update-subtask` with timestamped information appending. This provides more flexible task updating options:
|
||||
|
||||
**CLI Enhancement:**
|
||||
- `task-master update-task --id=5 --prompt="New info"` - Full task update (existing behavior)
|
||||
- `task-master update-task --id=5 --append --prompt="Progress update"` - Append timestamped info to task details
|
||||
|
||||
**Full MCP Integration:**
|
||||
- MCP tool `update_task` now supports `append` parameter
|
||||
- Seamless integration with Cursor and other MCP clients
|
||||
- Consistent behavior between CLI and MCP interfaces
|
||||
|
||||
Instead of requiring separate subtask creation for progress tracking, you can now append timestamped information directly to parent tasks while preserving the option for comprehensive task updates.
|
||||
@@ -2,4 +2,4 @@
|
||||
"task-master-ai": patch
|
||||
---
|
||||
|
||||
Improves Amazon Bedrock support
|
||||
Update o3 model price
|
||||
11
.changeset/late-dryers-relax.md
Normal file
11
.changeset/late-dryers-relax.md
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
"task-master-ai": minor
|
||||
---
|
||||
|
||||
Add --tag flag support to core commands for multi-context task management. Commands like parse-prd, analyze-complexity, and others now support targeting specific task lists, enabling rapid prototyping and parallel development workflows.
|
||||
|
||||
Key features:
|
||||
- parse-prd --tag=feature-name: Parse PRDs into separate task contexts on the fly
|
||||
- analyze-complexity --tag=branch: Generate tag-specific complexity reports
|
||||
- All task operations can target specific contexts while preserving other lists
|
||||
- Non-existent tags are created automatically for seamless workflow
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
'task-master-ai': minor
|
||||
---
|
||||
|
||||
Added comprehensive rule profile management:
|
||||
|
||||
**New Profile Support**: Added comprehensive IDE profile support with eight specialized profiles: Claude Code, Cline, Codex, Cursor, Roo, Trae, VS Code, and Windsurf. Each profile is optimized for its respective IDE with appropriate mappings and configuration.
|
||||
**Initialization**: You can now specify which rule profiles to include at project initialization using `--rules <profiles>` or `-r <profiles>` (e.g., `task-master init -r cursor,roo`). Only the selected profiles and configuration are included.
|
||||
**Add/Remove Commands**: `task-master rules add <profiles>` and `task-master rules remove <profiles>` let you manage specific rule profiles and MCP config after initialization, supporting multiple profiles at once.
|
||||
**Interactive Setup**: `task-master rules setup` launches an interactive prompt to select which rule profiles to add to your project. This does **not** re-initialize your project or affect shell aliases; it only manages rules.
|
||||
**Selective Removal**: Rules removal intelligently preserves existing non-Task Master rules and files and only removes Task Master-specific rules. Profile directories are only removed when completely empty and all conditions are met (no existing rules, no other files/folders, MCP config completely removed).
|
||||
**Safety Features**: Confirmation messages clearly explain that only Task Master-specific rules and MCP configurations will be removed, while preserving existing custom rules and other files.
|
||||
**Robust Validation**: Includes comprehensive checks for array types in MCP config processing and error handling throughout the rules management system.
|
||||
|
||||
This enables more flexible, rule-specific project setups with intelligent cleanup that preserves user customizations while safely managing Task Master components.
|
||||
|
||||
- Resolves #338
|
||||
8
.changeset/nasty-chefs-add.md
Normal file
8
.changeset/nasty-chefs-add.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
"task-master-ai": patch
|
||||
---
|
||||
|
||||
Fixes issue with expand CLI command "Complexity report not found"
|
||||
|
||||
- Closes #735
|
||||
- Closes #728
|
||||
31
.changeset/pre.json
Normal file
31
.changeset/pre.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"mode": "exit",
|
||||
"tag": "rc",
|
||||
"initialVersions": {
|
||||
"task-master-ai": "0.16.2"
|
||||
},
|
||||
"changesets": [
|
||||
"bright-windows-sing",
|
||||
"chatty-rats-talk",
|
||||
"cold-pears-poke",
|
||||
"curly-dragons-design",
|
||||
"eleven-news-check",
|
||||
"fluffy-waves-allow",
|
||||
"four-cups-enter",
|
||||
"free-pants-rescue",
|
||||
"large-wolves-strive",
|
||||
"late-dryers-relax",
|
||||
"nasty-chefs-add",
|
||||
"pink-houses-lay",
|
||||
"polite-areas-shave",
|
||||
"quick-flies-sniff",
|
||||
"six-cups-see",
|
||||
"slick-webs-lead",
|
||||
"slow-lies-make",
|
||||
"stale-bats-sin",
|
||||
"tiny-ads-decide",
|
||||
"two-lies-start",
|
||||
"vast-shrimps-happen",
|
||||
"yellow-olives-admire"
|
||||
]
|
||||
}
|
||||
5
.changeset/quick-flies-sniff.md
Normal file
5
.changeset/quick-flies-sniff.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"task-master-ai": patch
|
||||
---
|
||||
|
||||
Fix issue with generate command which was creating tasks in the legacy tasks location.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"task-master-ai": patch
|
||||
---
|
||||
|
||||
Fix contextGatherer bug when adding a task `Cannot read properties of undefined (reading 'forEach')`
|
||||
136
.changeset/six-cups-see.md
Normal file
136
.changeset/six-cups-see.md
Normal file
@@ -0,0 +1,136 @@
|
||||
---
|
||||
"task-master-ai": minor
|
||||
---
|
||||
|
||||
Introduces Tagged Lists: AI Multi-Context Task Management System
|
||||
|
||||
This major release introduces Tagged Lists, a comprehensive system that transforms Task Master into a multi-context task management powerhouse. You can now organize tasks into completely isolated contexts, enabling parallel (agentic) development workflows, team collaboration, and project experimentation without conflicts.
|
||||
|
||||
**🏷️ Tagged Task Lists Architecture:**
|
||||
|
||||
The new tagged system fundamentally changes how tasks are organized:
|
||||
- **Legacy Format**: `{ "tasks": [...] }`
|
||||
- **New Tagged Format**: `{ "master": { "tasks": [...], "metadata": {...} }, "feature-xyz": { "tasks": [...], "metadata": {...} } }`
|
||||
- **Automatic Migration**: Existing projects seamlessly migrate to tagged format with zero user intervention
|
||||
- **State Management**: New `.taskmaster/state.json` tracks current tag, last switched time, and migration status
|
||||
- **Configuration Integration**: Enhanced `.taskmaster/config.json` with tag-specific settings and defaults
|
||||
|
||||
**🚀 Complete Tag Management Suite:**
|
||||
|
||||
**Core Tag Commands:**
|
||||
- `task-master tags [--show-metadata]` - List all tags with task counts, completion stats, and metadata
|
||||
- `task-master add-tag <name> [options]` - Create new tag contexts with optional task copying
|
||||
- `task-master delete-tag <name> [--yes]` - Delete tags with double confirmation protection
|
||||
- `task-master use-tag <name>` - Switch contexts and immediately see next available task
|
||||
- `task-master rename-tag <old> <new>` - Rename tags with automatic current tag reference updates
|
||||
- `task-master copy-tag <source> <target> [options]` - Duplicate tag contexts for experimentation
|
||||
|
||||
**🤖 Full MCP Integration for Tag Management:**
|
||||
|
||||
Task Master's multi-context capabilities are now fully exposed through the MCP server, enabling powerful agentic workflows:
|
||||
- **`list_tags`**: List all available tag contexts.
|
||||
- **`add_tag`**: Programmatically create new tags.
|
||||
- **`delete_tag`**: Remove tag contexts.
|
||||
- **`use_tag`**: Switch the agent's active task context.
|
||||
- **`rename_tag`**: Rename existing tags.
|
||||
- **`copy_tag`**: Duplicate entire task contexts for experimentation.
|
||||
|
||||
**Tag Creation Options:**
|
||||
- `--copy-from-current` - Copy tasks from currently active tag
|
||||
- `--copy-from=<tag>` - Copy tasks from specific tag
|
||||
- `--from-branch` - Creates a new tag usin active git branch name (for `add-tag` only)
|
||||
- `--description="<text>"` - Add custom tag descriptions
|
||||
- Empty tag creation for fresh contexts
|
||||
|
||||
**🎯 Universal --tag Flag Support:**
|
||||
|
||||
Every task operation now supports tag-specific execution:
|
||||
- `task-master list --tag=feature-branch` - View tasks in specific context
|
||||
- `task-master add-task --tag=experiment --prompt="..."` - Create tasks in specific tag
|
||||
- `task-master parse-prd document.txt --tag=v2-redesign` - Parse PRDs into dedicated contexts
|
||||
- `task-master analyze-complexity --tag=performance-work` - Generate tag-specific reports
|
||||
- `task-master set-status --tag=hotfix --id=5 --status=done` - Update tasks in specific contexts
|
||||
- `task-master expand --tag=research --id=3` - Break down tasks within tag contexts
|
||||
|
||||
**📊 Enhanced Workflow Features:**
|
||||
|
||||
**Smart Context Switching:**
|
||||
- `use-tag` command shows immediate next task after switching
|
||||
- Automatic tag creation when targeting non-existent tags
|
||||
- Current tag persistence across terminal sessions
|
||||
- Branch-tag mapping for future Git integration
|
||||
|
||||
**Intelligent File Management:**
|
||||
- Tag-specific complexity reports: `task-complexity-report_tagname.json`
|
||||
- Master tag uses default filenames: `task-complexity-report.json`
|
||||
- Automatic file isolation prevents cross-tag contamination
|
||||
|
||||
**Advanced Confirmation Logic:**
|
||||
- Commands only prompt when target tag has existing tasks
|
||||
- Empty tags allow immediate operations without confirmation
|
||||
- Smart append vs overwrite detection
|
||||
|
||||
**🔄 Seamless Migration & Compatibility:**
|
||||
|
||||
**Zero-Disruption Migration:**
|
||||
- Existing `tasks.json` files automatically migrate on first command
|
||||
- Master tag receives proper metadata (creation date, description)
|
||||
- Migration notice shown once with helpful explanation
|
||||
- All existing commands work identically to before
|
||||
|
||||
**State Management:**
|
||||
- `.taskmaster/state.json` tracks current tag and migration status
|
||||
- Automatic state creation and maintenance
|
||||
- Branch-tag mapping foundation for Git integration
|
||||
- Migration notice tracking to avoid repeated notifications
|
||||
- Grounds for future context additions
|
||||
|
||||
**Backward Compatibility:**
|
||||
- All existing workflows continue unchanged
|
||||
- Legacy commands work exactly as before
|
||||
- Gradual adoption - users can ignore tags entirely if desired
|
||||
- No breaking changes to existing tasks or file formats
|
||||
|
||||
**💡 Real-World Use Cases:**
|
||||
|
||||
**Team Collaboration:**
|
||||
- `task-master add-tag alice --copy-from-current` - Create teammate-specific contexts
|
||||
- `task-master add-tag bob --copy-from=master` - Onboard new team members
|
||||
- `task-master use-tag alice` - Switch to teammate's work context
|
||||
|
||||
**Feature Development:**
|
||||
- `task-master parse-prd feature-spec.txt --tag=user-auth` - Dedicated feature planning
|
||||
- `task-master add-tag experiment --copy-from=user-auth` - Safe experimentation
|
||||
- `task-master analyze-complexity --tag=user-auth` - Feature-specific analysis
|
||||
|
||||
**Release Management:**
|
||||
- `task-master add-tag v2.0 --description="Next major release"` - Version-specific planning
|
||||
- `task-master copy-tag master v2.1` - Release branch preparation
|
||||
- `task-master use-tag hotfix` - Emergency fix context
|
||||
|
||||
**Project Phases:**
|
||||
- `task-master add-tag research --description="Discovery phase"` - Research tasks
|
||||
- `task-master add-tag implementation --copy-from=research` - Development phase
|
||||
- `task-master add-tag testing --copy-from=implementation` - QA phase
|
||||
|
||||
**🛠️ Technical Implementation:**
|
||||
|
||||
**Data Structure:**
|
||||
- Tagged format with complete isolation between contexts
|
||||
- Rich metadata per tag (creation date, description, update tracking)
|
||||
- Automatic metadata enhancement for existing tags
|
||||
- Clean separation of tag data and internal state
|
||||
|
||||
**Performance Optimizations:**
|
||||
- Dynamic task counting without stored counters
|
||||
- Efficient tag resolution and caching
|
||||
- Minimal file I/O with smart data loading
|
||||
- Responsive table layouts adapting to terminal width
|
||||
|
||||
**Error Handling:**
|
||||
- Comprehensive validation for tag names (alphanumeric, hyphens, underscores)
|
||||
- Reserved name protection (master, main, default)
|
||||
- Graceful handling of missing tags and corrupted data
|
||||
- Detailed error messages with suggested corrections
|
||||
|
||||
This release establishes the foundation for advanced multi-context workflows while maintaining the simplicity and power that makes Task Master effective for individual developers.
|
||||
24
.changeset/slick-webs-lead.md
Normal file
24
.changeset/slick-webs-lead.md
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
"task-master-ai": minor
|
||||
---
|
||||
|
||||
Research Save-to-File Feature & Critical MCP Tag Corruption Fix
|
||||
|
||||
**🔬 New Research Save-to-File Functionality:**
|
||||
|
||||
Added comprehensive save-to-file capability to the research command, enabling users to preserve research sessions for future reference and documentation.
|
||||
|
||||
**CLI Integration:**
|
||||
- New `--save-file` flag for `task-master research` command
|
||||
- Consistent with existing `--save` and `--save-to` flags for intuitive usage
|
||||
- Interactive "Save to file" option in follow-up questions menu
|
||||
|
||||
**MCP Integration:**
|
||||
- New `saveToFile` boolean parameter for the `research` MCP tool
|
||||
- Enables programmatic research saving for AI agents and integrated tools
|
||||
|
||||
**File Management:**
|
||||
- Automatically creates `.taskmaster/docs/research/` directory structure
|
||||
- Generates timestamped, slugified filenames (e.g., `2025-01-13_what-is-typescript.md`)
|
||||
- Comprehensive Markdown format with metadata headers including query, timestamp, and context sources
|
||||
- Clean conversation history formatting without duplicate information
|
||||
5
.changeset/slow-lies-make.md
Normal file
5
.changeset/slow-lies-make.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"task-master-ai": minor
|
||||
---
|
||||
|
||||
No longer automatically creates individual task files as they are not used by the applicatoin. You can still generate them anytime using the `generate` command.
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
"task-master-ai": patch
|
||||
---
|
||||
|
||||
Improve provider validation system with clean constants structure
|
||||
|
||||
- **Fixed "Invalid provider hint" errors**: Resolved validation failures for Azure, Vertex, and Bedrock providers
|
||||
- **Improved search UX**: Integrated search for better model discovery with real-time filtering
|
||||
- **Better organization**: Moved custom provider options to bottom of model selection with clear section separators
|
||||
|
||||
This change ensures all custom providers (Azure, Vertex, Bedrock, OpenRouter, Ollama) work correctly in `task-master models --setup`
|
||||
19
.changeset/stale-bats-sin.md
Normal file
19
.changeset/stale-bats-sin.md
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
'task-master-ai': minor
|
||||
---
|
||||
|
||||
Enhanced get-task/show command to support comma-separated task IDs for efficient batch operations
|
||||
|
||||
**New Features:**
|
||||
- **Multiple Task Retrieval**: Pass comma-separated IDs to get/show multiple tasks at once (e.g., `task-master show 1,3,5` or MCP `get_task` with `id: "1,3,5"`)
|
||||
- **Smart Display Logic**: Single ID shows detailed view, multiple IDs show compact summary table with interactive options
|
||||
- **Batch Action Menu**: Interactive menu for multiple tasks with copy-paste ready commands for common operations (mark as done/in-progress, expand all, view dependencies, etc.)
|
||||
- **MCP Array Response**: MCP tool returns structured array of task objects for efficient AI agent context gathering
|
||||
|
||||
**Benefits:**
|
||||
- **Faster Context Gathering**: AI agents can collect multiple tasks/subtasks in one call instead of iterating
|
||||
- **Improved Workflow**: Interactive batch operations reduce repetitive command execution
|
||||
- **Better UX**: Responsive layout adapts to terminal width, maintains consistency with existing UI patterns
|
||||
- **API Efficiency**: RESTful array responses in MCP format enable more sophisticated integrations
|
||||
|
||||
This enhancement maintains full backward compatibility while significantly improving efficiency for both human users and AI agents working with multiple tasks.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"task-master-ai": patch
|
||||
---
|
||||
|
||||
Fix weird `task-master init` bug when using in certain environments
|
||||
7
.changeset/tiny-ads-decide.md
Normal file
7
.changeset/tiny-ads-decide.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"task-master-ai": minor
|
||||
---
|
||||
|
||||
Adds support for filtering tasks by multiple statuses at once using comma-separated statuses.
|
||||
|
||||
Example: `cancelled,deferred`
|
||||
5
.changeset/two-lies-start.md
Normal file
5
.changeset/two-lies-start.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"task-master-ai": patch
|
||||
---
|
||||
|
||||
Improves dependency management when moving tasks by updating subtask dependencies that reference sibling subtasks by their old parent-based ID
|
||||
5
.changeset/yellow-olives-admire.md
Normal file
5
.changeset/yellow-olives-admire.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"task-master-ai": minor
|
||||
---
|
||||
|
||||
Adds tag to CLI output so you know which tag you are performing operations on. Already supported in the MCP response.
|
||||
@@ -1,200 +1,19 @@
|
||||
---
|
||||
description: Guide for using Taskmaster to manage task-driven development workflows
|
||||
description: Guide for using Task Master to manage task-driven development workflows
|
||||
globs: **/*
|
||||
alwaysApply: true
|
||||
---
|
||||
# Task Master Development Workflow
|
||||
|
||||
# Taskmaster Development Workflow
|
||||
|
||||
This guide outlines the standard process for using Taskmaster to manage software development projects. It is written as a set of instructions for you, the AI agent.
|
||||
|
||||
- **Your Default Stance**: For most projects, the user can work directly within the `master` task context. Your initial actions should operate on this default context unless a clear pattern for multi-context work emerges.
|
||||
- **Your Goal**: Your role is to elevate the user's workflow by intelligently introducing advanced features like **Tagged Task Lists** when you detect the appropriate context. Do not force tags on the user; suggest them as a helpful solution to a specific need.
|
||||
|
||||
## The Basic Loop
|
||||
The fundamental development cycle you will facilitate is:
|
||||
1. **`list`**: Show the user what needs to be done.
|
||||
2. **`next`**: Help the user decide what to work on.
|
||||
3. **`show <id>`**: Provide details for a specific task.
|
||||
4. **`expand <id>`**: Break down a complex task into smaller, manageable subtasks.
|
||||
5. **Implement**: The user writes the code and tests.
|
||||
6. **`update-subtask`**: Log progress and findings on behalf of the user.
|
||||
7. **`set-status`**: Mark tasks and subtasks as `done` as work is completed.
|
||||
8. **Repeat**.
|
||||
|
||||
All your standard command executions should operate on the user's current task context, which defaults to `master`.
|
||||
|
||||
---
|
||||
|
||||
## Standard Development Workflow Process
|
||||
|
||||
### Simple Workflow (Default Starting Point)
|
||||
|
||||
For new projects or when users are getting started, operate within the `master` tag context:
|
||||
|
||||
- Start new projects by running `initialize_project` tool / `task-master init` or `parse_prd` / `task-master parse-prd --input='<prd-file.txt>'` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) to generate initial tasks.json with tagged structure
|
||||
- Configure rule sets during initialization with `--rules` flag (e.g., `task-master init --rules cursor,windsurf`) or manage them later with `task-master rules add/remove` commands
|
||||
- Begin coding sessions with `get_tasks` / `task-master list` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) to see current tasks, status, and IDs
|
||||
- Determine the next task to work on using `next_task` / `task-master next` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc))
|
||||
- Analyze task complexity with `analyze_project_complexity` / `task-master analyze-complexity --research` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) before breaking down tasks
|
||||
- Review complexity report using `complexity_report` / `task-master complexity-report` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc))
|
||||
- Select tasks based on dependencies (all marked 'done'), priority level, and ID order
|
||||
- View specific task details using `get_task` / `task-master show <id>` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) to understand implementation requirements
|
||||
- Break down complex tasks using `expand_task` / `task-master expand --id=<id> --force --research` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) with appropriate flags like `--force` (to replace existing subtasks) and `--research`
|
||||
- Implement code following task details, dependencies, and project standards
|
||||
- Mark completed tasks with `set_task_status` / `task-master set-status --id=<id> --status=done` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc))
|
||||
- Update dependent tasks when implementation differs from original plan using `update` / `task-master update --from=<id> --prompt="..."` or `update_task` / `task-master update-task --id=<id> --prompt="..."` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc))
|
||||
|
||||
---
|
||||
|
||||
## Leveling Up: Agent-Led Multi-Context Workflows
|
||||
|
||||
While the basic workflow is powerful, your primary opportunity to add value is by identifying when to introduce **Tagged Task Lists**. These patterns are your tools for creating a more organized and efficient development environment for the user, especially if you detect agentic or parallel development happening across the same session.
|
||||
|
||||
**Critical Principle**: Most users should never see a difference in their experience. Only introduce advanced workflows when you detect clear indicators that the project has evolved beyond simple task management.
|
||||
|
||||
### When to Introduce Tags: Your Decision Patterns
|
||||
|
||||
Here are the patterns to look for. When you detect one, you should propose the corresponding workflow to the user.
|
||||
|
||||
#### Pattern 1: Simple Git Feature Branching
|
||||
This is the most common and direct use case for tags.
|
||||
|
||||
- **Trigger**: The user creates a new git branch (e.g., `git checkout -b feature/user-auth`).
|
||||
- **Your Action**: Propose creating a new tag that mirrors the branch name to isolate the feature's tasks from `master`.
|
||||
- **Your Suggested Prompt**: *"I see you've created a new branch named 'feature/user-auth'. To keep all related tasks neatly organized and separate from your main list, I can create a corresponding task tag for you. This helps prevent merge conflicts in your `tasks.json` file later. Shall I create the 'feature-user-auth' tag?"*
|
||||
- **Tool to Use**: `task-master add-tag --from-branch`
|
||||
|
||||
#### Pattern 2: Team Collaboration
|
||||
- **Trigger**: The user mentions working with teammates (e.g., "My teammate Alice is handling the database schema," or "I need to review Bob's work on the API.").
|
||||
- **Your Action**: Suggest creating a separate tag for the user's work to prevent conflicts with shared master context.
|
||||
- **Your Suggested Prompt**: *"Since you're working with Alice, I can create a separate task context for your work to avoid conflicts. This way, Alice can continue working with the master list while you have your own isolated context. When you're ready to merge your work, we can coordinate the tasks back to master. Shall I create a tag for your current work?"*
|
||||
- **Tool to Use**: `task-master add-tag my-work --copy-from-current --description="My tasks while collaborating with Alice"`
|
||||
|
||||
#### Pattern 3: Experiments or Risky Refactors
|
||||
- **Trigger**: The user wants to try something that might not be kept (e.g., "I want to experiment with switching our state management library," or "Let's refactor the old API module, but I want to keep the current tasks as a reference.").
|
||||
- **Your Action**: Propose creating a sandboxed tag for the experimental work.
|
||||
- **Your Suggested Prompt**: *"This sounds like a great experiment. To keep these new tasks separate from our main plan, I can create a temporary 'experiment-zustand' tag for this work. If we decide not to proceed, we can simply delete the tag without affecting the main task list. Sound good?"*
|
||||
- **Tool to Use**: `task-master add-tag experiment-zustand --description="Exploring Zustand migration"`
|
||||
|
||||
#### Pattern 4: Large Feature Initiatives (PRD-Driven)
|
||||
This is a more structured approach for significant new features or epics.
|
||||
|
||||
- **Trigger**: The user describes a large, multi-step feature that would benefit from a formal plan.
|
||||
- **Your Action**: Propose a comprehensive, PRD-driven workflow.
|
||||
- **Your Suggested Prompt**: *"This sounds like a significant new feature. To manage this effectively, I suggest we create a dedicated task context for it. Here's the plan: I'll create a new tag called 'feature-xyz', then we can draft a Product Requirements Document (PRD) together to scope the work. Once the PRD is ready, I'll automatically generate all the necessary tasks within that new tag. How does that sound?"*
|
||||
- **Your Implementation Flow**:
|
||||
1. **Create an empty tag**: `task-master add-tag feature-xyz --description "Tasks for the new XYZ feature"`. You can also start by creating a git branch if applicable, and then create the tag from that branch.
|
||||
2. **Collaborate & Create PRD**: Work with the user to create a detailed PRD file (e.g., `.taskmaster/docs/feature-xyz-prd.txt`).
|
||||
3. **Parse PRD into the new tag**: `task-master parse-prd .taskmaster/docs/feature-xyz-prd.txt --tag feature-xyz`
|
||||
4. **Prepare the new task list**: Follow up by suggesting `analyze-complexity` and `expand-all` for the newly created tasks within the `feature-xyz` tag.
|
||||
|
||||
#### Pattern 5: Version-Based Development
|
||||
Tailor your approach based on the project maturity indicated by tag names.
|
||||
|
||||
- **Prototype/MVP Tags** (`prototype`, `mvp`, `poc`, `v0.x`):
|
||||
- **Your Approach**: Focus on speed and functionality over perfection
|
||||
- **Task Generation**: Create tasks that emphasize "get it working" over "get it perfect"
|
||||
- **Complexity Level**: Lower complexity, fewer subtasks, more direct implementation paths
|
||||
- **Research Prompts**: Include context like "This is a prototype - prioritize speed and basic functionality over optimization"
|
||||
- **Example Prompt Addition**: *"Since this is for the MVP, I'll focus on tasks that get core functionality working quickly rather than over-engineering."*
|
||||
|
||||
- **Production/Mature Tags** (`v1.0+`, `production`, `stable`):
|
||||
- **Your Approach**: Emphasize robustness, testing, and maintainability
|
||||
- **Task Generation**: Include comprehensive error handling, testing, documentation, and optimization
|
||||
- **Complexity Level**: Higher complexity, more detailed subtasks, thorough implementation paths
|
||||
- **Research Prompts**: Include context like "This is for production - prioritize reliability, performance, and maintainability"
|
||||
- **Example Prompt Addition**: *"Since this is for production, I'll ensure tasks include proper error handling, testing, and documentation."*
|
||||
|
||||
### Advanced Workflow (Tag-Based & PRD-Driven)
|
||||
|
||||
**When to Transition**: Recognize when the project has evolved (or has initiated a project which existing code) beyond simple task management. Look for these indicators:
|
||||
- User mentions teammates or collaboration needs
|
||||
- Project has grown to 15+ tasks with mixed priorities
|
||||
- User creates feature branches or mentions major initiatives
|
||||
- User initializes Taskmaster on an existing, complex codebase
|
||||
- User describes large features that would benefit from dedicated planning
|
||||
|
||||
**Your Role in Transition**: Guide the user to a more sophisticated workflow that leverages tags for organization and PRDs for comprehensive planning.
|
||||
|
||||
#### Master List Strategy (High-Value Focus)
|
||||
Once you transition to tag-based workflows, the `master` tag should ideally contain only:
|
||||
- **High-level deliverables** that provide significant business value
|
||||
- **Major milestones** and epic-level features
|
||||
- **Critical infrastructure** work that affects the entire project
|
||||
- **Release-blocking** items
|
||||
|
||||
**What NOT to put in master**:
|
||||
- Detailed implementation subtasks (these go in feature-specific tags' parent tasks)
|
||||
- Refactoring work (create dedicated tags like `refactor-auth`)
|
||||
- Experimental features (use `experiment-*` tags)
|
||||
- Team member-specific tasks (use person-specific tags)
|
||||
|
||||
#### PRD-Driven Feature Development
|
||||
|
||||
**For New Major Features**:
|
||||
1. **Identify the Initiative**: When user describes a significant feature
|
||||
2. **Create Dedicated Tag**: `add_tag feature-[name] --description="[Feature description]"`
|
||||
3. **Collaborative PRD Creation**: Work with user to create comprehensive PRD in `.taskmaster/docs/feature-[name]-prd.txt`
|
||||
4. **Parse & Prepare**:
|
||||
- `parse_prd .taskmaster/docs/feature-[name]-prd.txt --tag=feature-[name]`
|
||||
- `analyze_project_complexity --tag=feature-[name] --research`
|
||||
- `expand_all --tag=feature-[name] --research`
|
||||
5. **Add Master Reference**: Create a high-level task in `master` that references the feature tag
|
||||
|
||||
**For Existing Codebase Analysis**:
|
||||
When users initialize Taskmaster on existing projects:
|
||||
1. **Codebase Discovery**: Use your native tools for producing deep context about the code base. You may use `research` tool with `--tree` and `--files` to collect up to date information using the existing architecture as context.
|
||||
2. **Collaborative Assessment**: Work with user to identify improvement areas, technical debt, or new features
|
||||
3. **Strategic PRD Creation**: Co-author PRDs that include:
|
||||
- Current state analysis (based on your codebase research)
|
||||
- Proposed improvements or new features
|
||||
- Implementation strategy considering existing code
|
||||
4. **Tag-Based Organization**: Parse PRDs into appropriate tags (`refactor-api`, `feature-dashboard`, `tech-debt`, etc.)
|
||||
5. **Master List Curation**: Keep only the most valuable initiatives in master
|
||||
|
||||
The parse-prd's `--append` flag enables the user to parse multple PRDs within tags or across tags. PRDs should be focused and the number of tasks they are parsed into should be strategically chosen relative to the PRD's complexity and level of detail.
|
||||
|
||||
### Workflow Transition Examples
|
||||
|
||||
**Example 1: Simple → Team-Based**
|
||||
```
|
||||
User: "Alice is going to help with the API work"
|
||||
Your Response: "Great! To avoid conflicts, I'll create a separate task context for your work. Alice can continue with the master list while you work in your own context. When you're ready to merge, we can coordinate the tasks back together."
|
||||
Action: add_tag my-api-work --copy-from-current --description="My API tasks while collaborating with Alice"
|
||||
```
|
||||
|
||||
**Example 2: Simple → PRD-Driven**
|
||||
```
|
||||
User: "I want to add a complete user dashboard with analytics, user management, and reporting"
|
||||
Your Response: "This sounds like a major feature that would benefit from detailed planning. Let me create a dedicated context for this work and we can draft a PRD together to ensure we capture all requirements."
|
||||
Actions:
|
||||
1. add_tag feature-dashboard --description="User dashboard with analytics and management"
|
||||
2. Collaborate on PRD creation
|
||||
3. parse_prd dashboard-prd.txt --tag=feature-dashboard
|
||||
4. Add high-level "User Dashboard" task to master
|
||||
```
|
||||
|
||||
**Example 3: Existing Project → Strategic Planning**
|
||||
```
|
||||
User: "I just initialized Taskmaster on my existing React app. It's getting messy and I want to improve it."
|
||||
Your Response: "Let me research your codebase to understand the current architecture, then we can create a strategic plan for improvements."
|
||||
Actions:
|
||||
1. research "Current React app architecture and improvement opportunities" --tree --files=src/
|
||||
2. Collaborate on improvement PRD based on findings
|
||||
3. Create tags for different improvement areas (refactor-components, improve-state-management, etc.)
|
||||
4. Keep only major improvement initiatives in master
|
||||
```
|
||||
|
||||
---
|
||||
This guide outlines the typical process for using Task Master to manage software development projects.
|
||||
|
||||
## Primary Interaction: MCP Server vs. CLI
|
||||
|
||||
Taskmaster offers two primary ways to interact:
|
||||
Task Master offers two primary ways to interact:
|
||||
|
||||
1. **MCP Server (Recommended for Integrated Tools)**:
|
||||
- For AI agents and integrated development environments (like Cursor), interacting via the **MCP server is the preferred method**.
|
||||
- The MCP server exposes Taskmaster functionality through a set of tools (e.g., `get_tasks`, `add_subtask`).
|
||||
- The MCP server exposes Task Master functionality through a set of tools (e.g., `get_tasks`, `add_subtask`).
|
||||
- This method offers better performance, structured data exchange, and richer error handling compared to CLI parsing.
|
||||
- Refer to [`mcp.mdc`](mdc:.cursor/rules/mcp.mdc) for details on the MCP architecture and available tools.
|
||||
- A comprehensive list and description of MCP tools and their corresponding CLI commands can be found in [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc).
|
||||
@@ -209,15 +28,62 @@ Taskmaster offers two primary ways to interact:
|
||||
- Refer to [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc) for a detailed command reference.
|
||||
- **Tagged Task Lists**: CLI fully supports the new tagged system with seamless migration.
|
||||
|
||||
## How the Tag System Works (For Your Reference)
|
||||
## Tagged Task Lists System
|
||||
|
||||
- **Data Structure**: Tasks are organized into separate contexts (tags) like "master", "feature-branch", or "v2.0".
|
||||
- **Silent Migration**: Existing projects automatically migrate to use a "master" tag with zero disruption.
|
||||
- **Context Isolation**: Tasks in different tags are completely separate. Changes in one tag do not affect any other tag.
|
||||
- **Manual Control**: The user is always in control. There is no automatic switching. You facilitate switching by using `use-tag <name>`.
|
||||
- **Full CLI & MCP Support**: All tag management commands are available through both the CLI and MCP tools for you to use. Refer to [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc) for a full command list.
|
||||
Task Master now supports **tagged task lists** for multi-context task management:
|
||||
|
||||
---
|
||||
- **Data Structure**: Tasks are organized into separate contexts (tags) like "master", "feature-branch", or "v2.0"
|
||||
- **Seamless Migration**: Existing projects automatically migrate to use a "master" tag with zero disruption
|
||||
- **Backward Compatibility**: All existing commands continue to work exactly as before
|
||||
- **Context Isolation**: Tasks in different tags are completely separate and isolated
|
||||
- **Silent Migration**: The first time you run any Task Master command, your existing tasks.json will be automatically migrated to the new tagged format
|
||||
- **Migration Notice**: You'll see a friendly FYI notice after migration explaining the new system
|
||||
|
||||
**Migration Example**:
|
||||
```json
|
||||
// Before (legacy format)
|
||||
{
|
||||
"tasks": [
|
||||
{ "id": 1, "title": "Setup API", ... }
|
||||
]
|
||||
}
|
||||
|
||||
// After (tagged format - automatic)
|
||||
{
|
||||
"master": {
|
||||
"tasks": [
|
||||
{ "id": 1, "title": "Setup API", ... }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Tag Management**: CLI commands for tag management (`add-tag`, `use-tag`, `list-tags`, `delete-tag`, `rename-tag`, `copy-tag`) are now available with manual git integration via `--from-branch` option.
|
||||
|
||||
## Standard Development Workflow Process
|
||||
|
||||
- Start new projects by running `initialize_project` tool / `task-master init` or `parse_prd` / `task-master parse-prd --input='<prd-file.txt>'` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) to generate initial tasks.json with tagged structure
|
||||
- Begin coding sessions with `get_tasks` / `task-master list` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) to see current tasks, status, and IDs
|
||||
- Determine the next task to work on using `next_task` / `task-master next` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)).
|
||||
- Analyze task complexity with `analyze_project_complexity` / `task-master analyze-complexity --research` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) before breaking down tasks
|
||||
- Review complexity report using `complexity_report` / `task-master complexity-report` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)).
|
||||
- Select tasks based on dependencies (all marked 'done'), priority level, and ID order
|
||||
- Clarify tasks by checking task files in tasks/ directory or asking for user input
|
||||
- View specific task details using `get_task` / `task-master show <id>` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) to understand implementation requirements
|
||||
- Break down complex tasks using `expand_task` / `task-master expand --id=<id> --force --research` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) with appropriate flags like `--force` (to replace existing subtasks) and `--research`.
|
||||
- Clear existing subtasks if needed using `clear_subtasks` / `task-master clear-subtasks --id=<id>` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) before regenerating
|
||||
- Implement code following task details, dependencies, and project standards
|
||||
- Verify tasks according to test strategies before marking as complete (See [`tests.mdc`](mdc:.cursor/rules/tests.mdc))
|
||||
- Mark completed tasks with `set_task_status` / `task-master set-status --id=<id> --status=done` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc))
|
||||
- Update dependent tasks when implementation differs from original plan using `update` / `task-master update --from=<id> --prompt="..."` or `update_task` / `task-master update-task --id=<id> --prompt="..."` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc))
|
||||
- Add new tasks discovered during implementation using `add_task` / `task-master add-task --prompt="..." --research` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)).
|
||||
- Add new subtasks as needed using `add_subtask` / `task-master add-subtask --parent=<id> --title="..."` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)).
|
||||
- Append notes or details to subtasks using `update_subtask` / `task-master update-subtask --id=<subtaskId> --prompt='Add implementation notes here...\nMore details...'` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)).
|
||||
- Generate task files with `generate` / `task-master generate` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) after updating tasks.json
|
||||
- Maintain valid dependency structure with `add_dependency`/`remove_dependency` tools or `task-master add-dependency`/`remove-dependency` commands, `validate_dependencies` / `task-master validate-dependencies`, and `fix_dependencies` / `task-master fix-dependencies` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) when needed
|
||||
- Respect dependency chains and task priorities when selecting work
|
||||
- Report progress regularly using `get_tasks` / `task-master list`
|
||||
- Reorganize tasks as needed using `move_task` / `task-master move --from=<id> --to=<id>` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) to change task hierarchy or ordering
|
||||
|
||||
## Task Complexity Analysis
|
||||
|
||||
@@ -295,17 +161,6 @@ Taskmaster configuration is managed through two main mechanisms:
|
||||
**If AI commands FAIL in MCP** verify that the API key for the selected provider is present in the `env` section of `.cursor/mcp.json`.
|
||||
**If AI commands FAIL in CLI** verify that the API key for the selected provider is present in the `.env` file in the root of the project.
|
||||
|
||||
## Rules Management
|
||||
|
||||
Taskmaster supports multiple AI coding assistant rule sets that can be configured during project initialization or managed afterward:
|
||||
|
||||
- **Available Profiles**: Claude Code, Cline, Codex, Cursor, Roo Code, Trae, Windsurf (claude, cline, codex, cursor, roo, trae, windsurf)
|
||||
- **During Initialization**: Use `task-master init --rules cursor,windsurf` to specify which rule sets to include
|
||||
- **After Initialization**: Use `task-master rules add <profiles>` or `task-master rules remove <profiles>` to manage rule sets
|
||||
- **Interactive Setup**: Use `task-master rules setup` to launch an interactive prompt for selecting rule profiles
|
||||
- **Default Behavior**: If no `--rules` flag is specified during initialization, all available rule profiles are included
|
||||
- **Rule Structure**: Each profile creates its own directory (e.g., `.cursor/rules`, `.roo/rules`) with appropriate configuration files
|
||||
|
||||
## Determining the Next Task
|
||||
|
||||
- Run `next_task` / `task-master next` to show the next task to work on.
|
||||
|
||||
@@ -77,7 +77,6 @@ This document provides a detailed reference for interacting with Taskmaster, cov
|
||||
* `--set-fallback <model_id>`: `Set the fallback model.`
|
||||
* `--ollama`: `Specify that the provided model ID is for Ollama (use with --set-*).`
|
||||
* `--openrouter`: `Specify that the provided model ID is for OpenRouter (use with --set-*). Validates against OpenRouter API.`
|
||||
* `--bedrock`: `Specify that the provided model ID is for AWS Bedrock (use with --set-*).`
|
||||
* `--setup`: `Run interactive setup to configure models, including custom Ollama/OpenRouter IDs.`
|
||||
* **Usage (MCP):** Call without set flags to get current config. Use `setMain`, `setResearch`, or `setFallback` with a valid model ID to update the configuration. Use `listAvailableModels: true` to get a list of unassigned models. To set a custom model, provide the model ID and set `ollama: true` or `openrouter: true`.
|
||||
* **Usage (CLI):** Run without flags to view current configuration and available models. Use set flags to update specific roles. Use `--setup` for guided configuration, including custom models. To set a custom model via flags, use `--set-<role>=<model_id>` along with either `--ollama` or `--openrouter`.
|
||||
@@ -109,7 +108,6 @@ This document provides a detailed reference for interacting with Taskmaster, cov
|
||||
* **Description:** `Ask Taskmaster to show the next available task you can work on, based on status and completed dependencies.`
|
||||
* **Key Parameters/Options:**
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* `tag`: `Specify which tag context to use. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* **Usage:** Identify what to work on next according to the plan.
|
||||
|
||||
### 5. Get Task Details (`get_task`)
|
||||
@@ -138,7 +136,6 @@ This document provides a detailed reference for interacting with Taskmaster, cov
|
||||
* `dependencies`: `Specify the IDs of any Taskmaster tasks that must be completed before this new one can start, e.g., '12,14'.` (CLI: `-d, --dependencies <ids>`)
|
||||
* `priority`: `Set the priority for the new task: 'high', 'medium', or 'low'. Default is 'medium'.` (CLI: `--priority <priority>`)
|
||||
* `research`: `Enable Taskmaster to use the research role for potentially more informed task creation.` (CLI: `-r, --research`)
|
||||
* `tag`: `Specify which tag context to add the task to. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Quickly add newly identified tasks during development.
|
||||
* **Important:** This MCP tool makes AI calls and can take up to a minute to complete. Please inform users to hang tight while the operation is in progress.
|
||||
@@ -157,7 +154,6 @@ This document provides a detailed reference for interacting with Taskmaster, cov
|
||||
* `dependencies`: `Specify IDs of other tasks or subtasks, e.g., '15' or '16.1', that must be done before this new subtask.` (CLI: `--dependencies <ids>`)
|
||||
* `status`: `Set the initial status for the new subtask. Default is 'pending'.` (CLI: `-s, --status <status>`)
|
||||
* `skipGenerate`: `Prevent Taskmaster from automatically regenerating markdown task files after adding the subtask.` (CLI: `--skip-generate`)
|
||||
* `tag`: `Specify which tag context to operate on. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Break down tasks manually or reorganize existing tasks.
|
||||
|
||||
@@ -170,7 +166,6 @@ This document provides a detailed reference for interacting with Taskmaster, cov
|
||||
* `from`: `Required. The ID of the first task Taskmaster should update. All tasks with this ID or higher that are not 'done' will be considered.` (CLI: `--from <id>`)
|
||||
* `prompt`: `Required. Explain the change or new context for Taskmaster to apply to the tasks, e.g., "We are now using React Query instead of Redux Toolkit for data fetching".` (CLI: `-p, --prompt <text>`)
|
||||
* `research`: `Enable Taskmaster to use the research role for more informed updates. Requires appropriate API key.` (CLI: `-r, --research`)
|
||||
* `tag`: `Specify which tag context to operate on. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Handle significant implementation changes or pivots that affect multiple future tasks. Example CLI: `task-master update --from='18' --prompt='Switching to React Query.\nNeed to refactor data fetching...'`
|
||||
* **Important:** This MCP tool makes AI calls and can take up to a minute to complete. Please inform users to hang tight while the operation is in progress.
|
||||
@@ -196,13 +191,12 @@ This document provides a detailed reference for interacting with Taskmaster, cov
|
||||
* **CLI Command:** `task-master update-subtask [options]`
|
||||
* **Description:** `Append timestamped notes or details to a specific Taskmaster subtask without overwriting existing content. Intended for iterative implementation logging.`
|
||||
* **Key Parameters/Options:**
|
||||
* `id`: `Required. The ID of the Taskmaster subtask, e.g., '5.2', to update with new information.` (CLI: `-i, --id <id>`)
|
||||
* `prompt`: `Required. The information, findings, or progress notes to append to the subtask's details with a timestamp.` (CLI: `-p, --prompt <text>`)
|
||||
* `research`: `Enable Taskmaster to use the research role for more informed updates. Requires appropriate API key.` (CLI: `-r, --research`)
|
||||
* `tag`: `Specify which tag context the subtask belongs to. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `id`: `Required. The ID of the Taskmaster task, e.g., '5', or subtask, e.g., '5.2', to permanently remove.` (CLI: `-i, --id <id>`)
|
||||
* `yes`: `Skip the confirmation prompt and immediately delete the task.` (CLI: `-y, --yes`)
|
||||
* `tag`: `Specify which tag context to remove the task from. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Log implementation progress, findings, and discoveries during subtask development. Each update is timestamped and appended to preserve the implementation journey.
|
||||
* **Important:** This MCP tool makes AI calls and can take up to a minute to complete. Please inform users to hang tight while the operation is in progress.
|
||||
* **Usage:** Permanently delete tasks or subtasks that are no longer needed in the project.
|
||||
* **Notes:** Use with caution as this operation cannot be undone. Consider using 'blocked', 'cancelled', or 'deferred' status instead if you just want to exclude a task from active planning but keep it for reference. The command automatically cleans up dependency references in other tasks.
|
||||
|
||||
### 11. Set Task Status (`set_task_status`)
|
||||
|
||||
@@ -212,7 +206,6 @@ This document provides a detailed reference for interacting with Taskmaster, cov
|
||||
* **Key Parameters/Options:**
|
||||
* `id`: `Required. The ID(s) of the Taskmaster task(s) or subtask(s), e.g., '15', '15.2', or '16,17.1', to update.` (CLI: `-i, --id <id>`)
|
||||
* `status`: `Required. The new status to set, e.g., 'done', 'pending', 'in-progress', 'review', 'cancelled'.` (CLI: `-s, --status <status>`)
|
||||
* `tag`: `Specify which tag context to operate on. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Mark progress as tasks move through the development cycle.
|
||||
|
||||
@@ -224,7 +217,6 @@ This document provides a detailed reference for interacting with Taskmaster, cov
|
||||
* **Key Parameters/Options:**
|
||||
* `id`: `Required. The ID of the Taskmaster task, e.g., '5', or subtask, e.g., '5.2', to permanently remove.` (CLI: `-i, --id <id>`)
|
||||
* `yes`: `Skip the confirmation prompt and immediately delete the task.` (CLI: `-y, --yes`)
|
||||
* `tag`: `Specify which tag context to operate on. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Permanently delete tasks or subtasks that are no longer needed in the project.
|
||||
* **Notes:** Use with caution as this operation cannot be undone. Consider using 'blocked', 'cancelled', or 'deferred' status instead if you just want to exclude a task from active planning but keep it for reference. The command automatically cleans up dependency references in other tasks.
|
||||
@@ -272,7 +264,6 @@ This document provides a detailed reference for interacting with Taskmaster, cov
|
||||
* **Key Parameters/Options:**
|
||||
* `id`: `The ID(s) of the Taskmaster parent task(s) whose subtasks you want to remove, e.g., '15' or '16,18'. Required unless using `all`.) (CLI: `-i, --id <ids>`)
|
||||
* `all`: `Tell Taskmaster to remove subtasks from all parent tasks.` (CLI: `--all`)
|
||||
* `tag`: `Specify which tag context to operate on. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Used before regenerating subtasks with `expand_task` if the previous breakdown needs replacement.
|
||||
|
||||
@@ -285,7 +276,6 @@ This document provides a detailed reference for interacting with Taskmaster, cov
|
||||
* `id`: `Required. The ID(s) of the Taskmaster subtask(s) to remove, e.g., '15.2' or '16.1,16.3'.` (CLI: `-i, --id <id>`)
|
||||
* `convert`: `If used, Taskmaster will turn the subtask into a regular top-level task instead of deleting it.` (CLI: `-c, --convert`)
|
||||
* `skipGenerate`: `Prevent Taskmaster from automatically regenerating markdown task files after removing the subtask.` (CLI: `--skip-generate`)
|
||||
* `tag`: `Specify which tag context to operate on. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Delete unnecessary subtasks or promote a subtask to a top-level task.
|
||||
|
||||
@@ -297,7 +287,6 @@ This document provides a detailed reference for interacting with Taskmaster, cov
|
||||
* **Key Parameters/Options:**
|
||||
* `from`: `Required. ID of the task/subtask to move (e.g., "5" or "5.2"). Can be comma-separated for multiple tasks.` (CLI: `--from <id>`)
|
||||
* `to`: `Required. ID of the destination (e.g., "7" or "7.3"). Must match the number of source IDs if comma-separated.` (CLI: `--to <id>`)
|
||||
* `tag`: `Specify which tag context to operate on. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Reorganize tasks by moving them within the hierarchy. Supports various scenarios like:
|
||||
* Moving a task to become a subtask
|
||||
@@ -327,7 +316,6 @@ This document provides a detailed reference for interacting with Taskmaster, cov
|
||||
* **Key Parameters/Options:**
|
||||
* `id`: `Required. The ID of the Taskmaster task that will depend on another.` (CLI: `-i, --id <id>`)
|
||||
* `dependsOn`: `Required. The ID of the Taskmaster task that must be completed first, the prerequisite.` (CLI: `-d, --depends-on <id>`)
|
||||
* `tag`: `Specify which tag context to operate on. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <path>`)
|
||||
* **Usage:** Establish the correct order of execution between tasks.
|
||||
|
||||
@@ -339,7 +327,6 @@ This document provides a detailed reference for interacting with Taskmaster, cov
|
||||
* **Key Parameters/Options:**
|
||||
* `id`: `Required. The ID of the Taskmaster task you want to remove a prerequisite from.` (CLI: `-i, --id <id>`)
|
||||
* `dependsOn`: `Required. The ID of the Taskmaster task that should no longer be a prerequisite.` (CLI: `-d, --depends-on <id>`)
|
||||
* `tag`: `Specify which tag context to operate on. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Update task relationships when the order of execution changes.
|
||||
|
||||
@@ -349,7 +336,6 @@ This document provides a detailed reference for interacting with Taskmaster, cov
|
||||
* **CLI Command:** `task-master validate-dependencies [options]`
|
||||
* **Description:** `Check your Taskmaster tasks for dependency issues (like circular references or links to non-existent tasks) without making changes.`
|
||||
* **Key Parameters/Options:**
|
||||
* `tag`: `Specify which tag context to validate. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Audit the integrity of your task dependencies.
|
||||
|
||||
@@ -387,7 +373,6 @@ This document provides a detailed reference for interacting with Taskmaster, cov
|
||||
* **CLI Command:** `task-master complexity-report [options]`
|
||||
* **Description:** `Display the task complexity analysis report in a readable format.`
|
||||
* **Key Parameters/Options:**
|
||||
* `tag`: `Specify which tag context to show the report for. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to the complexity report (default: '.taskmaster/reports/task-complexity-report.json').` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Review and understand the complexity analysis results after running analyze-complexity.
|
||||
|
||||
@@ -459,7 +444,6 @@ This new suite of commands allows you to manage different task contexts (tags).
|
||||
* **CLI Command:** `task-master tags [options]`
|
||||
* **Description:** `List all available tags with task counts, completion status, and other metadata.`
|
||||
* **Key Parameters/Options:**
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* `--show-metadata`: `Include detailed metadata in the output (e.g., creation date, description).` (CLI: `--show-metadata`)
|
||||
|
||||
### 27. Add Tag (`add_tag`)
|
||||
@@ -473,7 +457,6 @@ This new suite of commands allows you to manage different task contexts (tags).
|
||||
* `--copy-from-current`: `Copy tasks from the currently active tag to the new tag.` (CLI: `--copy-from-current`)
|
||||
* `--copy-from <tag>`: `Copy tasks from a specific source tag to the new tag.` (CLI: `--copy-from <tag>`)
|
||||
* `--description <text>`: `Provide an optional description for the new tag.` (CLI: `-d, --description <text>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
|
||||
### 28. Delete Tag (`delete_tag`)
|
||||
|
||||
@@ -483,7 +466,6 @@ This new suite of commands allows you to manage different task contexts (tags).
|
||||
* **Key Parameters/Options:**
|
||||
* `tagName`: `Name of the tag to delete.` (CLI: `<tagName>` positional)
|
||||
* `--yes`: `Skip the confirmation prompt.` (CLI: `-y, --yes`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
|
||||
### 29. Use Tag (`use_tag`)
|
||||
|
||||
@@ -492,7 +474,6 @@ This new suite of commands allows you to manage different task contexts (tags).
|
||||
* **Description:** `Switch your active task context to a different tag.`
|
||||
* **Key Parameters/Options:**
|
||||
* `tagName`: `Name of the tag to switch to.` (CLI: `<tagName>` positional)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
|
||||
### 30. Rename Tag (`rename_tag`)
|
||||
|
||||
@@ -502,7 +483,6 @@ This new suite of commands allows you to manage different task contexts (tags).
|
||||
* **Key Parameters/Options:**
|
||||
* `oldName`: `The current name of the tag.` (CLI: `<oldName>` positional)
|
||||
* `newName`: `The new name for the tag.` (CLI: `<newName>` positional)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
|
||||
### 31. Copy Tag (`copy_tag`)
|
||||
|
||||
|
||||
10
.gitignore
vendored
10
.gitignore
vendored
@@ -77,13 +77,3 @@ dev-debug.log
|
||||
|
||||
# NPMRC
|
||||
.npmrc
|
||||
|
||||
# Added by Task Master AI
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"models": {
|
||||
"main": {
|
||||
"provider": "vertex",
|
||||
"modelId": "gemini-1.5-pro-002",
|
||||
"provider": "anthropic",
|
||||
"modelId": "claude-sonnet-4-20250514",
|
||||
"maxTokens": 50000,
|
||||
"temperature": 0.2
|
||||
},
|
||||
"research": {
|
||||
"provider": "perplexity",
|
||||
"modelId": "sonar",
|
||||
"modelId": "sonar-pro",
|
||||
"maxTokens": 8700,
|
||||
"temperature": 0.1
|
||||
},
|
||||
@@ -20,6 +20,7 @@
|
||||
}
|
||||
},
|
||||
"global": {
|
||||
"userId": "1234567890",
|
||||
"logLevel": "info",
|
||||
"debug": false,
|
||||
"defaultSubtasks": 5,
|
||||
@@ -27,7 +28,6 @@
|
||||
"projectName": "Taskmaster",
|
||||
"ollamaBaseURL": "http://localhost:11434/api",
|
||||
"bedrockBaseURL": "https://bedrock.us-east-1.amazonaws.com",
|
||||
"userId": "1234567890",
|
||||
"azureBaseURL": "https://your-endpoint.azure.com/",
|
||||
"defaultTag": "master"
|
||||
}
|
||||
|
||||
42
.taskmaster/docs/research/2025-06-13_what-is-typescript.md
Normal file
42
.taskmaster/docs/research/2025-06-13_what-is-typescript.md
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
title: Research Session
|
||||
query: "What is TypeScript?"
|
||||
date: 6/13/2025
|
||||
time: 3:07:41 PM
|
||||
timestamp: 2025-06-13T19:07:41.313Z
|
||||
exchanges: 2
|
||||
---
|
||||
|
||||
# Research Session
|
||||
|
||||
**Query:** What is TypeScript?
|
||||
**Date:** 6/13/2025 3:07:41 PM
|
||||
**Exchanges:** 2
|
||||
|
||||
---
|
||||
|
||||
## Initial Query
|
||||
|
||||
**Question:** What is TypeScript?
|
||||
|
||||
**Response:**
|
||||
|
||||
TypeScript is a strongly typed, object-oriented programming language that builds on JavaScript by adding static typing and type annotations. It is a syntactic superset of JavaScript, meaning all JavaScript code is valid TypeScript, but TypeScript adds extra syntax to specify types, enabling compile-time type checking and better tooling support. This helps catch errors early before running the code and improves code quality, especially in large-scale applications. TypeScript code is transpiled to plain JavaScript, which can run anywhere JavaScript runs, such as browsers, Node.js, or Deno. It is developed and maintained by Microsoft as open-source software under the Apache 2.0 license[1][2][3][4][5].
|
||||
|
||||
---
|
||||
|
||||
## Follow-up 1
|
||||
|
||||
**Question:** And why is the sky blue?
|
||||
|
||||
**Response:**
|
||||
|
||||
- The sky appears blue because sunlight interacts with Earth's atmosphere, and the shorter wavelengths of light (blue and violet) are scattered in all directions by air molecules—a process called Rayleigh scattering[1][2][3][5].
|
||||
- Blue light is scattered more than red light because it has a shorter wavelength, making the sky look blue to our eyes during the day[2][3][5].
|
||||
- If Earth's atmosphere didn't scatter sunlight, the sky would appear dark, as it does on the Moon, which lacks an atmosphere[5].
|
||||
|
||||
|
||||
---
|
||||
|
||||
*Generated by Task Master Research Command*
|
||||
*Timestamp: 2025-06-13T19:07:41.313Z*
|
||||
@@ -1,128 +0,0 @@
|
||||
---
|
||||
title: Research Session
|
||||
query: "how can i improve the scope-up and scope-down command"
|
||||
date: 6/14/2025
|
||||
time: 4:35:03 PM
|
||||
timestamp: 2025-06-14T20:35:03.231Z
|
||||
exchanges: 2
|
||||
---
|
||||
|
||||
# Research Session
|
||||
|
||||
## Initial Query
|
||||
|
||||
**Question:** how can i improve the scope-up and scope-down command
|
||||
|
||||
**Response:**
|
||||
|
||||
## Improving the `scope-up` and `scope-down` Commands
|
||||
|
||||
**Clarify Command Purpose and User Experience**
|
||||
|
||||
- Ensure the commands have clear, intuitive behavior. For example, `scope-up` should consistently broaden the context or task view, while `scope-down` should reliably narrow it.
|
||||
- Provide immediate, user-friendly feedback after each command, indicating what changed (e.g., "Now viewing parent task and all subtasks" or "Focused on subtask: X").
|
||||
- Document the commands with examples in your `docs/command-reference.md` and `docs/examples.md` to help users understand their effects and use cases.
|
||||
|
||||
**Enhance Context Awareness**
|
||||
|
||||
- Make the commands context-sensitive: if the user is already at the top or bottom of the scope hierarchy, return a helpful message instead of failing silently.
|
||||
- Allow optional arguments to specify the exact level or target (e.g., `scope-up 2` to move up two levels, or `scope-down <subtask-id>` to jump directly to a subtask).
|
||||
|
||||
**Robust Error Handling and Validation**
|
||||
|
||||
- Implement checks to prevent invalid operations, such as attempting to scope up from the root or scope down when no subtasks exist.
|
||||
- Provide clear error messages and suggestions for next steps, improving user trust and reducing confusion[1][5].
|
||||
|
||||
**Integrate with Task and Tag Systems**
|
||||
|
||||
- Ensure `scope-up` and `scope-down` interact smoothly with your tagging and task hierarchy features. For example, scoping up from a subtask should show the parent task and its context, while scoping down should allow selection from available subtasks or related tags.
|
||||
- Consider supporting batch operations, such as scoping down into all subtasks and displaying them in a summarized view.
|
||||
|
||||
**Improve Performance and Synchronization**
|
||||
|
||||
- Optimize the underlying logic to minimize delays, especially in large task trees. Use asynchronous operations where possible to keep the UI responsive.
|
||||
- If the commands trigger data loading or context switching, ensure synchronization so that subsequent commands operate on the correct, updated context[2][4].
|
||||
|
||||
**Testing and Stakeholder Feedback**
|
||||
|
||||
- Add or expand unit and integration tests for these commands (see `tests/unit/commands.test.js` and `integration/cli/commands.test.js`) to cover edge cases and ensure reliability.
|
||||
- Gather feedback from users and stakeholders to identify pain points and desired enhancements, then iterate on the command design and implementation[1][5].
|
||||
|
||||
**Example Improvements Table**
|
||||
|
||||
| Area | Current Issue | Suggested Improvement |
|
||||
|---------------------|-----------------------------|----------------------------------------------|
|
||||
| User Feedback | Minimal or unclear output | Add explicit, contextual messages |
|
||||
| Error Handling | Silent failures | Informative errors and recovery suggestions |
|
||||
| Flexibility | Only single-level movement | Support multi-level and targeted navigation |
|
||||
| Integration | Limited context awareness | Sync with tags, parent/child relationships |
|
||||
| Performance | Slow on large hierarchies | Optimize data access and UI updates |
|
||||
|
||||
By focusing on these areas, you can make `scope-up` and `scope-down` more robust, user-friendly, and aligned with best practices in scope management and command design[1][5].
|
||||
|
||||
---
|
||||
|
||||
## Follow-up 1
|
||||
|
||||
**Question:** how might i do this relative to the tag system?
|
||||
|
||||
**Response:**
|
||||
|
||||
## Integrating Scope-Up and Scope-Down with the Tag System
|
||||
|
||||
**Conceptual Mapping**
|
||||
|
||||
- In your project, *scope-up* and *scope-down* commands currently operate on the task hierarchy, broadening or narrowing the context by moving between parent and child tasks.
|
||||
- The tag system provides a parallel, orthogonal way to group and filter tasks—tags can represent categories, priorities, or any custom grouping, independent of strict parent/child relationships.
|
||||
|
||||
**How to Make Scope Commands Tag-Aware**
|
||||
|
||||
- **Scoping by Tag**: Allow users to "scope-down" into a tag, viewing only tasks associated with that tag. Conversely, "scope-up" could move from a specific tag context back to the broader task list or to a parent tag grouping if you support tag hierarchies.
|
||||
- **Tag-Scoped Views**: When a tag is active as the current scope, all navigation and operations (listing, editing, etc.) are limited to tasks with that tag. This is similar to how some systems let you set the "Tag Scope" to filter navigation and search to only tagged items[1][5].
|
||||
- **Combining Hierarchy and Tags**: Enable compound scoping, such as "all subtasks of Task X that also have Tag Y." This can be implemented by intersecting the results of hierarchical and tag-based queries.
|
||||
|
||||
**Implementation Strategies**
|
||||
|
||||
- **Scope Stack or Context Object**: Maintain a stack or context object that tracks both the current task scope and any active tag filters. Each scope change (up/down or tag in/out) updates this context.
|
||||
- **Commands**:
|
||||
- `scope-down --tag <tag>`: Narrows the view to tasks with the specified tag.
|
||||
- `scope-up --tag`: Removes the tag filter, broadening the view.
|
||||
- `scope-down <subtask-id> --tag <tag>`: Narrows to a specific subtask *and* applies a tag filter.
|
||||
- **Tag Hierarchies (Optional)**: If your tag system supports parent/child tags or tag groups, allow "scope-up" to move from a specific tag to its parent tag or group, and "scope-down" to move into a child tag[4][5].
|
||||
|
||||
**Example User Flows**
|
||||
|
||||
- *Scoping Down by Tag*:
|
||||
User runs `scope-down --tag urgent` → Only tasks tagged "urgent" are shown.
|
||||
- *Combining Task and Tag*:
|
||||
User runs `scope-down <task-id> --tag frontend` → Only subtasks of `<task-id>` that are tagged "frontend" are shown.
|
||||
- *Scoping Up from Tag*:
|
||||
User runs `scope-up --tag` → Removes the tag filter, returning to the previous broader context.
|
||||
|
||||
**Technical Considerations**
|
||||
|
||||
- **Efficient Filtering**: Use indexed queries or in-memory filtering to efficiently combine tag and hierarchy filters, especially for large task sets.
|
||||
- **Feedback and UI**: Always display the current scope context (e.g., "Viewing tasks tagged 'urgent' under 'Feature X'") so users understand what they're seeing.
|
||||
- **Extensibility**: Design the scope system so new types of scopes (e.g., by status, assignee) can be added in the future, not just tags and hierarchy.
|
||||
|
||||
**Relevant Precedents**
|
||||
|
||||
- Tag-based file systems and management tools often implement "scopes" as dynamic groups defined by tag criteria, allowing users to build up complex views by combining tags and other filters[5].
|
||||
- Some systems allow scoping into tag management itself, where you can create, edit, or view tags and their associated objects, which could be mirrored in your CLI or UI[4].
|
||||
|
||||
**Summary Table**
|
||||
|
||||
| Command Example | Resulting Scope |
|
||||
|----------------------------------|--------------------------------------------------|
|
||||
| `scope-down --tag bug` | All tasks tagged "bug" |
|
||||
| `scope-down <task-id> --tag api` | Subtasks of `<task-id>` tagged "api" |
|
||||
| `scope-up --tag` | Removes tag filter, returns to previous scope |
|
||||
| `scope-down --tag frontend` | All tasks tagged "frontend" |
|
||||
|
||||
By integrating tag-based scoping, you make your scope commands more flexible and powerful, supporting both hierarchical and ad-hoc organizational strategies[1][5].
|
||||
|
||||
|
||||
---
|
||||
|
||||
*Generated by Task Master Research Command*
|
||||
*Timestamp: 2025-06-14T20:35:03.231Z*
|
||||
@@ -1,158 +0,0 @@
|
||||
---
|
||||
title: Research Session
|
||||
query: "should i be using any specific libraries for this?"
|
||||
date: 6/14/2025
|
||||
time: 5:20:00 PM
|
||||
timestamp: 2025-06-14T21:20:00.561Z
|
||||
exchanges: 2
|
||||
---
|
||||
|
||||
# Research Session
|
||||
|
||||
## Initial Query
|
||||
|
||||
**Question:** should i be using any specific libraries for this?
|
||||
|
||||
**Response:**
|
||||
|
||||
HELL YEAH PARTNER
|
||||
|
||||
For implementing a GitHub Issue export feature with bidirectional linking in your project, you absolutely should use a dedicated library for interacting with the GitHub API. This will save you a ton of time, reduce boilerplate, and help you avoid common pitfalls with authentication, pagination, and error handling.
|
||||
|
||||
## Recommended Library: PyGithub
|
||||
|
||||
- **PyGithub** is the most widely used and well-documented Python library for accessing the GitHub REST API v3. It allows you to manage repositories, issues, user profiles, and more, all with a clean, object-oriented interface[1][2][3][5].
|
||||
- It supports all the operations you’ll need for your export feature, including creating issues, updating them, and managing references for bidirectional linking.
|
||||
|
||||
### Key Features
|
||||
|
||||
- **Authentication**: Easily authenticate with a personal access token.
|
||||
- **Issue Management**: Create, update, and link issues programmatically.
|
||||
- **Repository Access**: Fetch and manipulate repository data.
|
||||
- **User/Org Support**: Works for both user and organization repos.
|
||||
- **Pagination & Rate Limiting**: Handles these for you.
|
||||
|
||||
### Example Usage
|
||||
|
||||
```python
|
||||
from github import Github, Auth
|
||||
|
||||
# Authenticate with a personal access token
|
||||
auth = Auth.Token("your_github_token")
|
||||
g = Github(auth=auth)
|
||||
|
||||
# Get the repo
|
||||
repo = g.get_repo("owner/repo_name")
|
||||
|
||||
# Create a new issue
|
||||
issue = repo.create_issue(
|
||||
title="Exported from Task Master",
|
||||
body="This issue was created by Task Master. [Task ID: 123]"
|
||||
)
|
||||
|
||||
# Add a comment or update the issue to include a backlink
|
||||
issue.create_comment("Linked to Task Master task #123")
|
||||
```
|
||||
[3][5]
|
||||
|
||||
## Why Not Use Raw Requests?
|
||||
|
||||
- While you *can* use the `requests` library to call the GitHub API directly[4], PyGithub abstracts away authentication, error handling, and pagination, making your code much cleaner and less error-prone[2][5].
|
||||
|
||||
## Integration Tips
|
||||
|
||||
- Store your GitHub token securely (e.g., in environment variables).
|
||||
- Use PyGithub’s objects to fetch, create, and update issues.
|
||||
- For bidirectional linking, store the GitHub issue URL in your task data and the Task Master task ID in the GitHub issue body or as a label/comment.
|
||||
- PyGithub is actively maintained and widely used, so you’ll find plenty of support and documentation.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install PyGithub
|
||||
```
|
||||
[3]
|
||||
|
||||
## Summary Table
|
||||
|
||||
| Library | Language | API Coverage | Ease of Use | Maintained | Docs/Support |
|
||||
|-----------|----------|--------------|-------------|------------|--------------|
|
||||
| PyGithub | Python | Full | High | Yes | Excellent |
|
||||
| requests | Python | Full (manual)| Medium | Yes | Good |
|
||||
|
||||
**Use PyGithub for all GitHub API interactions in your export/import features.** It’s the standard for Python projects and will make your implementation faster, safer, and more maintainable[1][2][3][5].
|
||||
|
||||
---
|
||||
|
||||
## Follow-up 1
|
||||
|
||||
**Question:** we don't want to use py github because we have a node project
|
||||
|
||||
**Response:**
|
||||
|
||||
HELL YEAH PARTNER
|
||||
|
||||
For a Node.js project, you should use a Node-native library to interact with the GitHub API instead of PyGithub, which is Python-only. The industry standard for Node.js is **Octokit**, the official GitHub SDK for JavaScript/TypeScript[1][2].
|
||||
|
||||
## Why Use Octokit?
|
||||
|
||||
- **Official & Complete**: Octokit is maintained by GitHub and covers all REST and GraphQL API endpoints, including issues, repositories, users, and more[1][2].
|
||||
- **Universal**: Works in Node.js, browsers, and Deno environments[2].
|
||||
- **Typed & Tested**: Comes with TypeScript declarations and 100% test coverage[2].
|
||||
- **Authentication**: Supports personal access tokens, GitHub Apps, OAuth, and more.
|
||||
- **Pagination & Error Handling**: Handles pagination and common error scenarios out of the box.
|
||||
- **Extendable**: Supports plugins and custom hooks for advanced use cases[2].
|
||||
|
||||
## How to Use Octokit for GitHub Issue Export
|
||||
|
||||
Install Octokit in your Node.js project:
|
||||
|
||||
```bash
|
||||
npm install @octokit/rest
|
||||
```
|
||||
|
||||
Basic usage for creating and linking issues:
|
||||
|
||||
```js
|
||||
const { Octokit } = require("@octokit/rest");
|
||||
|
||||
// Authenticate with a personal access token
|
||||
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
|
||||
|
||||
// Create a new issue
|
||||
const { data: issue } = await octokit.issues.create({
|
||||
owner: "owner",
|
||||
repo: "repo_name",
|
||||
title: "Exported from Task Master",
|
||||
body: "This issue was created by Task Master. [Task ID: 123]"
|
||||
});
|
||||
|
||||
// Add a comment for bidirectional linking
|
||||
await octokit.issues.createComment({
|
||||
owner: "owner",
|
||||
repo: "repo_name",
|
||||
issue_number: issue.number,
|
||||
body: "Linked to Task Master task #123"
|
||||
});
|
||||
```
|
||||
|
||||
## Alternatives
|
||||
|
||||
- **node-github**: An older library, less maintained than Octokit, but still functional for basic API access[5].
|
||||
- **Raw HTTP Requests**: You can use `node-fetch` or `axios` to call the GitHub API directly, but you’ll need to handle authentication, pagination, and error handling yourself[3]. Octokit abstracts all of this for you.
|
||||
|
||||
## Summary Table
|
||||
|
||||
| Library | Language | API Coverage | Maintained | TypeScript | Docs/Support |
|
||||
|-----------------|----------|--------------|------------|------------|--------------|
|
||||
| Octokit | Node.js | Full | Yes | Yes | Excellent |
|
||||
| node-github | Node.js | Full | No | No | Limited |
|
||||
| Raw HTTP | Node.js | Full (manual)| N/A | N/A | N/A |
|
||||
|
||||
**Recommendation:** Use Octokit for all GitHub API interactions in your Node.js project. It’s the official, most robust, and best-supported option for your use case[1][2].
|
||||
|
||||
|
||||
---
|
||||
|
||||
*Generated by Task Master Research Command*
|
||||
*Timestamp: 2025-06-14T21:20:00.561Z*
|
||||
@@ -1,76 +0,0 @@
|
||||
---
|
||||
title: Research Session
|
||||
query: "test save functionality"
|
||||
date: 6/14/2025
|
||||
time: 5:50:23 PM
|
||||
timestamp: 2025-06-14T21:50:23.797Z
|
||||
exchanges: 1
|
||||
---
|
||||
|
||||
# Research Session
|
||||
|
||||
## Initial Query
|
||||
|
||||
**Question:** test save functionality
|
||||
|
||||
**Response:**
|
||||
|
||||
## Testing Save Functionality in a Task Management System
|
||||
|
||||
**Core Steps for Testing Save Functionality**
|
||||
|
||||
- Create or update a task in the system using the CLI or UI.
|
||||
- Trigger the save operation (e.g., by issuing a command or clicking a save button).
|
||||
- Verify that the data is correctly written to the persistent storage (e.g., `tasks.json`).
|
||||
- Reload or re-query the data to ensure the saved changes persist and are accurately reflected.
|
||||
- Validate that all required fields and relationships (such as dependencies and subtasks) are preserved and correctly structured.
|
||||
|
||||
**Key Test Scenarios**
|
||||
|
||||
- **Basic Save:** Add a new task and confirm it appears in `tasks.json` with all fields populated.
|
||||
- **Update Save:** Modify an existing task (e.g., change status, description, or priority) and verify the changes are saved.
|
||||
- **Subtask and Dependency Preservation:** Update a parent task and ensure that completed subtasks and dependency links remain intact, especially after using commands like `updateTask`.
|
||||
- **Batch Operations:** Perform bulk updates or additions and confirm all affected tasks are saved correctly, with no data loss or corruption.
|
||||
- **Validation:** Attempt to save invalid data and ensure the system rejects it or provides meaningful error messages, confirming validation logic is enforced.
|
||||
|
||||
**Recommended Practices**
|
||||
|
||||
- Use automated tests to simulate save operations for various scenarios, including edge cases (e.g., circular dependencies, missing required fields).
|
||||
- After each save, reload the data and compare it to the expected state to ensure integrity.
|
||||
- Log all save operations and errors for traceability and debugging.
|
||||
- Regularly back up the `tasks.json` file to prevent data loss during testing and development.
|
||||
|
||||
**Relevant Features from Industry Standards**
|
||||
|
||||
- Task management systems typically provide robust save and update mechanisms, ensuring that all task attributes (including dependencies, subtasks, and comments) are consistently maintained[1][2][5].
|
||||
- Validation and error handling are critical to prevent corrupt or incomplete saves, especially when handling complex relationships or batch operations[1][5].
|
||||
- User feedback (such as success messages or error alerts) is important for confirming save actions and guiding users in case of issues[1][2].
|
||||
|
||||
**Example Test Case**
|
||||
|
||||
1. Create a task with subtasks and dependencies.
|
||||
2. Mark some subtasks as complete.
|
||||
3. Use the `updateTask` command to modify the parent task.
|
||||
4. Save and reload the data.
|
||||
5. Assert that:
|
||||
- The parent task reflects the updates.
|
||||
- Completed subtasks remain marked as complete.
|
||||
- Dependencies are unchanged and valid.
|
||||
|
||||
**Summary Table: Save Functionality Test Aspects**
|
||||
|
||||
| Aspect | What to Test | Expected Outcome |
|
||||
|-----------------------|---------------------------------------------------|-----------------------------------------|
|
||||
| Basic Save | Add/update task, save, reload | Data persists and matches expectations |
|
||||
| Subtask Preservation | Update parent, check subtask status | Completed subtasks remain complete |
|
||||
| Dependency Handling | Save with dependencies, reload | Dependencies are intact and valid |
|
||||
| Batch Operations | Bulk save/update, reload | All changes persist, no data loss |
|
||||
| Validation | Save invalid data | Error is raised, data not corrupted |
|
||||
|
||||
These steps and scenarios ensure that the save functionality is robust, reliable, and meets the requirements of a modern task management system[1][2][5].
|
||||
|
||||
|
||||
---
|
||||
|
||||
*Generated by Task Master Research Command*
|
||||
*Timestamp: 2025-06-14T21:50:23.797Z*
|
||||
@@ -1,81 +0,0 @@
|
||||
---
|
||||
title: Research Session
|
||||
query: "test the fix for duplicate saves - final test"
|
||||
date: 6/14/2025
|
||||
time: 6:10:00 PM
|
||||
timestamp: 2025-06-14T22:10:00.394Z
|
||||
exchanges: 1
|
||||
---
|
||||
|
||||
# Research Session
|
||||
|
||||
## Initial Query
|
||||
|
||||
**Question:** test the fix for duplicate saves - final test
|
||||
|
||||
**Response:**
|
||||
|
||||
## Steps to Test the Fix for Duplicate Saves
|
||||
|
||||
**1. Prepare a Clean Test Environment**
|
||||
- Ensure the tasks.json file and any related data stores are in a known, clean state with no pre-existing duplicate entries.
|
||||
- Back up the current tasks.json in case rollback is needed.
|
||||
|
||||
**2. Define Test Scenarios**
|
||||
- Attempt to save a new task with unique data.
|
||||
- Attempt to save a task with the same ID or unique identifier as an existing task (simulate a duplicate).
|
||||
- Attempt to save a task with the same title or content but a different ID (to test if the fix is based on ID or content).
|
||||
- Simultaneously trigger multiple save operations (if the system supports concurrency) to test race conditions.
|
||||
|
||||
**3. Execute Manual and Automated Tests**
|
||||
- Use the application's UI or API to create and save tasks as per the scenarios above.
|
||||
- For each save operation, check the tasks.json file or the data store to verify:
|
||||
- No duplicate entries are created.
|
||||
- Existing tasks are not overwritten unless intended (e.g., for updates).
|
||||
- The system returns appropriate error messages or warnings when a duplicate save is attempted.
|
||||
|
||||
**4. Validate System Behavior**
|
||||
- Confirm that after all test operations, each task in tasks.json is unique according to the defined criteria (ID, title, or other unique fields).
|
||||
- If the system is designed to merge duplicates, verify that merging occurs as expected.
|
||||
- If the system is designed to reject duplicates, ensure that the rejection is clear and no duplicate is present.
|
||||
|
||||
**5. Test Edge Cases**
|
||||
- Attempt to save tasks with minor variations (e.g., whitespace differences, case sensitivity) to ensure the duplicate detection logic is robust.
|
||||
- Test with large numbers of tasks to ensure performance and correctness at scale.
|
||||
|
||||
**6. Review Logs and Error Handling**
|
||||
- Check application logs for any errors or warnings related to duplicate saves.
|
||||
- Ensure that error handling is user-friendly and provides actionable feedback.
|
||||
|
||||
**7. Regression Testing**
|
||||
- Run the full suite of task operations (create, update, delete) to ensure the fix for duplicate saves does not introduce new issues elsewhere in the system.
|
||||
|
||||
## Additional Best Practices
|
||||
|
||||
- Communicate test results with the team to confirm the fix is effective and to prevent future regressions[1][2][5].
|
||||
- Consider implementing automated duplicate detection as part of the save operation to prevent future issues[2][3].
|
||||
- Document the test cases and results for future reference and audits.
|
||||
|
||||
## Example Test Case Table
|
||||
|
||||
| Test Scenario | Expected Result | Actual Result | Pass/Fail |
|
||||
|--------------------------------------|-------------------------------|---------------|-----------|
|
||||
| Save unique task | Task saved | | |
|
||||
| Save duplicate task (same ID) | Duplicate rejected/merged | | |
|
||||
| Save duplicate task (same title) | Duplicate rejected/merged | | |
|
||||
| Simultaneous saves (race condition) | Only one unique task present | | |
|
||||
| Save with minor variations | No false positives/negatives | | |
|
||||
|
||||
Fill in the "Actual Result" and "Pass/Fail" columns during testing.
|
||||
|
||||
## Action Items
|
||||
|
||||
- Complete all test scenarios above.
|
||||
- Document any issues found and retest after fixes.
|
||||
- Confirm with stakeholders before closing the issue.
|
||||
|
||||
|
||||
---
|
||||
|
||||
*Generated by Task Master Research Command*
|
||||
*Timestamp: 2025-06-14T22:10:00.394Z*
|
||||
@@ -1,471 +0,0 @@
|
||||
# Task Template Importing System - Product Requirements Document
|
||||
|
||||
<context>
|
||||
# Overview
|
||||
The Task Template Importing system enables seamless integration of external task templates into the Task Master CLI through automatic file discovery. This system allows users to drop task template files into the tasks directory and immediately access them as new tag contexts without manual import commands or configuration. The solution addresses the need for multi-project task management, team collaboration through shared templates, and clean separation between permanent tasks and temporary project contexts.
|
||||
|
||||
# Core Features
|
||||
## Silent Task Template Discovery
|
||||
- **What it does**: Automatically scans for `tasks_*.json` files in the tasks directory during tag operations
|
||||
- **Why it's important**: Eliminates friction in adding new task contexts and enables zero-configuration workflow
|
||||
- **How it works**: File pattern matching extracts tag names from filenames and validates against internal tag keys
|
||||
|
||||
## External Tag Resolution System
|
||||
- **What it does**: Provides fallback mechanism to external files when tags are not found in main tasks.json
|
||||
- **Why it's important**: Maintains clean separation between core tasks and project-specific templates
|
||||
- **How it works**: Tag resolution logic checks external files as secondary source while preserving main file precedence
|
||||
|
||||
## Read-Only External Tag Access
|
||||
- **What it does**: Allows viewing and switching to external tags while preventing modifications
|
||||
- **Why it's important**: Protects template integrity and prevents accidental changes to shared templates
|
||||
- **How it works**: All task modifications route to main tasks.json regardless of current tag context
|
||||
|
||||
## Tag Precedence Management
|
||||
- **What it does**: Ensures main tasks.json tags override external files with same tag names
|
||||
- **Why it's important**: Prevents conflicts and maintains data integrity
|
||||
- **How it works**: Priority system where main file tags take precedence over external file tags
|
||||
|
||||
# User Experience
|
||||
## User Personas
|
||||
- **Solo Developer**: Manages multiple projects with different task contexts
|
||||
- **Team Lead**: Shares standardized task templates across team members
|
||||
- **Project Manager**: Organizes tasks by project phases or feature branches
|
||||
|
||||
## Key User Flows
|
||||
### Template Addition Flow
|
||||
1. User receives or creates a `tasks_projectname.json` file
|
||||
2. User drops file into `.taskmaster/tasks/` directory
|
||||
3. Tag becomes immediately available via `task-master use-tag projectname`
|
||||
4. User can list, view, and switch to external tag without configuration
|
||||
|
||||
### Template Usage Flow
|
||||
1. User runs `task-master tags` to see available tags including external ones
|
||||
2. External tags display with `(imported)` indicator
|
||||
3. User switches to external tag with `task-master use-tag projectname`
|
||||
4. User can view tasks but modifications are routed to main tasks.json
|
||||
|
||||
## UI/UX Considerations
|
||||
- External tags clearly marked with `(imported)` suffix in listings
|
||||
- Visual indicators distinguish between main and external tags
|
||||
- Error messages guide users when external files are malformed
|
||||
- Read-only warnings when attempting to modify external tag contexts
|
||||
</context>
|
||||
|
||||
<PRD>
|
||||
# Technical Architecture
|
||||
## System Components
|
||||
1. **External File Discovery Engine**
|
||||
- File pattern scanner for `tasks_*.json` files
|
||||
- Tag name extraction from filenames using regex
|
||||
- Dynamic tag registry combining main and external sources
|
||||
- Error handling for malformed external files
|
||||
|
||||
2. **Enhanced Tag Resolution System**
|
||||
- Fallback mechanism to external files when tags not found in main tasks.json
|
||||
- Precedence management ensuring main file tags override external files
|
||||
- Read-only access enforcement for external tags
|
||||
- Tag metadata preservation during discovery operations
|
||||
|
||||
3. **Silent Discovery Integration**
|
||||
- Automatic scanning during tag-related operations
|
||||
- Seamless integration with existing tag management functions
|
||||
- Zero-configuration workflow requiring no manual import commands
|
||||
- Dynamic tag availability without restart requirements
|
||||
|
||||
## Data Models
|
||||
|
||||
### External Task File Structure
|
||||
```json
|
||||
{
|
||||
"meta": {
|
||||
"projectName": "External Project Name",
|
||||
"version": "1.0.0",
|
||||
"templateSource": "external",
|
||||
"createdAt": "ISO-8601 timestamp"
|
||||
},
|
||||
"tags": {
|
||||
"projectname": {
|
||||
"meta": {
|
||||
"name": "Project Name",
|
||||
"description": "Project description",
|
||||
"createdAt": "ISO-8601 timestamp"
|
||||
},
|
||||
"tasks": [
|
||||
// Array of task objects
|
||||
]
|
||||
},
|
||||
"master": {
|
||||
// This section is ignored to prevent conflicts
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Enhanced Tag Registry Model
|
||||
```json
|
||||
{
|
||||
"mainTags": [
|
||||
{
|
||||
"name": "master",
|
||||
"source": "main",
|
||||
"taskCount": 150,
|
||||
"isActive": true
|
||||
}
|
||||
],
|
||||
"externalTags": [
|
||||
{
|
||||
"name": "projectname",
|
||||
"source": "external",
|
||||
"filename": "tasks_projectname.json",
|
||||
"taskCount": 25,
|
||||
"isReadOnly": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## APIs and Integrations
|
||||
1. **File System Discovery API**
|
||||
- Directory scanning with pattern matching
|
||||
- JSON file validation and parsing
|
||||
- Error handling for corrupted or malformed files
|
||||
- File modification time tracking for cache invalidation
|
||||
|
||||
2. **Enhanced Tag Management API**
|
||||
- `scanForExternalTaskFiles(projectRoot)` - Discover external template files
|
||||
- `getExternalTagsFromFiles(projectRoot)` - Extract tag names from external files
|
||||
- `readExternalTagData(projectRoot, tagName)` - Read specific external tag data
|
||||
- `getAvailableTags(projectRoot)` - Combined main and external tag listing
|
||||
|
||||
3. **Tag Resolution Enhancement**
|
||||
- Modified `readJSON()` with external file fallback
|
||||
- Enhanced `tags()` function with external tag display
|
||||
- Updated `useTag()` function supporting external tag switching
|
||||
- Read-only enforcement for external tag operations
|
||||
|
||||
## Infrastructure Requirements
|
||||
1. **File System Access**
|
||||
- Read permissions for tasks directory
|
||||
- JSON parsing capabilities
|
||||
- Pattern matching and regex support
|
||||
- Error handling for file system operations
|
||||
|
||||
2. **Backward Compatibility**
|
||||
- Existing tag operations continue unchanged
|
||||
- Main tasks.json structure preserved
|
||||
- No breaking changes to current workflows
|
||||
- Graceful degradation when external files unavailable
|
||||
|
||||
# Development Roadmap
|
||||
## Phase 1: Core External File Discovery (Foundation)
|
||||
1. **External File Scanner Implementation**
|
||||
- Create `scanForExternalTaskFiles()` function in utils.js
|
||||
- Implement file pattern matching for `tasks_*.json` files
|
||||
- Add error handling for file system access issues
|
||||
- Test with various filename patterns and edge cases
|
||||
|
||||
2. **Tag Name Extraction System**
|
||||
- Implement `getExternalTagsFromFiles()` function
|
||||
- Create regex pattern for extracting tag names from filenames
|
||||
- Add validation to ensure tag names match internal tag key format
|
||||
- Handle special characters and invalid filename patterns
|
||||
|
||||
3. **External Tag Data Reader**
|
||||
- Create `readExternalTagData()` function
|
||||
- Implement JSON parsing with error handling
|
||||
- Add validation for required tag structure
|
||||
- Ignore 'master' key in external files to prevent conflicts
|
||||
|
||||
## Phase 2: Tag Resolution Enhancement (Core Integration)
|
||||
1. **Enhanced Tag Registry**
|
||||
- Implement `getAvailableTags()` function combining main and external sources
|
||||
- Create tag metadata structure including source information
|
||||
- Add deduplication logic prioritizing main tags over external
|
||||
- Implement caching mechanism for performance optimization
|
||||
|
||||
2. **Modified readJSON Function**
|
||||
- Add external file fallback when tag not found in main tasks.json
|
||||
- Maintain precedence rule: main tasks.json overrides external files
|
||||
- Preserve existing error handling and validation patterns
|
||||
- Ensure read-only access for external tags
|
||||
|
||||
3. **Tag Listing Enhancement**
|
||||
- Update `tags()` function to display external tags with `(imported)` indicator
|
||||
- Show external tag metadata and task counts
|
||||
- Maintain current tag highlighting and sorting functionality
|
||||
- Add visual distinction between main and external tags
|
||||
|
||||
## Phase 3: User Interface Integration (User Experience)
|
||||
1. **Tag Switching Enhancement**
|
||||
- Update `useTag()` function to support external tag switching
|
||||
- Add read-only warnings when switching to external tags
|
||||
- Update state.json with external tag context information
|
||||
- Maintain current tag switching behavior for main tags
|
||||
|
||||
2. **Error Handling and User Feedback**
|
||||
- Implement comprehensive error messages for malformed external files
|
||||
- Add user guidance for proper external file structure
|
||||
- Create warnings for read-only operations on external tags
|
||||
- Ensure graceful degradation when external files are corrupted
|
||||
|
||||
3. **Documentation and Help Integration**
|
||||
- Update command help text to include external tag information
|
||||
- Add examples of external file structure and usage
|
||||
- Create troubleshooting guide for common external file issues
|
||||
- Document file naming conventions and best practices
|
||||
|
||||
## Phase 4: Advanced Features and Optimization (Enhancement)
|
||||
1. **Performance Optimization**
|
||||
- Implement file modification time caching
|
||||
- Add lazy loading for external tag data
|
||||
- Optimize file scanning for directories with many files
|
||||
- Create efficient tag resolution caching mechanism
|
||||
|
||||
2. **Advanced External File Features**
|
||||
- Support for nested external file directories
|
||||
- Batch external file validation and reporting
|
||||
- External file metadata display and management
|
||||
- Integration with version control ignore patterns
|
||||
|
||||
3. **Team Collaboration Features**
|
||||
- Shared external file validation
|
||||
- External file conflict detection and resolution
|
||||
- Team template sharing guidelines and documentation
|
||||
- Integration with git workflows for template management
|
||||
|
||||
# Logical Dependency Chain
|
||||
## Foundation Layer (Must Be Built First)
|
||||
1. **External File Scanner**
|
||||
- Core requirement for all other functionality
|
||||
- Provides the discovery mechanism for external template files
|
||||
- Must handle file system access and pattern matching reliably
|
||||
|
||||
2. **Tag Name Extraction**
|
||||
- Depends on file scanner functionality
|
||||
- Required for identifying available external tags
|
||||
- Must validate tag names against internal format requirements
|
||||
|
||||
3. **External Tag Data Reader**
|
||||
- Depends on tag name extraction
|
||||
- Provides access to external tag content
|
||||
- Must handle JSON parsing and validation safely
|
||||
|
||||
## Integration Layer (Builds on Foundation)
|
||||
4. **Enhanced Tag Registry**
|
||||
- Depends on all foundation components
|
||||
- Combines main and external tag sources
|
||||
- Required for unified tag management across the system
|
||||
|
||||
5. **Modified readJSON Function**
|
||||
- Depends on enhanced tag registry
|
||||
- Provides fallback mechanism for tag resolution
|
||||
- Critical for maintaining backward compatibility
|
||||
|
||||
6. **Tag Listing Enhancement**
|
||||
- Depends on enhanced tag registry
|
||||
- Provides user visibility into external tags
|
||||
- Required for user discovery of available templates
|
||||
|
||||
## User Experience Layer (Completes the Feature)
|
||||
7. **Tag Switching Enhancement**
|
||||
- Depends on modified readJSON and tag listing
|
||||
- Enables user interaction with external tags
|
||||
- Must enforce read-only access properly
|
||||
|
||||
8. **Error Handling and User Feedback**
|
||||
- Can be developed in parallel with other UX components
|
||||
- Enhances reliability and user experience
|
||||
- Should be integrated throughout development process
|
||||
|
||||
9. **Documentation and Help Integration**
|
||||
- Should be developed alongside implementation
|
||||
- Required for user adoption and proper usage
|
||||
- Can be completed in parallel with advanced features
|
||||
|
||||
## Optimization Layer (Performance and Advanced Features)
|
||||
10. **Performance Optimization**
|
||||
- Can be developed after core functionality is stable
|
||||
- Improves user experience with large numbers of external files
|
||||
- Not blocking for initial release
|
||||
|
||||
11. **Advanced External File Features**
|
||||
- Can be developed independently after core features
|
||||
- Enhances power user workflows
|
||||
- Optional for initial release
|
||||
|
||||
12. **Team Collaboration Features**
|
||||
- Depends on stable core functionality
|
||||
- Enhances team workflows and template sharing
|
||||
- Can be prioritized based on user feedback
|
||||
|
||||
# Risks and Mitigations
|
||||
## Technical Challenges
|
||||
|
||||
### File System Performance
|
||||
**Risk**: Scanning for external files on every tag operation could impact performance with large directories.
|
||||
**Mitigation**:
|
||||
- Implement file modification time caching to avoid unnecessary rescans
|
||||
- Use lazy loading for external tag data - only read when accessed
|
||||
- Add configurable limits on number of external files to scan
|
||||
- Optimize file pattern matching with efficient regex patterns
|
||||
|
||||
### External File Corruption
|
||||
**Risk**: Malformed or corrupted external JSON files could break tag operations.
|
||||
**Mitigation**:
|
||||
- Implement robust JSON parsing with comprehensive error handling
|
||||
- Add file validation before attempting to parse external files
|
||||
- Gracefully skip corrupted files and continue with valid ones
|
||||
- Provide clear error messages guiding users to fix malformed files
|
||||
|
||||
### Tag Name Conflicts
|
||||
**Risk**: External files might contain tag names that conflict with main tasks.json tags.
|
||||
**Mitigation**:
|
||||
- Implement strict precedence rule: main tasks.json always overrides external files
|
||||
- Add warnings when external tags are ignored due to conflicts
|
||||
- Document naming conventions to avoid common conflicts
|
||||
- Provide validation tools to check for potential conflicts
|
||||
|
||||
## MVP Definition
|
||||
|
||||
### Core Feature Scope
|
||||
**Risk**: Including too many advanced features could delay the core functionality.
|
||||
**Mitigation**:
|
||||
- Define MVP as basic external file discovery + tag switching
|
||||
- Focus on the silent discovery mechanism as the primary value proposition
|
||||
- Defer advanced features like nested directories and batch operations
|
||||
- Ensure each phase delivers complete, usable functionality
|
||||
|
||||
### User Experience Complexity
|
||||
**Risk**: The read-only nature of external tags might confuse users.
|
||||
**Mitigation**:
|
||||
- Provide clear visual indicators for external tags in all interfaces
|
||||
- Add explicit warnings when users attempt to modify external tag contexts
|
||||
- Document the read-only behavior and its rationale clearly
|
||||
- Consider future enhancement for external tag modification workflows
|
||||
|
||||
### Backward Compatibility
|
||||
**Risk**: Changes to tag resolution logic might break existing workflows.
|
||||
**Mitigation**:
|
||||
- Maintain existing tag operations unchanged for main tasks.json
|
||||
- Add external file support as enhancement, not replacement
|
||||
- Test thoroughly with existing task structures and workflows
|
||||
- Provide migration path if any breaking changes are necessary
|
||||
|
||||
## Resource Constraints
|
||||
|
||||
### Development Complexity
|
||||
**Risk**: Integration with existing tag management system could be complex.
|
||||
**Mitigation**:
|
||||
- Phase implementation to minimize risk of breaking existing functionality
|
||||
- Create comprehensive test suite covering both main and external tag scenarios
|
||||
- Use feature flags to enable/disable external file support during development
|
||||
- Implement thorough error handling to prevent system failures
|
||||
|
||||
### File System Dependencies
|
||||
**Risk**: Different operating systems might handle file operations differently.
|
||||
**Mitigation**:
|
||||
- Use Node.js built-in file system APIs for cross-platform compatibility
|
||||
- Test on multiple operating systems (Windows, macOS, Linux)
|
||||
- Handle file path separators and naming conventions properly
|
||||
- Add fallback mechanisms for file system access issues
|
||||
|
||||
### User Adoption
|
||||
**Risk**: Users might not understand or adopt the external file template system.
|
||||
**Mitigation**:
|
||||
- Create clear documentation with practical examples
|
||||
- Provide sample external template files for common use cases
|
||||
- Integrate help and guidance directly into the CLI interface
|
||||
- Gather user feedback early and iterate on the user experience
|
||||
|
||||
# Appendix
|
||||
## External File Naming Convention
|
||||
|
||||
### Filename Pattern
|
||||
- **Format**: `tasks_[tagname].json`
|
||||
- **Examples**: `tasks_feature-auth.json`, `tasks_v2-migration.json`, `tasks_project-alpha.json`
|
||||
- **Validation**: Tag name must match internal tag key format (alphanumeric, hyphens, underscores)
|
||||
|
||||
### File Structure Requirements
|
||||
```json
|
||||
{
|
||||
"meta": {
|
||||
"projectName": "Required: Human-readable project name",
|
||||
"version": "Optional: Template version",
|
||||
"templateSource": "Optional: Source identifier",
|
||||
"createdAt": "Optional: ISO-8601 timestamp"
|
||||
},
|
||||
"tags": {
|
||||
"[tagname]": {
|
||||
"meta": {
|
||||
"name": "Required: Tag display name",
|
||||
"description": "Optional: Tag description",
|
||||
"createdAt": "Optional: ISO-8601 timestamp"
|
||||
},
|
||||
"tasks": [
|
||||
// Required: Array of task objects following standard task structure
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation Functions Specification
|
||||
|
||||
### Core Discovery Functions
|
||||
```javascript
|
||||
// Scan tasks directory for external template files
|
||||
function scanForExternalTaskFiles(projectRoot) {
|
||||
// Returns: Array of external file paths
|
||||
}
|
||||
|
||||
// Extract tag names from external filenames
|
||||
function getExternalTagsFromFiles(projectRoot) {
|
||||
// Returns: Array of external tag names
|
||||
}
|
||||
|
||||
// Read specific external tag data
|
||||
function readExternalTagData(projectRoot, tagName) {
|
||||
// Returns: Tag data object or null if not found
|
||||
}
|
||||
|
||||
// Get combined main and external tags
|
||||
function getAvailableTags(projectRoot) {
|
||||
// Returns: Combined tag registry with metadata
|
||||
}
|
||||
```
|
||||
|
||||
### Integration Points
|
||||
```javascript
|
||||
// Enhanced readJSON with external fallback
|
||||
function readJSON(projectRoot, tag = null) {
|
||||
// Modified to check external files when tag not found in main
|
||||
}
|
||||
|
||||
// Enhanced tags listing with external indicators
|
||||
function tags(projectRoot, options = {}) {
|
||||
// Modified to display external tags with (imported) suffix
|
||||
}
|
||||
|
||||
// Enhanced tag switching with external support
|
||||
function useTag(projectRoot, tagName) {
|
||||
// Modified to support switching to external tags (read-only)
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling Specifications
|
||||
|
||||
### File System Errors
|
||||
- **ENOENT**: External file not found - gracefully skip and continue
|
||||
- **EACCES**: Permission denied - warn user and continue with available files
|
||||
- **EISDIR**: Directory instead of file - skip and continue scanning
|
||||
|
||||
### JSON Parsing Errors
|
||||
- **SyntaxError**: Malformed JSON - skip file and log warning with filename
|
||||
- **Missing required fields**: Skip file and provide specific error message
|
||||
- **Invalid tag structure**: Skip file and guide user to correct format
|
||||
|
||||
### Tag Conflict Resolution
|
||||
- **Duplicate tag names**: Main tasks.json takes precedence, log warning
|
||||
- **Invalid tag names**: Skip external file and provide naming guidance
|
||||
- **Master key in external**: Ignore master key, process other tags normally
|
||||
</PRD>
|
||||
@@ -1,9 +1,8 @@
|
||||
{
|
||||
"currentTag": "master",
|
||||
"lastSwitched": "2025-06-14T20:37:15.456Z",
|
||||
"lastSwitched": "2025-06-14T00:46:38.351Z",
|
||||
"branchTagMapping": {
|
||||
"v017-adds": "v017-adds",
|
||||
"next": "next"
|
||||
"v017-adds": "v017-adds"
|
||||
},
|
||||
"migrationNoticeShown": true
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
# 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.
|
||||
149
.taskmaster/tasks/task_095.txt
Normal file
149
.taskmaster/tasks/task_095.txt
Normal file
@@ -0,0 +1,149 @@
|
||||
# Task ID: 95
|
||||
# Title: Implement .taskmaster Directory Structure
|
||||
# Status: done
|
||||
# Dependencies: 1, 3, 4, 17
|
||||
# Priority: high
|
||||
# Description: Consolidate all Task Master-managed files in user projects into a clean, centralized .taskmaster/ directory structure to improve organization and keep user project directories clean, based on GitHub issue #275.
|
||||
# Details:
|
||||
This task involves restructuring how Task Master organizes files within user projects to improve maintainability and keep project directories clean:
|
||||
|
||||
1. Create a new `.taskmaster/` directory structure in user projects:
|
||||
- Move task files from `tasks/` to `.taskmaster/tasks/`
|
||||
- Move PRD files from `scripts/` to `.taskmaster/docs/`
|
||||
- Move analysis reports to `.taskmaster/reports/`
|
||||
- Move configuration from `.taskmasterconfig` to `.taskmaster/config.json`
|
||||
- Create `.taskmaster/templates/` for user templates
|
||||
|
||||
2. Update all Task Master code that creates/reads user files:
|
||||
- Modify task file generation to use `.taskmaster/tasks/`
|
||||
- Update PRD file handling to use `.taskmaster/docs/`
|
||||
- Adjust report generation to save to `.taskmaster/reports/`
|
||||
- Update configuration loading to look for `.taskmaster/config.json`
|
||||
- Modify any path resolution logic in Task Master's codebase
|
||||
|
||||
3. Ensure backward compatibility during migration:
|
||||
- Implement path fallback logic that checks both old and new locations
|
||||
- Add deprecation warnings when old paths are detected
|
||||
- Create a migration command to help users transition to the new structure
|
||||
- Preserve existing user data during migration
|
||||
|
||||
4. Update the project initialization process:
|
||||
- Modify the init command to create the new `.taskmaster/` directory structure
|
||||
- Update default file creation to use new paths
|
||||
|
||||
5. Benefits of the new structure:
|
||||
- Keeps user project directories clean and organized
|
||||
- Clearly separates Task Master files from user project files
|
||||
- Makes it easier to add Task Master to .gitignore if desired
|
||||
- Provides logical grouping of different file types
|
||||
|
||||
6. Test thoroughly to ensure all functionality works with the new structure:
|
||||
- Verify all Task Master commands work with the new paths
|
||||
- Ensure backward compatibility functions correctly
|
||||
- Test migration process preserves all user data
|
||||
|
||||
7. Update documentation:
|
||||
- Update README.md to reflect the new user file structure
|
||||
- Add migration guide for existing users
|
||||
- Document the benefits of the cleaner organization
|
||||
|
||||
# Test Strategy:
|
||||
1. Unit Testing:
|
||||
- Create unit tests for path resolution that verify both new and old paths work
|
||||
- Test configuration loading with both `.taskmasterconfig` and `.taskmaster/config.json`
|
||||
- Verify the migration command correctly moves files and preserves content
|
||||
- Test file creation in all new subdirectories
|
||||
|
||||
2. Integration Testing:
|
||||
- Run all existing integration tests with the new directory structure
|
||||
- Verify that all Task Master commands function correctly with new paths
|
||||
- Test backward compatibility by running commands with old file structure
|
||||
|
||||
3. Migration Testing:
|
||||
- Test the migration process on sample projects with existing tasks and files
|
||||
- Verify all tasks, PRDs, reports, and configurations are correctly moved
|
||||
- Ensure no data loss occurs during migration
|
||||
- Test migration with partial existing structures (e.g., only tasks/ exists)
|
||||
|
||||
4. User Workflow Testing:
|
||||
- Test complete workflows: init → create tasks → generate reports → update PRDs
|
||||
- Verify all generated files go to correct locations in `.taskmaster/`
|
||||
- Test that user project directories remain clean
|
||||
|
||||
5. Manual Testing:
|
||||
- Perform end-to-end testing with the new structure
|
||||
- Create, update, and delete tasks using the new structure
|
||||
- Generate reports and verify they're saved to `.taskmaster/reports/`
|
||||
|
||||
6. Documentation Verification:
|
||||
- Review all documentation to ensure it accurately reflects the new user file structure
|
||||
- Verify the migration guide provides clear instructions
|
||||
|
||||
7. Regression Testing:
|
||||
- Run the full test suite to ensure no regressions were introduced
|
||||
- Verify existing user projects continue to work during transition period
|
||||
|
||||
# Subtasks:
|
||||
## 1. Create .taskmaster directory structure [done]
|
||||
### Dependencies: None
|
||||
### Description: Create the new .taskmaster directory and move existing files to their new locations
|
||||
### Details:
|
||||
Create a new .taskmaster/ directory in the project root. Move the tasks/ directory to .taskmaster/tasks/. Move the scripts/ directory to .taskmaster/scripts/. Move the .taskmasterconfig file to .taskmaster/config.json. Ensure proper file permissions are maintained during the move.
|
||||
<info added on 2025-05-29T15:03:56.912Z>
|
||||
Create the new .taskmaster/ directory structure in user projects with subdirectories for tasks/, docs/, reports/, and templates/. Move the existing .taskmasterconfig file to .taskmaster/config.json. Since this project is also a Task Master user, move this project's current user files (tasks.json, PRD files, etc.) to the new .taskmaster/ structure to test the implementation. This subtask focuses on user project directory structure, not Task Master source code relocation.
|
||||
</info added on 2025-05-29T15:03:56.912Z>
|
||||
|
||||
## 2. Update Task Master code for new user file paths [done]
|
||||
### Dependencies: 95.1
|
||||
### Description: Modify all Task Master code that creates or reads user project files to use the new .taskmaster structure
|
||||
### Details:
|
||||
Update Task Master's file handling code to use the new paths: tasks in .taskmaster/tasks/, PRD files in .taskmaster/docs/, reports in .taskmaster/reports/, and config in .taskmaster/config.json. Modify path resolution logic throughout the Task Master codebase to reference the new user file locations.
|
||||
|
||||
## 3. Update task file generation system [done]
|
||||
### Dependencies: 95.1
|
||||
### Description: Modify the task file generation system to use the new directory structure
|
||||
### Details:
|
||||
Update the task file generation system to create and read task files from .taskmaster/tasks/ instead of tasks/. Ensure all template paths are updated. Modify any path resolution logic specific to task file handling.
|
||||
|
||||
## 4. Implement backward compatibility logic [done]
|
||||
### Dependencies: 95.2, 95.3
|
||||
### Description: Add fallback mechanisms to support both old and new file locations during transition
|
||||
### Details:
|
||||
Implement path fallback logic that checks both old and new locations when files aren't found. Add deprecation warnings when old paths are used, informing users about the new structure. Ensure error messages are clear about the transition.
|
||||
|
||||
## 5. Create migration command for users [done]
|
||||
### Dependencies: 95.1, 95.4
|
||||
### Description: Develop a Task Master command to help users transition their existing projects to the new structure
|
||||
### Details:
|
||||
Create a 'taskmaster migrate' command that automatically moves user files from old locations to the new .taskmaster structure. Move tasks/ to .taskmaster/tasks/, scripts/prd.txt to .taskmaster/docs/, reports to .taskmaster/reports/, and .taskmasterconfig to .taskmaster/config.json. Include backup functionality and validation to ensure migration completed successfully.
|
||||
|
||||
## 6. Update project initialization process [done]
|
||||
### Dependencies: 95.1
|
||||
### Description: Modify the init command to create the new directory structure for new projects
|
||||
### Details:
|
||||
Update the init command to create the .taskmaster directory and its subdirectories (tasks/, docs/, reports/, templates/). Modify default file creation to use the new paths. Ensure new projects are created with the correct structure from the start.
|
||||
|
||||
## 7. Update PRD and report file handling [done]
|
||||
### Dependencies: 95.2, 95.6
|
||||
### Description: Modify PRD file creation and report generation to use the new directory structure
|
||||
### Details:
|
||||
Update PRD file handling to create and read files from .taskmaster/docs/ instead of scripts/. Modify report generation (like task-complexity-report.json) to save to .taskmaster/reports/. Ensure all file operations use the new paths consistently.
|
||||
|
||||
## 8. Update documentation and create migration guide [done]
|
||||
### Dependencies: 95.5, 95.6, 95.7
|
||||
### Description: Update all documentation to reflect the new directory structure and provide migration guidance
|
||||
### Details:
|
||||
Update README.md and other documentation to reflect the new .taskmaster structure for user projects. Create a comprehensive migration guide explaining the benefits of the new structure and how to migrate existing projects. Include examples of the new directory layout and explain how it keeps user project directories clean.
|
||||
|
||||
## 9. Add templates directory support [done]
|
||||
### Dependencies: 95.2, 95.6
|
||||
### Description: Implement support for user templates in the .taskmaster/templates/ directory
|
||||
### Details:
|
||||
Create functionality to support user-defined templates in .taskmaster/templates/. Allow users to store custom task templates, PRD templates, or other reusable files. Update Task Master commands to recognize and use templates from this directory when available.
|
||||
|
||||
## 10. Verify clean user project directories [done]
|
||||
### Dependencies: 95.8, 95.9
|
||||
### Description: Ensure the new structure keeps user project root directories clean and organized
|
||||
### Details:
|
||||
Validate that after implementing the new structure, user project root directories only contain their actual project files plus the single .taskmaster/ directory. Verify that no Task Master files are created outside of .taskmaster/. Test that users can easily add .taskmaster/ to .gitignore if they choose to exclude Task Master files from version control.
|
||||
|
||||
37
.taskmaster/tasks/task_096.txt
Normal file
37
.taskmaster/tasks/task_096.txt
Normal file
@@ -0,0 +1,37 @@
|
||||
# Task ID: 96
|
||||
# Title: Create Export Command for On-Demand Task File and PDF Generation
|
||||
# Status: pending
|
||||
# Dependencies: 2, 4, 95
|
||||
# Priority: medium
|
||||
# Description: Develop an 'export' CLI command that generates task files and comprehensive PDF exports on-demand, replacing automatic file generation and providing users with flexible export options.
|
||||
# Details:
|
||||
Implement a new 'export' command in the CLI that supports two primary modes: (1) generating individual task files on-demand (superseding the current automatic generation system), and (2) producing a comprehensive PDF export. The PDF should include: a first page with the output of 'tm list --with-subtasks', followed by individual pages for each task (using 'tm show <task_id>') and each subtask (using 'tm show <subtask_id>'). Integrate PDF generation using a robust library (e.g., pdfkit, Puppeteer, or jsPDF) to ensure high-quality output and proper pagination. Refactor or disable any existing automatic file generation logic to avoid performance overhead. Ensure the command supports flexible output paths and options for exporting only files, only PDF, or both. Update documentation and help output to reflect the new export capabilities. Consider concurrency and error handling for large projects. Ensure the export process is efficient and does not block the main CLI thread unnecessarily.
|
||||
|
||||
# Test Strategy:
|
||||
1. Run the 'export' command with various options and verify that task files are generated only on-demand, not automatically. 2. Generate a PDF export and confirm that the first page contains the correct 'tm list --with-subtasks' output, and that each subsequent page accurately reflects the output of 'tm show <task_id>' and 'tm show <subtask_id>' for all tasks and subtasks. 3. Test exporting in projects with large numbers of tasks and subtasks to ensure performance and correctness. 4. Attempt exports with invalid paths or missing data to verify robust error handling. 5. Confirm that no automatic file generation occurs during normal task operations. 6. Review CLI help output and documentation for accuracy regarding the new export functionality.
|
||||
|
||||
# Subtasks:
|
||||
## 1. Remove Automatic Task File Generation from Task Operations [pending]
|
||||
### Dependencies: None
|
||||
### Description: Eliminate all calls to generateTaskFiles() from task operations such as add-task, remove-task, set-status, and similar commands to prevent unnecessary performance overhead.
|
||||
### Details:
|
||||
Audit the codebase for any automatic invocations of generateTaskFiles() and remove or refactor them to ensure task files are not generated automatically during task operations.
|
||||
|
||||
## 2. Implement Export Command Infrastructure with On-Demand Task File Generation [pending]
|
||||
### Dependencies: 96.1
|
||||
### Description: Develop the CLI 'export' command infrastructure, enabling users to generate task files on-demand by invoking the preserved generateTaskFiles function only when requested.
|
||||
### Details:
|
||||
Create the export command with options for output paths and modes (files, PDF, or both). Ensure generateTaskFiles is only called within this command and not elsewhere.
|
||||
|
||||
## 3. Implement Comprehensive PDF Export Functionality [pending]
|
||||
### Dependencies: 96.2
|
||||
### Description: Add PDF export capability to the export command, generating a structured PDF with a first page listing all tasks and subtasks, followed by individual pages for each task and subtask, using a robust PDF library.
|
||||
### Details:
|
||||
Integrate a PDF generation library (e.g., pdfkit, Puppeteer, or jsPDF). Ensure the PDF includes the output of 'tm list --with-subtasks' on the first page, and uses 'tm show <task_id>' and 'tm show <subtask_id>' for subsequent pages. Handle pagination, concurrency, and error handling for large projects.
|
||||
|
||||
## 4. Update Documentation, Tests, and CLI Help for Export Workflow [pending]
|
||||
### Dependencies: 96.2, 96.3
|
||||
### Description: Revise all relevant documentation, automated tests, and CLI help output to reflect the new export-based workflow and available options.
|
||||
### Details:
|
||||
Update user guides, README files, and CLI help text. Add or modify tests to cover the new export command and its options. Ensure all documentation accurately describes the new workflow and usage.
|
||||
|
||||
File diff suppressed because one or more lines are too long
78
CHANGELOG.md
78
CHANGELOG.md
@@ -1,10 +1,10 @@
|
||||
# task-master-ai
|
||||
|
||||
## 0.17.0
|
||||
## 0.17.0-rc.1
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#779](https://github.com/eyaltoledano/claude-task-master/pull/779) [`c0b3f43`](https://github.com/eyaltoledano/claude-task-master/commit/c0b3f432a60891550b00acb113dc877bd432995f) Thanks [@eyaltoledano](https://github.com/eyaltoledano)! - Add comprehensive AI-powered research command with intelligent context gathering and interactive follow-ups.
|
||||
- [#771](https://github.com/eyaltoledano/claude-task-master/pull/771) [`7250241`](https://github.com/eyaltoledano/claude-task-master/commit/72502416c6969055e0d139e408e726e03371c4f4) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Add comprehensive AI-powered research command with intelligent context gathering and interactive follow-ups.
|
||||
|
||||
The new `research` command provides AI-powered research capabilities that automatically gather relevant project context to answer your questions. The command intelligently selects context from multiple sources and supports interactive follow-up questions in CLI mode.
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
|
||||
The research command integrates with the existing AI service layer and supports all configured AI providers. Both CLI and MCP interfaces provide comprehensive research capabilities with intelligent context gathering and flexible output options.
|
||||
|
||||
- [#779](https://github.com/eyaltoledano/claude-task-master/pull/779) [`c0b3f43`](https://github.com/eyaltoledano/claude-task-master/commit/c0b3f432a60891550b00acb113dc877bd432995f) Thanks [@eyaltoledano](https://github.com/eyaltoledano)! - Enhance update-task with --append flag for timestamped task updates
|
||||
- [#771](https://github.com/eyaltoledano/claude-task-master/pull/771) [`7250241`](https://github.com/eyaltoledano/claude-task-master/commit/72502416c6969055e0d139e408e726e03371c4f4) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Enhance update-task with --append flag for timestamped task updates
|
||||
|
||||
Adds the `--append` flag to `update-task` command, enabling it to behave like `update-subtask` with timestamped information appending. This provides more flexible task updating options:
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
|
||||
Instead of requiring separate subtask creation for progress tracking, you can now append timestamped information directly to parent tasks while preserving the option for comprehensive task updates.
|
||||
|
||||
- [#779](https://github.com/eyaltoledano/claude-task-master/pull/779) [`c0b3f43`](https://github.com/eyaltoledano/claude-task-master/commit/c0b3f432a60891550b00acb113dc877bd432995f) Thanks [@eyaltoledano](https://github.com/eyaltoledano)! - Add --tag flag support to core commands for multi-context task management. Commands like parse-prd, analyze-complexity, and others now support targeting specific task lists, enabling rapid prototyping and parallel development workflows.
|
||||
- [#771](https://github.com/eyaltoledano/claude-task-master/pull/771) [`7250241`](https://github.com/eyaltoledano/claude-task-master/commit/72502416c6969055e0d139e408e726e03371c4f4) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Add --tag flag support to core commands for multi-context task management. Commands like parse-prd, analyze-complexity, and others now support targeting specific task lists, enabling rapid prototyping and parallel development workflows.
|
||||
|
||||
Key features:
|
||||
|
||||
@@ -104,21 +104,19 @@
|
||||
- All task operations can target specific contexts while preserving other lists
|
||||
- Non-existent tags are created automatically for seamless workflow
|
||||
|
||||
- [#779](https://github.com/eyaltoledano/claude-task-master/pull/779) [`c0b3f43`](https://github.com/eyaltoledano/claude-task-master/commit/c0b3f432a60891550b00acb113dc877bd432995f) Thanks [@eyaltoledano](https://github.com/eyaltoledano)! - Introduces Tagged Lists: AI Multi-Context Task Management System
|
||||
- [#771](https://github.com/eyaltoledano/claude-task-master/pull/771) [`7250241`](https://github.com/eyaltoledano/claude-task-master/commit/72502416c6969055e0d139e408e726e03371c4f4) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Introduces Tagged Lists: AI Multi-Context Task Management System
|
||||
|
||||
This major feature release introduces Tagged Lists, a comprehensive system that transforms Taskmaster into a multi-context task management powerhouse. You can now organize tasks into completely isolated contexts, enabling parallel (agentic) development workflows, team collaboration, and project experimentation without conflicts.
|
||||
This major release introduces Tagged Lists, a comprehensive system that transforms Task Master into a multi-context task management powerhouse. You can now organize tasks into completely isolated contexts, enabling parallel (agentic) development workflows, team collaboration, and project experimentation without conflicts.
|
||||
|
||||
**🏷️ Tagged Task Lists Architecture:**
|
||||
|
||||
The new tagged system fundamentally improves how tasks are organized:
|
||||
The new tagged system fundamentally changes how tasks are organized:
|
||||
|
||||
- **Legacy Format**: `{ "tasks": [...] }`
|
||||
- **New Tagged Format**: `{ "master": { "tasks": [...], "metadata": {...} }, "feature-xyz": { "tasks": [...], "metadata": {...} } }`
|
||||
- **Automatic Migration**: Existing projects will seamlessly migrate to tagged format with zero user intervention
|
||||
- **State Management**: New `.taskmaster/state.json` tracks current tag, last switched time, migration status and more.
|
||||
- **Configuration Integration**: Enhanced `.taskmaster/config.json` with tag-specific settings and defaults.
|
||||
|
||||
By default, your existing task list will be migrated to the `master` tag.
|
||||
- **Automatic Migration**: Existing projects seamlessly migrate to tagged format with zero user intervention
|
||||
- **State Management**: New `.taskmaster/state.json` tracks current tag, last switched time, and migration status
|
||||
- **Configuration Integration**: Enhanced `.taskmaster/config.json` with tag-specific settings and defaults
|
||||
|
||||
**🚀 Complete Tag Management Suite:**
|
||||
|
||||
@@ -126,7 +124,7 @@
|
||||
|
||||
- `task-master tags [--show-metadata]` - List all tags with task counts, completion stats, and metadata
|
||||
- `task-master add-tag <name> [options]` - Create new tag contexts with optional task copying
|
||||
- `task-master delete-tag <name> [--yes]` - Delete tags (and attached tasks) with double confirmation protection
|
||||
- `task-master delete-tag <name> [--yes]` - Delete tags with double confirmation protection
|
||||
- `task-master use-tag <name>` - Switch contexts and immediately see next available task
|
||||
- `task-master rename-tag <old> <new>` - Rename tags with automatic current tag reference updates
|
||||
- `task-master copy-tag <source> <target> [options]` - Duplicate tag contexts for experimentation
|
||||
@@ -146,7 +144,7 @@
|
||||
|
||||
- `--copy-from-current` - Copy tasks from currently active tag
|
||||
- `--copy-from=<tag>` - Copy tasks from specific tag
|
||||
- `--from-branch` - Creates a new tag using the active git branch name (for `add-tag` only)
|
||||
- `--from-branch` - Creates a new tag usin active git branch name (for `add-tag` only)
|
||||
- `--description="<text>"` - Add custom tag descriptions
|
||||
- Empty tag creation for fresh contexts
|
||||
|
||||
@@ -161,8 +159,6 @@
|
||||
- `task-master set-status --tag=hotfix --id=5 --status=done` - Update tasks in specific contexts
|
||||
- `task-master expand --tag=research --id=3` - Break down tasks within tag contexts
|
||||
|
||||
This way you or your agent can store out of context tasks into the appropriate tags for later, allowing you to maintain a groomed and scoped master list. Focus on value, not chores.
|
||||
|
||||
**📊 Enhanced Workflow Features:**
|
||||
|
||||
**Smart Context Switching:**
|
||||
@@ -259,7 +255,7 @@
|
||||
|
||||
This release establishes the foundation for advanced multi-context workflows while maintaining the simplicity and power that makes Task Master effective for individual developers.
|
||||
|
||||
- [#779](https://github.com/eyaltoledano/claude-task-master/pull/779) [`c0b3f43`](https://github.com/eyaltoledano/claude-task-master/commit/c0b3f432a60891550b00acb113dc877bd432995f) Thanks [@eyaltoledano](https://github.com/eyaltoledano)! - Research Save-to-File Feature & Critical MCP Tag Corruption Fix
|
||||
- [#771](https://github.com/eyaltoledano/claude-task-master/pull/771) [`7250241`](https://github.com/eyaltoledano/claude-task-master/commit/72502416c6969055e0d139e408e726e03371c4f4) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Research Save-to-File Feature & Critical MCP Tag Corruption Fix
|
||||
|
||||
**🔬 New Research Save-to-File Functionality:**
|
||||
|
||||
@@ -283,9 +279,9 @@
|
||||
- Comprehensive Markdown format with metadata headers including query, timestamp, and context sources
|
||||
- Clean conversation history formatting without duplicate information
|
||||
|
||||
- [#779](https://github.com/eyaltoledano/claude-task-master/pull/779) [`c0b3f43`](https://github.com/eyaltoledano/claude-task-master/commit/c0b3f432a60891550b00acb113dc877bd432995f) Thanks [@eyaltoledano](https://github.com/eyaltoledano)! - No longer automatically creates individual task files as they are not used by the applicatoin. You can still generate them anytime using the `generate` command.
|
||||
- [#771](https://github.com/eyaltoledano/claude-task-master/pull/771) [`7250241`](https://github.com/eyaltoledano/claude-task-master/commit/72502416c6969055e0d139e408e726e03371c4f4) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - No longer automatically creates individual task files as they are not used by the applicatoin. You can still generate them anytime using the `generate` command.
|
||||
|
||||
- [#779](https://github.com/eyaltoledano/claude-task-master/pull/779) [`c0b3f43`](https://github.com/eyaltoledano/claude-task-master/commit/c0b3f432a60891550b00acb113dc877bd432995f) Thanks [@eyaltoledano](https://github.com/eyaltoledano)! - Enhanced get-task/show command to support comma-separated task IDs for efficient batch operations
|
||||
- [#771](https://github.com/eyaltoledano/claude-task-master/pull/771) [`7250241`](https://github.com/eyaltoledano/claude-task-master/commit/72502416c6969055e0d139e408e726e03371c4f4) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Enhanced get-task/show command to support comma-separated task IDs for efficient batch operations
|
||||
|
||||
**New Features:**
|
||||
|
||||
@@ -303,17 +299,17 @@
|
||||
|
||||
This enhancement maintains full backward compatibility while significantly improving efficiency for both human users and AI agents working with multiple tasks.
|
||||
|
||||
- [#779](https://github.com/eyaltoledano/claude-task-master/pull/779) [`c0b3f43`](https://github.com/eyaltoledano/claude-task-master/commit/c0b3f432a60891550b00acb113dc877bd432995f) Thanks [@eyaltoledano](https://github.com/eyaltoledano)! - Adds support for filtering tasks by multiple statuses at once using comma-separated statuses.
|
||||
- [#771](https://github.com/eyaltoledano/claude-task-master/pull/771) [`7250241`](https://github.com/eyaltoledano/claude-task-master/commit/72502416c6969055e0d139e408e726e03371c4f4) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Adds support for filtering tasks by multiple statuses at once using comma-separated statuses.
|
||||
|
||||
Example: `cancelled,deferred`
|
||||
|
||||
- [#779](https://github.com/eyaltoledano/claude-task-master/pull/779) [`c0b3f43`](https://github.com/eyaltoledano/claude-task-master/commit/c0b3f432a60891550b00acb113dc877bd432995f) Thanks [@eyaltoledano](https://github.com/eyaltoledano)! - Adds tag to CLI and MCP outputs/responses so you know which tag you are performing operations on.
|
||||
- [#771](https://github.com/eyaltoledano/claude-task-master/pull/771) [`7250241`](https://github.com/eyaltoledano/claude-task-master/commit/72502416c6969055e0d139e408e726e03371c4f4) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Adds tag to CLI output so you know which tag you are performing operations on. Already supported in the MCP response.
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#779](https://github.com/eyaltoledano/claude-task-master/pull/779) [`5ec1f61`](https://github.com/eyaltoledano/claude-task-master/commit/5ec1f61c13f468648b7fdc8fa112e95aec25f76d) Thanks [@eyaltoledano](https://github.com/eyaltoledano)! - Fix Cursor deeplink installation by providing copy-paste instructions for GitHub compatibility
|
||||
- [#723](https://github.com/eyaltoledano/claude-task-master/pull/723) [`40a5238`](https://github.com/eyaltoledano/claude-task-master/commit/40a52385baf43a5ed97ce29bc5c4fb3fea766b70) Thanks [@joedanz](https://github.com/joedanz)! - Fix Cursor deeplink installation by providing copy-paste instructions for GitHub compatibility
|
||||
|
||||
- [#779](https://github.com/eyaltoledano/claude-task-master/pull/779) [`c0b3f43`](https://github.com/eyaltoledano/claude-task-master/commit/c0b3f432a60891550b00acb113dc877bd432995f) Thanks [@eyaltoledano](https://github.com/eyaltoledano)! - Fix critical bugs in task move functionality:
|
||||
- [#771](https://github.com/eyaltoledano/claude-task-master/pull/771) [`7250241`](https://github.com/eyaltoledano/claude-task-master/commit/72502416c6969055e0d139e408e726e03371c4f4) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Fix critical bugs in task move functionality:
|
||||
|
||||
- **Fixed moving tasks to become subtasks of empty parents**: When moving a task to become a subtask of a parent that had no existing subtasks (e.g., task 89 → task 98.1), the operation would fail with validation errors.
|
||||
- **Fixed moving subtasks between parents**: Subtasks can now be properly moved between different parent tasks, including to parents that previously had no subtasks.
|
||||
@@ -321,30 +317,12 @@
|
||||
|
||||
These fixes enables proper task hierarchy reorganization for corner cases that were previously broken.
|
||||
|
||||
- [#779](https://github.com/eyaltoledano/claude-task-master/pull/779) [`d76bea4`](https://github.com/eyaltoledano/claude-task-master/commit/d76bea49b381c523183f39e33c2a4269371576ed) Thanks [@eyaltoledano](https://github.com/eyaltoledano)! - Update o3 model price
|
||||
|
||||
- [#779](https://github.com/eyaltoledano/claude-task-master/pull/779) [`0849c0c`](https://github.com/eyaltoledano/claude-task-master/commit/0849c0c2cedb16ac44ba5cc2d109625a9b4efd67) Thanks [@eyaltoledano](https://github.com/eyaltoledano)! - Fixes issue with expand CLI command "Complexity report not found"
|
||||
|
||||
- Closes #735
|
||||
- Closes #728
|
||||
|
||||
- [#779](https://github.com/eyaltoledano/claude-task-master/pull/779) [`c0b3f43`](https://github.com/eyaltoledano/claude-task-master/commit/c0b3f432a60891550b00acb113dc877bd432995f) Thanks [@eyaltoledano](https://github.com/eyaltoledano)! - Fix issue with generate command which was creating tasks in the legacy tasks location.
|
||||
|
||||
- No longer creates individual task files automatically. You can still use `generate` if you need to create our update your task files.
|
||||
|
||||
- [#779](https://github.com/eyaltoledano/claude-task-master/pull/779) [`c0b3f43`](https://github.com/eyaltoledano/claude-task-master/commit/c0b3f432a60891550b00acb113dc877bd432995f) Thanks [@eyaltoledano](https://github.com/eyaltoledano)! - Improves dependency management when moving tasks by updating subtask dependencies that reference sibling subtasks by their old parent-based ID
|
||||
|
||||
- Updated dependencies [[`c0b3f43`](https://github.com/eyaltoledano/claude-task-master/commit/c0b3f432a60891550b00acb113dc877bd432995f), [`5ec1f61`](https://github.com/eyaltoledano/claude-task-master/commit/5ec1f61c13f468648b7fdc8fa112e95aec25f76d), [`c0b3f43`](https://github.com/eyaltoledano/claude-task-master/commit/c0b3f432a60891550b00acb113dc877bd432995f), [`c0b3f43`](https://github.com/eyaltoledano/claude-task-master/commit/c0b3f432a60891550b00acb113dc877bd432995f), [`d76bea4`](https://github.com/eyaltoledano/claude-task-master/commit/d76bea49b381c523183f39e33c2a4269371576ed), [`c0b3f43`](https://github.com/eyaltoledano/claude-task-master/commit/c0b3f432a60891550b00acb113dc877bd432995f), [`0849c0c`](https://github.com/eyaltoledano/claude-task-master/commit/0849c0c2cedb16ac44ba5cc2d109625a9b4efd67), [`c0b3f43`](https://github.com/eyaltoledano/claude-task-master/commit/c0b3f432a60891550b00acb113dc877bd432995f), [`c0b3f43`](https://github.com/eyaltoledano/claude-task-master/commit/c0b3f432a60891550b00acb113dc877bd432995f), [`c0b3f43`](https://github.com/eyaltoledano/claude-task-master/commit/c0b3f432a60891550b00acb113dc877bd432995f), [`c0b3f43`](https://github.com/eyaltoledano/claude-task-master/commit/c0b3f432a60891550b00acb113dc877bd432995f), [`c0b3f43`](https://github.com/eyaltoledano/claude-task-master/commit/c0b3f432a60891550b00acb113dc877bd432995f), [`c0b3f43`](https://github.com/eyaltoledano/claude-task-master/commit/c0b3f432a60891550b00acb113dc877bd432995f), [`c0b3f43`](https://github.com/eyaltoledano/claude-task-master/commit/c0b3f432a60891550b00acb113dc877bd432995f), [`c0b3f43`](https://github.com/eyaltoledano/claude-task-master/commit/c0b3f432a60891550b00acb113dc877bd432995f)]:
|
||||
- task-master-ai@0.17.0
|
||||
|
||||
## 0.16.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#695](https://github.com/eyaltoledano/claude-task-master/pull/695) [`1ece6f1`](https://github.com/eyaltoledano/claude-task-master/commit/1ece6f19048df6ae2a0b25cbfb84d2c0f430642c) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - improve findTasks algorithm for resolving tasks path
|
||||
|
||||
- [#695](https://github.com/eyaltoledano/claude-task-master/pull/695) [`ee0be04`](https://github.com/eyaltoledano/claude-task-master/commit/ee0be04302cc602246de5cd296291db69bc8b300) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Fix update tool on MCP giving `No valid tasks found`
|
||||
|
||||
- [#771](https://github.com/eyaltoledano/claude-task-master/pull/771) [`7250241`](https://github.com/eyaltoledano/claude-task-master/commit/72502416c6969055e0d139e408e726e03371c4f4) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Adds ability to automatically create/switch tags to match the current git branch. The configuration to enable the git workflow and then use the auto switching is in config.json."
|
||||
|
||||
- [#699](https://github.com/eyaltoledano/claude-task-master/pull/699) [`27edbd8`](https://github.com/eyaltoledano/claude-task-master/commit/27edbd8f3fe5e2ac200b80e7f27f4c0e74a074d6) Thanks [@eyaltoledano](https://github.com/eyaltoledano)! - Enhanced add-task fuzzy search intelligence and improved user experience
|
||||
|
||||
**Smarter Task Discovery:**
|
||||
@@ -381,11 +359,16 @@
|
||||
|
||||
The add-task system now provides truly relevant task context based on semantic understanding rather than arbitrary categories and limits, while maintaining a cleaner and more professional terminal experience.
|
||||
|
||||
- [#655](https://github.com/eyaltoledano/claude-task-master/pull/655) [`edaa5fe`](https://github.com/eyaltoledano/claude-task-master/commit/edaa5fe0d56e0e4e7c4370670a7a388eebd922ac) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Fix double .taskmaster directory paths in file resolution utilities
|
||||
- [#751](https://github.com/eyaltoledano/claude-task-master/pull/751) [`4901908`](https://github.com/eyaltoledano/claude-task-master/commit/4901908f5d1f5c39e82a23d650047074691deda4) Thanks [@zahorniak](https://github.com/zahorniak)! - Update o3 model price
|
||||
|
||||
- Closes #636
|
||||
- [#755](https://github.com/eyaltoledano/claude-task-master/pull/755) [`dc7a541`](https://github.com/eyaltoledano/claude-task-master/commit/dc7a5414c0364a87412192ac01ccc49cf7b68768) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Fixes issue with expand CLI command "Complexity report not found"
|
||||
|
||||
- [#671](https://github.com/eyaltoledano/claude-task-master/pull/671) [`86ea6d1`](https://github.com/eyaltoledano/claude-task-master/commit/86ea6d1dbc03eeb39f524f565b50b7017b1d2c9c) Thanks [@joedanz](https://github.com/joedanz)! - Add one-click MCP server installation for Cursor
|
||||
- Closes #735
|
||||
- Closes #728
|
||||
|
||||
- [#771](https://github.com/eyaltoledano/claude-task-master/pull/771) [`7250241`](https://github.com/eyaltoledano/claude-task-master/commit/72502416c6969055e0d139e408e726e03371c4f4) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Fix issue with generate command which was creating tasks in the legacy tasks location.
|
||||
|
||||
- [#771](https://github.com/eyaltoledano/claude-task-master/pull/771) [`7250241`](https://github.com/eyaltoledano/claude-task-master/commit/72502416c6969055e0d139e408e726e03371c4f4) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Improves dependency management when moving tasks by updating subtask dependencies that reference sibling subtasks by their old parent-based ID
|
||||
|
||||
- [#699](https://github.com/eyaltoledano/claude-task-master/pull/699) [`2e55757`](https://github.com/eyaltoledano/claude-task-master/commit/2e55757b2698ba20b78f09ec0286951297510b8e) Thanks [@eyaltoledano](https://github.com/eyaltoledano)! - Add sync-readme command for a task export to GitHub README
|
||||
|
||||
@@ -406,6 +389,9 @@
|
||||
|
||||
Perfect for showcasing project progress on GitHub. Experimental. Open to feedback.
|
||||
|
||||
- Updated dependencies [[`7250241`](https://github.com/eyaltoledano/claude-task-master/commit/72502416c6969055e0d139e408e726e03371c4f4), [`40a5238`](https://github.com/eyaltoledano/claude-task-master/commit/40a52385baf43a5ed97ce29bc5c4fb3fea766b70), [`7250241`](https://github.com/eyaltoledano/claude-task-master/commit/72502416c6969055e0d139e408e726e03371c4f4), [`1ece6f1`](https://github.com/eyaltoledano/claude-task-master/commit/1ece6f19048df6ae2a0b25cbfb84d2c0f430642c), [`ee0be04`](https://github.com/eyaltoledano/claude-task-master/commit/ee0be04302cc602246de5cd296291db69bc8b300), [`7250241`](https://github.com/eyaltoledano/claude-task-master/commit/72502416c6969055e0d139e408e726e03371c4f4), [`27edbd8`](https://github.com/eyaltoledano/claude-task-master/commit/27edbd8f3fe5e2ac200b80e7f27f4c0e74a074d6), [`7250241`](https://github.com/eyaltoledano/claude-task-master/commit/72502416c6969055e0d139e408e726e03371c4f4), [`4901908`](https://github.com/eyaltoledano/claude-task-master/commit/4901908f5d1f5c39e82a23d650047074691deda4), [`7250241`](https://github.com/eyaltoledano/claude-task-master/commit/72502416c6969055e0d139e408e726e03371c4f4), [`dc7a541`](https://github.com/eyaltoledano/claude-task-master/commit/dc7a5414c0364a87412192ac01ccc49cf7b68768), [`7250241`](https://github.com/eyaltoledano/claude-task-master/commit/72502416c6969055e0d139e408e726e03371c4f4), [`7250241`](https://github.com/eyaltoledano/claude-task-master/commit/72502416c6969055e0d139e408e726e03371c4f4), [`7250241`](https://github.com/eyaltoledano/claude-task-master/commit/72502416c6969055e0d139e408e726e03371c4f4), [`7250241`](https://github.com/eyaltoledano/claude-task-master/commit/72502416c6969055e0d139e408e726e03371c4f4), [`7250241`](https://github.com/eyaltoledano/claude-task-master/commit/72502416c6969055e0d139e408e726e03371c4f4), [`7250241`](https://github.com/eyaltoledano/claude-task-master/commit/72502416c6969055e0d139e408e726e03371c4f4), [`7250241`](https://github.com/eyaltoledano/claude-task-master/commit/72502416c6969055e0d139e408e726e03371c4f4), [`2e55757`](https://github.com/eyaltoledano/claude-task-master/commit/2e55757b2698ba20b78f09ec0286951297510b8e), [`7250241`](https://github.com/eyaltoledano/claude-task-master/commit/72502416c6969055e0d139e408e726e03371c4f4)]:
|
||||
- task-master-ai@0.17.0-rc.1
|
||||
|
||||
## 0.16.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -189,9 +189,6 @@ task-master init
|
||||
|
||||
# If installed locally
|
||||
npx task-master init
|
||||
|
||||
# Initialize project with specific rules
|
||||
task-master init --rules cursor,windsurf,vscode
|
||||
```
|
||||
|
||||
This will prompt you for project details and set up a new project with the necessary files and structure.
|
||||
@@ -219,9 +216,6 @@ task-master research "What are the latest best practices for JWT authentication?
|
||||
|
||||
# Generate task files
|
||||
task-master generate
|
||||
|
||||
# Add rules after initialization
|
||||
task-master rules add windsurf,roo,vscode
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"customModes": [
|
||||
{
|
||||
"slug": "orchestrator",
|
||||
"name": "Orchestrator",
|
||||
"slug": "boomerang",
|
||||
"name": "Boomerang",
|
||||
"roleDefinition": "You are Roo, a strategic workflow orchestrator who coordinates complex tasks by delegating them to appropriate specialized modes. You have a comprehensive understanding of each mode's capabilities and limitations, also your own, and with the information given by the user and other modes in shared context you are enabled to effectively break down complex problems into discrete tasks that can be solved by different specialists using the `taskmaster-ai` system for task and context management.",
|
||||
"customInstructions": "Your role is to coordinate complex workflows by delegating tasks to specialized modes, using `taskmaster-ai` as the central hub for task definition, progress tracking, and context management. \nAs an orchestrator, you should:\nn1. When given a complex task, use contextual information (which gets updated frequently) to break it down into logical subtasks that can be delegated to appropriate specialized modes.\nn2. For each subtask, use the `new_task` tool to delegate. Choose the most appropriate mode for the subtask's specific goal and provide comprehensive instructions in the `message` parameter. \nThese instructions must include:\n* All necessary context from the parent task or previous subtasks required to complete the work.\n* A clearly defined scope, specifying exactly what the subtask should accomplish.\n* An explicit statement that the subtask should *only* perform the work outlined in these instructions and not deviate.\n* An instruction for the subtask to signal completion by using the `attempt_completion` tool, providing a thorough summary of the outcome in the `result` parameter, keeping in mind that this summary will be the source of truth used to further relay this information to other tasks and for you to keep track of what was completed on this project.\nn3. Track and manage the progress of all subtasks. When a subtask is completed, acknowledge its results and determine the next steps.\nn4. Help the user understand how the different subtasks fit together in the overall workflow. Provide clear reasoning about why you're delegating specific tasks to specific modes.\nn5. Ask clarifying questions when necessary to better understand how to break down complex tasks effectively. If it seems complex delegate to architect to accomplish that \nn6. Use subtasks to maintain clarity. If a request significantly shifts focus or requires a different expertise (mode), consider creating a subtask rather than overloading the current one.",
|
||||
"groups": [
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
---
|
||||
description: Guidelines for creating and maintaining Cursor rules to ensure consistency and effectiveness.
|
||||
globs: .cursor/rules/*.mdc
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
- **Required Rule Structure:**
|
||||
```markdown
|
||||
---
|
||||
description: Clear, one-line description of what the rule enforces
|
||||
globs: path/to/files/*.ext, other/path/**/*
|
||||
alwaysApply: boolean
|
||||
---
|
||||
|
||||
- **Main Points in Bold**
|
||||
- Sub-points with details
|
||||
- Examples and explanations
|
||||
```
|
||||
|
||||
- **File References:**
|
||||
- Use `[filename](mdc:path/to/file)` ([filename](mdc:filename)) to reference files
|
||||
- Example: [prisma.mdc](mdc:.cursor/rules/prisma.mdc) for rule references
|
||||
- Example: [schema.prisma](mdc:prisma/schema.prisma) for code references
|
||||
|
||||
- **Code Examples:**
|
||||
- Use language-specific code blocks
|
||||
```typescript
|
||||
// ✅ DO: Show good examples
|
||||
const goodExample = true;
|
||||
|
||||
// ❌ DON'T: Show anti-patterns
|
||||
const badExample = false;
|
||||
```
|
||||
|
||||
- **Rule Content Guidelines:**
|
||||
- Start with high-level overview
|
||||
- Include specific, actionable requirements
|
||||
- Show examples of correct implementation
|
||||
- Reference existing code when possible
|
||||
- Keep rules DRY by referencing other rules
|
||||
|
||||
- **Rule Maintenance:**
|
||||
- Update rules when new patterns emerge
|
||||
- Add examples from actual codebase
|
||||
- Remove outdated patterns
|
||||
- Cross-reference related rules
|
||||
|
||||
- **Best Practices:**
|
||||
- Use bullet points for clarity
|
||||
- Keep descriptions concise
|
||||
- Include both DO and DON'T examples
|
||||
- Reference actual code over theoretical examples
|
||||
- Use consistent formatting across rules
|
||||
@@ -1,424 +0,0 @@
|
||||
---
|
||||
description: Guide for using Taskmaster to manage task-driven development workflows
|
||||
globs: **/*
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# Taskmaster Development Workflow
|
||||
|
||||
This guide outlines the standard process for using Taskmaster to manage software development projects. It is written as a set of instructions for you, the AI agent.
|
||||
|
||||
- **Your Default Stance**: For most projects, the user can work directly within the `master` task context. Your initial actions should operate on this default context unless a clear pattern for multi-context work emerges.
|
||||
- **Your Goal**: Your role is to elevate the user's workflow by intelligently introducing advanced features like **Tagged Task Lists** when you detect the appropriate context. Do not force tags on the user; suggest them as a helpful solution to a specific need.
|
||||
|
||||
## The Basic Loop
|
||||
The fundamental development cycle you will facilitate is:
|
||||
1. **`list`**: Show the user what needs to be done.
|
||||
2. **`next`**: Help the user decide what to work on.
|
||||
3. **`show <id>`**: Provide details for a specific task.
|
||||
4. **`expand <id>`**: Break down a complex task into smaller, manageable subtasks.
|
||||
5. **Implement**: The user writes the code and tests.
|
||||
6. **`update-subtask`**: Log progress and findings on behalf of the user.
|
||||
7. **`set-status`**: Mark tasks and subtasks as `done` as work is completed.
|
||||
8. **Repeat**.
|
||||
|
||||
All your standard command executions should operate on the user's current task context, which defaults to `master`.
|
||||
|
||||
---
|
||||
|
||||
## Standard Development Workflow Process
|
||||
|
||||
### Simple Workflow (Default Starting Point)
|
||||
|
||||
For new projects or when users are getting started, operate within the `master` tag context:
|
||||
|
||||
- Start new projects by running `initialize_project` tool / `task-master init` or `parse_prd` / `task-master parse-prd --input='<prd-file.txt>'` (see @`taskmaster.mdc`) to generate initial tasks.json with tagged structure
|
||||
- Configure rule sets during initialization with `--rules` flag (e.g., `task-master init --rules cursor,windsurf`) or manage them later with `task-master rules add/remove` commands
|
||||
- Begin coding sessions with `get_tasks` / `task-master list` (see @`taskmaster.mdc`) to see current tasks, status, and IDs
|
||||
- Determine the next task to work on using `next_task` / `task-master next` (see @`taskmaster.mdc`)
|
||||
- Analyze task complexity with `analyze_project_complexity` / `task-master analyze-complexity --research` (see @`taskmaster.mdc`) before breaking down tasks
|
||||
- Review complexity report using `complexity_report` / `task-master complexity-report` (see @`taskmaster.mdc`)
|
||||
- Select tasks based on dependencies (all marked 'done'), priority level, and ID order
|
||||
- View specific task details using `get_task` / `task-master show <id>` (see @`taskmaster.mdc`) to understand implementation requirements
|
||||
- Break down complex tasks using `expand_task` / `task-master expand --id=<id> --force --research` (see @`taskmaster.mdc`) with appropriate flags like `--force` (to replace existing subtasks) and `--research`
|
||||
- Implement code following task details, dependencies, and project standards
|
||||
- Mark completed tasks with `set_task_status` / `task-master set-status --id=<id> --status=done` (see @`taskmaster.mdc`)
|
||||
- Update dependent tasks when implementation differs from original plan using `update` / `task-master update --from=<id> --prompt="..."` or `update_task` / `task-master update-task --id=<id> --prompt="..."` (see @`taskmaster.mdc`)
|
||||
|
||||
---
|
||||
|
||||
## Leveling Up: Agent-Led Multi-Context Workflows
|
||||
|
||||
While the basic workflow is powerful, your primary opportunity to add value is by identifying when to introduce **Tagged Task Lists**. These patterns are your tools for creating a more organized and efficient development environment for the user, especially if you detect agentic or parallel development happening across the same session.
|
||||
|
||||
**Critical Principle**: Most users should never see a difference in their experience. Only introduce advanced workflows when you detect clear indicators that the project has evolved beyond simple task management.
|
||||
|
||||
### When to Introduce Tags: Your Decision Patterns
|
||||
|
||||
Here are the patterns to look for. When you detect one, you should propose the corresponding workflow to the user.
|
||||
|
||||
#### Pattern 1: Simple Git Feature Branching
|
||||
This is the most common and direct use case for tags.
|
||||
|
||||
- **Trigger**: The user creates a new git branch (e.g., `git checkout -b feature/user-auth`).
|
||||
- **Your Action**: Propose creating a new tag that mirrors the branch name to isolate the feature's tasks from `master`.
|
||||
- **Your Suggested Prompt**: *"I see you've created a new branch named 'feature/user-auth'. To keep all related tasks neatly organized and separate from your main list, I can create a corresponding task tag for you. This helps prevent merge conflicts in your `tasks.json` file later. Shall I create the 'feature-user-auth' tag?"*
|
||||
- **Tool to Use**: `task-master add-tag --from-branch`
|
||||
|
||||
#### Pattern 2: Team Collaboration
|
||||
- **Trigger**: The user mentions working with teammates (e.g., "My teammate Alice is handling the database schema," or "I need to review Bob's work on the API.").
|
||||
- **Your Action**: Suggest creating a separate tag for the user's work to prevent conflicts with shared master context.
|
||||
- **Your Suggested Prompt**: *"Since you're working with Alice, I can create a separate task context for your work to avoid conflicts. This way, Alice can continue working with the master list while you have your own isolated context. When you're ready to merge your work, we can coordinate the tasks back to master. Shall I create a tag for your current work?"*
|
||||
- **Tool to Use**: `task-master add-tag my-work --copy-from-current --description="My tasks while collaborating with Alice"`
|
||||
|
||||
#### Pattern 3: Experiments or Risky Refactors
|
||||
- **Trigger**: The user wants to try something that might not be kept (e.g., "I want to experiment with switching our state management library," or "Let's refactor the old API module, but I want to keep the current tasks as a reference.").
|
||||
- **Your Action**: Propose creating a sandboxed tag for the experimental work.
|
||||
- **Your Suggested Prompt**: *"This sounds like a great experiment. To keep these new tasks separate from our main plan, I can create a temporary 'experiment-zustand' tag for this work. If we decide not to proceed, we can simply delete the tag without affecting the main task list. Sound good?"*
|
||||
- **Tool to Use**: `task-master add-tag experiment-zustand --description="Exploring Zustand migration"`
|
||||
|
||||
#### Pattern 4: Large Feature Initiatives (PRD-Driven)
|
||||
This is a more structured approach for significant new features or epics.
|
||||
|
||||
- **Trigger**: The user describes a large, multi-step feature that would benefit from a formal plan.
|
||||
- **Your Action**: Propose a comprehensive, PRD-driven workflow.
|
||||
- **Your Suggested Prompt**: *"This sounds like a significant new feature. To manage this effectively, I suggest we create a dedicated task context for it. Here's the plan: I'll create a new tag called 'feature-xyz', then we can draft a Product Requirements Document (PRD) together to scope the work. Once the PRD is ready, I'll automatically generate all the necessary tasks within that new tag. How does that sound?"*
|
||||
- **Your Implementation Flow**:
|
||||
1. **Create an empty tag**: `task-master add-tag feature-xyz --description "Tasks for the new XYZ feature"`. You can also start by creating a git branch if applicable, and then create the tag from that branch.
|
||||
2. **Collaborate & Create PRD**: Work with the user to create a detailed PRD file (e.g., `.taskmaster/docs/feature-xyz-prd.txt`).
|
||||
3. **Parse PRD into the new tag**: `task-master parse-prd .taskmaster/docs/feature-xyz-prd.txt --tag feature-xyz`
|
||||
4. **Prepare the new task list**: Follow up by suggesting `analyze-complexity` and `expand-all` for the newly created tasks within the `feature-xyz` tag.
|
||||
|
||||
#### Pattern 5: Version-Based Development
|
||||
Tailor your approach based on the project maturity indicated by tag names.
|
||||
|
||||
- **Prototype/MVP Tags** (`prototype`, `mvp`, `poc`, `v0.x`):
|
||||
- **Your Approach**: Focus on speed and functionality over perfection
|
||||
- **Task Generation**: Create tasks that emphasize "get it working" over "get it perfect"
|
||||
- **Complexity Level**: Lower complexity, fewer subtasks, more direct implementation paths
|
||||
- **Research Prompts**: Include context like "This is a prototype - prioritize speed and basic functionality over optimization"
|
||||
- **Example Prompt Addition**: *"Since this is for the MVP, I'll focus on tasks that get core functionality working quickly rather than over-engineering."*
|
||||
|
||||
- **Production/Mature Tags** (`v1.0+`, `production`, `stable`):
|
||||
- **Your Approach**: Emphasize robustness, testing, and maintainability
|
||||
- **Task Generation**: Include comprehensive error handling, testing, documentation, and optimization
|
||||
- **Complexity Level**: Higher complexity, more detailed subtasks, thorough implementation paths
|
||||
- **Research Prompts**: Include context like "This is for production - prioritize reliability, performance, and maintainability"
|
||||
- **Example Prompt Addition**: *"Since this is for production, I'll ensure tasks include proper error handling, testing, and documentation."*
|
||||
|
||||
### Advanced Workflow (Tag-Based & PRD-Driven)
|
||||
|
||||
**When to Transition**: Recognize when the project has evolved (or has initiated a project which existing code) beyond simple task management. Look for these indicators:
|
||||
- User mentions teammates or collaboration needs
|
||||
- Project has grown to 15+ tasks with mixed priorities
|
||||
- User creates feature branches or mentions major initiatives
|
||||
- User initializes Taskmaster on an existing, complex codebase
|
||||
- User describes large features that would benefit from dedicated planning
|
||||
|
||||
**Your Role in Transition**: Guide the user to a more sophisticated workflow that leverages tags for organization and PRDs for comprehensive planning.
|
||||
|
||||
#### Master List Strategy (High-Value Focus)
|
||||
Once you transition to tag-based workflows, the `master` tag should ideally contain only:
|
||||
- **High-level deliverables** that provide significant business value
|
||||
- **Major milestones** and epic-level features
|
||||
- **Critical infrastructure** work that affects the entire project
|
||||
- **Release-blocking** items
|
||||
|
||||
**What NOT to put in master**:
|
||||
- Detailed implementation subtasks (these go in feature-specific tags' parent tasks)
|
||||
- Refactoring work (create dedicated tags like `refactor-auth`)
|
||||
- Experimental features (use `experiment-*` tags)
|
||||
- Team member-specific tasks (use person-specific tags)
|
||||
|
||||
#### PRD-Driven Feature Development
|
||||
|
||||
**For New Major Features**:
|
||||
1. **Identify the Initiative**: When user describes a significant feature
|
||||
2. **Create Dedicated Tag**: `add_tag feature-[name] --description="[Feature description]"`
|
||||
3. **Collaborative PRD Creation**: Work with user to create comprehensive PRD in `.taskmaster/docs/feature-[name]-prd.txt`
|
||||
4. **Parse & Prepare**:
|
||||
- `parse_prd .taskmaster/docs/feature-[name]-prd.txt --tag=feature-[name]`
|
||||
- `analyze_project_complexity --tag=feature-[name] --research`
|
||||
- `expand_all --tag=feature-[name] --research`
|
||||
5. **Add Master Reference**: Create a high-level task in `master` that references the feature tag
|
||||
|
||||
**For Existing Codebase Analysis**:
|
||||
When users initialize Taskmaster on existing projects:
|
||||
1. **Codebase Discovery**: Use your native tools for producing deep context about the code base. You may use `research` tool with `--tree` and `--files` to collect up to date information using the existing architecture as context.
|
||||
2. **Collaborative Assessment**: Work with user to identify improvement areas, technical debt, or new features
|
||||
3. **Strategic PRD Creation**: Co-author PRDs that include:
|
||||
- Current state analysis (based on your codebase research)
|
||||
- Proposed improvements or new features
|
||||
- Implementation strategy considering existing code
|
||||
4. **Tag-Based Organization**: Parse PRDs into appropriate tags (`refactor-api`, `feature-dashboard`, `tech-debt`, etc.)
|
||||
5. **Master List Curation**: Keep only the most valuable initiatives in master
|
||||
|
||||
The parse-prd's `--append` flag enables the user to parse multple PRDs within tags or across tags. PRDs should be focused and the number of tasks they are parsed into should be strategically chosen relative to the PRD's complexity and level of detail.
|
||||
|
||||
### Workflow Transition Examples
|
||||
|
||||
**Example 1: Simple → Team-Based**
|
||||
```
|
||||
User: "Alice is going to help with the API work"
|
||||
Your Response: "Great! To avoid conflicts, I'll create a separate task context for your work. Alice can continue with the master list while you work in your own context. When you're ready to merge, we can coordinate the tasks back together."
|
||||
Action: add_tag my-api-work --copy-from-current --description="My API tasks while collaborating with Alice"
|
||||
```
|
||||
|
||||
**Example 2: Simple → PRD-Driven**
|
||||
```
|
||||
User: "I want to add a complete user dashboard with analytics, user management, and reporting"
|
||||
Your Response: "This sounds like a major feature that would benefit from detailed planning. Let me create a dedicated context for this work and we can draft a PRD together to ensure we capture all requirements."
|
||||
Actions:
|
||||
1. add_tag feature-dashboard --description="User dashboard with analytics and management"
|
||||
2. Collaborate on PRD creation
|
||||
3. parse_prd dashboard-prd.txt --tag=feature-dashboard
|
||||
4. Add high-level "User Dashboard" task to master
|
||||
```
|
||||
|
||||
**Example 3: Existing Project → Strategic Planning**
|
||||
```
|
||||
User: "I just initialized Taskmaster on my existing React app. It's getting messy and I want to improve it."
|
||||
Your Response: "Let me research your codebase to understand the current architecture, then we can create a strategic plan for improvements."
|
||||
Actions:
|
||||
1. research "Current React app architecture and improvement opportunities" --tree --files=src/
|
||||
2. Collaborate on improvement PRD based on findings
|
||||
3. Create tags for different improvement areas (refactor-components, improve-state-management, etc.)
|
||||
4. Keep only major improvement initiatives in master
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Primary Interaction: MCP Server vs. CLI
|
||||
|
||||
Taskmaster offers two primary ways to interact:
|
||||
|
||||
1. **MCP Server (Recommended for Integrated Tools)**:
|
||||
- For AI agents and integrated development environments (like Cursor), interacting via the **MCP server is the preferred method**.
|
||||
- The MCP server exposes Taskmaster functionality through a set of tools (e.g., `get_tasks`, `add_subtask`).
|
||||
- This method offers better performance, structured data exchange, and richer error handling compared to CLI parsing.
|
||||
- Refer to @`mcp.mdc` for details on the MCP architecture and available tools.
|
||||
- A comprehensive list and description of MCP tools and their corresponding CLI commands can be found in @`taskmaster.mdc`.
|
||||
- **Restart the MCP server** if core logic in `scripts/modules` or MCP tool/direct function definitions change.
|
||||
- **Note**: MCP tools fully support tagged task lists with complete tag management capabilities.
|
||||
|
||||
2. **`task-master` CLI (For Users & Fallback)**:
|
||||
- The global `task-master` command provides a user-friendly interface for direct terminal interaction.
|
||||
- It can also serve as a fallback if the MCP server is inaccessible or a specific function isn't exposed via MCP.
|
||||
- Install globally with `npm install -g task-master-ai` or use locally via `npx task-master-ai ...`.
|
||||
- The CLI commands often mirror the MCP tools (e.g., `task-master list` corresponds to `get_tasks`).
|
||||
- Refer to @`taskmaster.mdc` for a detailed command reference.
|
||||
- **Tagged Task Lists**: CLI fully supports the new tagged system with seamless migration.
|
||||
|
||||
## How the Tag System Works (For Your Reference)
|
||||
|
||||
- **Data Structure**: Tasks are organized into separate contexts (tags) like "master", "feature-branch", or "v2.0".
|
||||
- **Silent Migration**: Existing projects automatically migrate to use a "master" tag with zero disruption.
|
||||
- **Context Isolation**: Tasks in different tags are completely separate. Changes in one tag do not affect any other tag.
|
||||
- **Manual Control**: The user is always in control. There is no automatic switching. You facilitate switching by using `use-tag <name>`.
|
||||
- **Full CLI & MCP Support**: All tag management commands are available through both the CLI and MCP tools for you to use. Refer to @`taskmaster.mdc` for a full command list.
|
||||
|
||||
---
|
||||
|
||||
## Task Complexity Analysis
|
||||
|
||||
- Run `analyze_project_complexity` / `task-master analyze-complexity --research` (see @`taskmaster.mdc`) for comprehensive analysis
|
||||
- Review complexity report via `complexity_report` / `task-master complexity-report` (see @`taskmaster.mdc`) for a formatted, readable version.
|
||||
- Focus on tasks with highest complexity scores (8-10) for detailed breakdown
|
||||
- Use analysis results to determine appropriate subtask allocation
|
||||
- Note that reports are automatically used by the `expand_task` tool/command
|
||||
|
||||
## Task Breakdown Process
|
||||
|
||||
- Use `expand_task` / `task-master expand --id=<id>`. It automatically uses the complexity report if found, otherwise generates default number of subtasks.
|
||||
- Use `--num=<number>` to specify an explicit number of subtasks, overriding defaults or complexity report recommendations.
|
||||
- Add `--research` flag to leverage Perplexity AI for research-backed expansion.
|
||||
- Add `--force` flag to clear existing subtasks before generating new ones (default is to append).
|
||||
- Use `--prompt="<context>"` to provide additional context when needed.
|
||||
- Review and adjust generated subtasks as necessary.
|
||||
- Use `expand_all` tool or `task-master expand --all` to expand multiple pending tasks at once, respecting flags like `--force` and `--research`.
|
||||
- If subtasks need complete replacement (regardless of the `--force` flag on `expand`), clear them first with `clear_subtasks` / `task-master clear-subtasks --id=<id>`.
|
||||
|
||||
## Implementation Drift Handling
|
||||
|
||||
- When implementation differs significantly from planned approach
|
||||
- When future tasks need modification due to current implementation choices
|
||||
- When new dependencies or requirements emerge
|
||||
- Use `update` / `task-master update --from=<futureTaskId> --prompt='<explanation>\nUpdate context...' --research` to update multiple future tasks.
|
||||
- Use `update_task` / `task-master update-task --id=<taskId> --prompt='<explanation>\nUpdate context...' --research` to update a single specific task.
|
||||
|
||||
## Task Status Management
|
||||
|
||||
- Use 'pending' for tasks ready to be worked on
|
||||
- Use 'done' for completed and verified tasks
|
||||
- Use 'deferred' for postponed tasks
|
||||
- Add custom status values as needed for project-specific workflows
|
||||
|
||||
## Task Structure Fields
|
||||
|
||||
- **id**: Unique identifier for the task (Example: `1`, `1.1`)
|
||||
- **title**: Brief, descriptive title (Example: `"Initialize Repo"`)
|
||||
- **description**: Concise summary of what the task involves (Example: `"Create a new repository, set up initial structure."`)
|
||||
- **status**: Current state of the task (Example: `"pending"`, `"done"`, `"deferred"`)
|
||||
- **dependencies**: IDs of prerequisite tasks (Example: `[1, 2.1]`)
|
||||
- Dependencies are displayed with status indicators (✅ for completed, ⏱️ for pending)
|
||||
- This helps quickly identify which prerequisite tasks are blocking work
|
||||
- **priority**: Importance level (Example: `"high"`, `"medium"`, `"low"`)
|
||||
- **details**: In-depth implementation instructions (Example: `"Use GitHub client ID/secret, handle callback, set session token."`)
|
||||
- **testStrategy**: Verification approach (Example: `"Deploy and call endpoint to confirm 'Hello World' response."`)
|
||||
- **subtasks**: List of smaller, more specific tasks (Example: `[{"id": 1, "title": "Configure OAuth", ...}]`)
|
||||
- Refer to task structure details (previously linked to `tasks.mdc`).
|
||||
|
||||
## Configuration Management (Updated)
|
||||
|
||||
Taskmaster configuration is managed through two main mechanisms:
|
||||
|
||||
1. **`.taskmaster/config.json` File (Primary):**
|
||||
* Located in the project root directory.
|
||||
* Stores most configuration settings: AI model selections (main, research, fallback), parameters (max tokens, temperature), logging level, default subtasks/priority, project name, etc.
|
||||
* **Tagged System Settings**: Includes `global.defaultTag` (defaults to "master") and `tags` section for tag management configuration.
|
||||
* **Managed via `task-master models --setup` command.** Do not edit manually unless you know what you are doing.
|
||||
* **View/Set specific models via `task-master models` command or `models` MCP tool.**
|
||||
* Created automatically when you run `task-master models --setup` for the first time or during tagged system migration.
|
||||
|
||||
2. **Environment Variables (`.env` / `mcp.json`):**
|
||||
* Used **only** for sensitive API keys and specific endpoint URLs.
|
||||
* Place API keys (one per provider) in a `.env` file in the project root for CLI usage.
|
||||
* For MCP/Cursor integration, configure these keys in the `env` section of `.cursor/mcp.json`.
|
||||
* Available keys/variables: See `assets/env.example` or the Configuration section in the command reference (previously linked to `taskmaster.mdc`).
|
||||
|
||||
3. **`.taskmaster/state.json` File (Tagged System State):**
|
||||
* Tracks current tag context and migration status.
|
||||
* Automatically created during tagged system migration.
|
||||
* Contains: `currentTag`, `lastSwitched`, `migrationNoticeShown`.
|
||||
|
||||
**Important:** Non-API key settings (like model selections, `MAX_TOKENS`, `TASKMASTER_LOG_LEVEL`) are **no longer configured via environment variables**. Use the `task-master models` command (or `--setup` for interactive configuration) or the `models` MCP tool.
|
||||
**If AI commands FAIL in MCP** verify that the API key for the selected provider is present in the `env` section of `.cursor/mcp.json`.
|
||||
**If AI commands FAIL in CLI** verify that the API key for the selected provider is present in the `.env` file in the root of the project.
|
||||
|
||||
## Rules Management
|
||||
|
||||
Taskmaster supports multiple AI coding assistant rule sets that can be configured during project initialization or managed afterward:
|
||||
|
||||
- **Available Profiles**: Claude Code, Cline, Codex, Cursor, Roo Code, Trae, Windsurf (claude, cline, codex, cursor, roo, trae, windsurf)
|
||||
- **During Initialization**: Use `task-master init --rules cursor,windsurf` to specify which rule sets to include
|
||||
- **After Initialization**: Use `task-master rules add <profiles>` or `task-master rules remove <profiles>` to manage rule sets
|
||||
- **Interactive Setup**: Use `task-master rules setup` to launch an interactive prompt for selecting rule profiles
|
||||
- **Default Behavior**: If no `--rules` flag is specified during initialization, all available rule profiles are included
|
||||
- **Rule Structure**: Each profile creates its own directory (e.g., `.cursor/rules`, `.roo/rules`) with appropriate configuration files
|
||||
|
||||
## Determining the Next Task
|
||||
|
||||
- Run `next_task` / `task-master next` to show the next task to work on.
|
||||
- The command identifies tasks with all dependencies satisfied
|
||||
- Tasks are prioritized by priority level, dependency count, and ID
|
||||
- The command shows comprehensive task information including:
|
||||
- Basic task details and description
|
||||
- Implementation details
|
||||
- Subtasks (if they exist)
|
||||
- Contextual suggested actions
|
||||
- Recommended before starting any new development work
|
||||
- Respects your project's dependency structure
|
||||
- Ensures tasks are completed in the appropriate sequence
|
||||
- Provides ready-to-use commands for common task actions
|
||||
|
||||
## Viewing Specific Task Details
|
||||
|
||||
- Run `get_task` / `task-master show <id>` to view a specific task.
|
||||
- Use dot notation for subtasks: `task-master show 1.2` (shows subtask 2 of task 1)
|
||||
- Displays comprehensive information similar to the next command, but for a specific task
|
||||
- For parent tasks, shows all subtasks and their current status
|
||||
- For subtasks, shows parent task information and relationship
|
||||
- Provides contextual suggested actions appropriate for the specific task
|
||||
- Useful for examining task details before implementation or checking status
|
||||
|
||||
## Managing Task Dependencies
|
||||
|
||||
- Use `add_dependency` / `task-master add-dependency --id=<id> --depends-on=<id>` to add a dependency.
|
||||
- Use `remove_dependency` / `task-master remove-dependency --id=<id> --depends-on=<id>` to remove a dependency.
|
||||
- The system prevents circular dependencies and duplicate dependency entries
|
||||
- Dependencies are checked for existence before being added or removed
|
||||
- Task files are automatically regenerated after dependency changes
|
||||
- Dependencies are visualized with status indicators in task listings and files
|
||||
|
||||
## Task Reorganization
|
||||
|
||||
- Use `move_task` / `task-master move --from=<id> --to=<id>` to move tasks or subtasks within the hierarchy
|
||||
- This command supports several use cases:
|
||||
- Moving a standalone task to become a subtask (e.g., `--from=5 --to=7`)
|
||||
- Moving a subtask to become a standalone task (e.g., `--from=5.2 --to=7`)
|
||||
- Moving a subtask to a different parent (e.g., `--from=5.2 --to=7.3`)
|
||||
- Reordering subtasks within the same parent (e.g., `--from=5.2 --to=5.4`)
|
||||
- Moving a task to a new, non-existent ID position (e.g., `--from=5 --to=25`)
|
||||
- Moving multiple tasks at once using comma-separated IDs (e.g., `--from=10,11,12 --to=16,17,18`)
|
||||
- The system includes validation to prevent data loss:
|
||||
- Allows moving to non-existent IDs by creating placeholder tasks
|
||||
- Prevents moving to existing task IDs that have content (to avoid overwriting)
|
||||
- Validates source tasks exist before attempting to move them
|
||||
- The system maintains proper parent-child relationships and dependency integrity
|
||||
- Task files are automatically regenerated after the move operation
|
||||
- This provides greater flexibility in organizing and refining your task structure as project understanding evolves
|
||||
- This is especially useful when dealing with potential merge conflicts arising from teams creating tasks on separate branches. Solve these conflicts very easily by moving your tasks and keeping theirs.
|
||||
|
||||
## Iterative Subtask Implementation
|
||||
|
||||
Once a task has been broken down into subtasks using `expand_task` or similar methods, follow this iterative process for implementation:
|
||||
|
||||
1. **Understand the Goal (Preparation):**
|
||||
* Use `get_task` / `task-master show <subtaskId>` (see @`taskmaster.mdc`) to thoroughly understand the specific goals and requirements of the subtask.
|
||||
|
||||
2. **Initial Exploration & Planning (Iteration 1):**
|
||||
* This is the first attempt at creating a concrete implementation plan.
|
||||
* Explore the codebase to identify the precise files, functions, and even specific lines of code that will need modification.
|
||||
* Determine the intended code changes (diffs) and their locations.
|
||||
* Gather *all* relevant details from this exploration phase.
|
||||
|
||||
3. **Log the Plan:**
|
||||
* Run `update_subtask` / `task-master update-subtask --id=<subtaskId> --prompt='<detailed plan>'`.
|
||||
* Provide the *complete and detailed* findings from the exploration phase in the prompt. Include file paths, line numbers, proposed diffs, reasoning, and any potential challenges identified. Do not omit details. The goal is to create a rich, timestamped log within the subtask's `details`.
|
||||
|
||||
4. **Verify the Plan:**
|
||||
* Run `get_task` / `task-master show <subtaskId>` again to confirm that the detailed implementation plan has been successfully appended to the subtask's details.
|
||||
|
||||
5. **Begin Implementation:**
|
||||
* Set the subtask status using `set_task_status` / `task-master set-status --id=<subtaskId> --status=in-progress`.
|
||||
* Start coding based on the logged plan.
|
||||
|
||||
6. **Refine and Log Progress (Iteration 2+):**
|
||||
* As implementation progresses, you will encounter challenges, discover nuances, or confirm successful approaches.
|
||||
* **Before appending new information**: Briefly review the *existing* details logged in the subtask (using `get_task` or recalling from context) to ensure the update adds fresh insights and avoids redundancy.
|
||||
* **Regularly** use `update_subtask` / `task-master update-subtask --id=<subtaskId> --prompt='<update details>\n- What worked...\n- What didn't work...'` to append new findings.
|
||||
* **Crucially, log:**
|
||||
* What worked ("fundamental truths" discovered).
|
||||
* What didn't work and why (to avoid repeating mistakes).
|
||||
* Specific code snippets or configurations that were successful.
|
||||
* Decisions made, especially if confirmed with user input.
|
||||
* Any deviations from the initial plan and the reasoning.
|
||||
* The objective is to continuously enrich the subtask's details, creating a log of the implementation journey that helps the AI (and human developers) learn, adapt, and avoid repeating errors.
|
||||
|
||||
7. **Review & Update Rules (Post-Implementation):**
|
||||
* Once the implementation for the subtask is functionally complete, review all code changes and the relevant chat history.
|
||||
* Identify any new or modified code patterns, conventions, or best practices established during the implementation.
|
||||
* Create new or update existing rules following internal guidelines (previously linked to `cursor_rules.mdc` and `self_improve.mdc`).
|
||||
|
||||
8. **Mark Task Complete:**
|
||||
* After verifying the implementation and updating any necessary rules, mark the subtask as completed: `set_task_status` / `task-master set-status --id=<subtaskId> --status=done`.
|
||||
|
||||
9. **Commit Changes (If using Git):**
|
||||
* Stage the relevant code changes and any updated/new rule files (`git add .`).
|
||||
* Craft a comprehensive Git commit message summarizing the work done for the subtask, including both code implementation and any rule adjustments.
|
||||
* Execute the commit command directly in the terminal (e.g., `git commit -m 'feat(module): Implement feature X for subtask <subtaskId>\n\n- Details about changes...\n- Updated rule Y for pattern Z'`).
|
||||
* Consider if a Changeset is needed according to internal versioning guidelines (previously linked to `changeset.mdc`). If so, run `npm run changeset`, stage the generated file, and amend the commit or create a new one.
|
||||
|
||||
10. **Proceed to Next Subtask:**
|
||||
* Identify the next subtask (e.g., using `next_task` / `task-master next`).
|
||||
|
||||
## Code Analysis & Refactoring Techniques
|
||||
|
||||
- **Top-Level Function Search**:
|
||||
- Useful for understanding module structure or planning refactors.
|
||||
- Use grep/ripgrep to find exported functions/constants:
|
||||
`rg "export (async function|function|const) \w+"` or similar patterns.
|
||||
- Can help compare functions between files during migrations or identify potential naming conflicts.
|
||||
|
||||
---
|
||||
*This workflow provides a general guideline. Adapt it based on your specific project needs and team practices.*
|
||||
@@ -1,72 +0,0 @@
|
||||
---
|
||||
description: Guidelines for continuously improving Cursor rules based on emerging code patterns and best practices.
|
||||
globs: **/*
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
- **Rule Improvement Triggers:**
|
||||
- New code patterns not covered by existing rules
|
||||
- Repeated similar implementations across files
|
||||
- Common error patterns that could be prevented
|
||||
- New libraries or tools being used consistently
|
||||
- Emerging best practices in the codebase
|
||||
|
||||
- **Analysis Process:**
|
||||
- Compare new code with existing rules
|
||||
- Identify patterns that should be standardized
|
||||
- Look for references to external documentation
|
||||
- Check for consistent error handling patterns
|
||||
- Monitor test patterns and coverage
|
||||
|
||||
- **Rule Updates:**
|
||||
- **Add New Rules When:**
|
||||
- A new technology/pattern is used in 3+ files
|
||||
- Common bugs could be prevented by a rule
|
||||
- Code reviews repeatedly mention the same feedback
|
||||
- New security or performance patterns emerge
|
||||
|
||||
- **Modify Existing Rules When:**
|
||||
- Better examples exist in the codebase
|
||||
- Additional edge cases are discovered
|
||||
- Related rules have been updated
|
||||
- Implementation details have changed
|
||||
|
||||
- **Example Pattern Recognition:**
|
||||
```typescript
|
||||
// If you see repeated patterns like:
|
||||
const data = await prisma.user.findMany({
|
||||
select: { id: true, email: true },
|
||||
where: { status: 'ACTIVE' }
|
||||
});
|
||||
|
||||
// Consider adding to [prisma.mdc](mdc:.cursor/rules/prisma.mdc):
|
||||
// - Standard select fields
|
||||
// - Common where conditions
|
||||
// - Performance optimization patterns
|
||||
```
|
||||
|
||||
- **Rule Quality Checks:**
|
||||
- Rules should be actionable and specific
|
||||
- Examples should come from actual code
|
||||
- References should be up to date
|
||||
- Patterns should be consistently enforced
|
||||
|
||||
- **Continuous Improvement:**
|
||||
- Monitor code review comments
|
||||
- Track common development questions
|
||||
- Update rules after major refactors
|
||||
- Add links to relevant documentation
|
||||
- Cross-reference related rules
|
||||
|
||||
- **Rule Deprecation:**
|
||||
- Mark outdated patterns as deprecated
|
||||
- Remove rules that no longer apply
|
||||
- Update references to deprecated rules
|
||||
- Document migration paths for old patterns
|
||||
|
||||
- **Documentation Updates:**
|
||||
- Keep examples synchronized with code
|
||||
- Update references to external docs
|
||||
- Maintain links between related rules
|
||||
- Document breaking changes
|
||||
Follow [cursor_rules.mdc](mdc:.cursor/rules/cursor_rules.mdc) for proper rule formatting and structure.
|
||||
@@ -1,558 +0,0 @@
|
||||
---
|
||||
description: Comprehensive reference for Taskmaster MCP tools and CLI commands.
|
||||
globs: **/*
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# Taskmaster Tool & Command Reference
|
||||
|
||||
This document provides a detailed reference for interacting with Taskmaster, covering both the recommended MCP tools, suitable for integrations like Cursor, and the corresponding `task-master` CLI commands, designed for direct user interaction or fallback.
|
||||
|
||||
**Note:** For interacting with Taskmaster programmatically or via integrated tools, using the **MCP tools is strongly recommended** due to better performance, structured data, and error handling. The CLI commands serve as a user-friendly alternative and fallback.
|
||||
|
||||
**Important:** Several MCP tools involve AI processing... The AI-powered tools include `parse_prd`, `analyze_project_complexity`, `update_subtask`, `update_task`, `update`, `expand_all`, `expand_task`, and `add_task`.
|
||||
|
||||
**🏷️ Tagged Task Lists System:** Task Master now supports **tagged task lists** for multi-context task management. This allows you to maintain separate, isolated lists of tasks for different features, branches, or experiments. Existing projects are seamlessly migrated to use a default "master" tag. Most commands now support a `--tag <name>` flag to specify which context to operate on. If omitted, commands use the currently active tag.
|
||||
|
||||
---
|
||||
|
||||
## Initialization & Setup
|
||||
|
||||
### 1. Initialize Project (`init`)
|
||||
|
||||
* **MCP Tool:** `initialize_project`
|
||||
* **CLI Command:** `task-master init [options]`
|
||||
* **Description:** `Set up the basic Taskmaster file structure and configuration in the current directory for a new project.`
|
||||
* **Key CLI Options:**
|
||||
* `--name <name>`: `Set the name for your project in Taskmaster's configuration.`
|
||||
* `--description <text>`: `Provide a brief description for your project.`
|
||||
* `--version <version>`: `Set the initial version for your project, e.g., '0.1.0'.`
|
||||
* `-y, --yes`: `Initialize Taskmaster quickly using default settings without interactive prompts.`
|
||||
* **Usage:** Run this once at the beginning of a new project.
|
||||
* **MCP Variant Description:** `Set up the basic Taskmaster file structure and configuration in the current directory for a new project by running the 'task-master init' command.`
|
||||
* **Key MCP Parameters/Options:**
|
||||
* `projectName`: `Set the name for your project.` (CLI: `--name <name>`)
|
||||
* `projectDescription`: `Provide a brief description for your project.` (CLI: `--description <text>`)
|
||||
* `projectVersion`: `Set the initial version for your project, e.g., '0.1.0'.` (CLI: `--version <version>`)
|
||||
* `authorName`: `Author name.` (CLI: `--author <author>`)
|
||||
* `skipInstall`: `Skip installing dependencies. Default is false.` (CLI: `--skip-install`)
|
||||
* `addAliases`: `Add shell aliases tm and taskmaster. Default is false.` (CLI: `--aliases`)
|
||||
* `yes`: `Skip prompts and use defaults/provided arguments. Default is false.` (CLI: `-y, --yes`)
|
||||
* **Usage:** Run this once at the beginning of a new project, typically via an integrated tool like Cursor. Operates on the current working directory of the MCP server.
|
||||
* **Important:** Once complete, you *MUST* parse a prd in order to generate tasks. There will be no tasks files until then. The next step after initializing should be to create a PRD using the example PRD in .taskmaster/templates/example_prd.txt.
|
||||
* **Tagging:** Use the `--tag` option to parse the PRD into a specific, non-default tag context. If the tag doesn't exist, it will be created automatically. Example: `task-master parse-prd spec.txt --tag=new-feature`.
|
||||
|
||||
### 2. Parse PRD (`parse_prd`)
|
||||
|
||||
* **MCP Tool:** `parse_prd`
|
||||
* **CLI Command:** `task-master parse-prd [file] [options]`
|
||||
* **Description:** `Parse a Product Requirements Document, PRD, or text file with Taskmaster to automatically generate an initial set of tasks in tasks.json.`
|
||||
* **Key Parameters/Options:**
|
||||
* `input`: `Path to your PRD or requirements text file that Taskmaster should parse for tasks.` (CLI: `[file]` positional or `-i, --input <file>`)
|
||||
* `output`: `Specify where Taskmaster should save the generated 'tasks.json' file. Defaults to '.taskmaster/tasks/tasks.json'.` (CLI: `-o, --output <file>`)
|
||||
* `numTasks`: `Approximate number of top-level tasks Taskmaster should aim to generate from the document.` (CLI: `-n, --num-tasks <number>`)
|
||||
* `force`: `Use this to allow Taskmaster to overwrite an existing 'tasks.json' without asking for confirmation.` (CLI: `-f, --force`)
|
||||
* **Usage:** Useful for bootstrapping a project from an existing requirements document.
|
||||
* **Notes:** Task Master will strictly adhere to any specific requirements mentioned in the PRD, such as libraries, database schemas, frameworks, tech stacks, etc., while filling in any gaps where the PRD isn't fully specified. Tasks are designed to provide the most direct implementation path while avoiding over-engineering.
|
||||
* **Important:** This MCP tool makes AI calls and can take up to a minute to complete. Please inform users to hang tight while the operation is in progress. If the user does not have a PRD, suggest discussing their idea and then use the example PRD in `.taskmaster/templates/example_prd.txt` as a template for creating the PRD based on their idea, for use with `parse-prd`.
|
||||
|
||||
---
|
||||
|
||||
## AI Model Configuration
|
||||
|
||||
### 2. Manage Models (`models`)
|
||||
* **MCP Tool:** `models`
|
||||
* **CLI Command:** `task-master models [options]`
|
||||
* **Description:** `View the current AI model configuration or set specific models for different roles (main, research, fallback). Allows setting custom model IDs for Ollama and OpenRouter.`
|
||||
* **Key MCP Parameters/Options:**
|
||||
* `setMain <model_id>`: `Set the primary model ID for task generation/updates.` (CLI: `--set-main <model_id>`)
|
||||
* `setResearch <model_id>`: `Set the model ID for research-backed operations.` (CLI: `--set-research <model_id>`)
|
||||
* `setFallback <model_id>`: `Set the model ID to use if the primary fails.` (CLI: `--set-fallback <model_id>`)
|
||||
* `ollama <boolean>`: `Indicates the set model ID is a custom Ollama model.` (CLI: `--ollama`)
|
||||
* `openrouter <boolean>`: `Indicates the set model ID is a custom OpenRouter model.` (CLI: `--openrouter`)
|
||||
* `listAvailableModels <boolean>`: `If true, lists available models not currently assigned to a role.` (CLI: No direct equivalent; CLI lists available automatically)
|
||||
* `projectRoot <string>`: `Optional. Absolute path to the project root directory.` (CLI: Determined automatically)
|
||||
* **Key CLI Options:**
|
||||
* `--set-main <model_id>`: `Set the primary model.`
|
||||
* `--set-research <model_id>`: `Set the research model.`
|
||||
* `--set-fallback <model_id>`: `Set the fallback model.`
|
||||
* `--ollama`: `Specify that the provided model ID is for Ollama (use with --set-*).`
|
||||
* `--openrouter`: `Specify that the provided model ID is for OpenRouter (use with --set-*). Validates against OpenRouter API.`
|
||||
* `--bedrock`: `Specify that the provided model ID is for AWS Bedrock (use with --set-*).`
|
||||
* `--setup`: `Run interactive setup to configure models, including custom Ollama/OpenRouter IDs.`
|
||||
* **Usage (MCP):** Call without set flags to get current config. Use `setMain`, `setResearch`, or `setFallback` with a valid model ID to update the configuration. Use `listAvailableModels: true` to get a list of unassigned models. To set a custom model, provide the model ID and set `ollama: true` or `openrouter: true`.
|
||||
* **Usage (CLI):** Run without flags to view current configuration and available models. Use set flags to update specific roles. Use `--setup` for guided configuration, including custom models. To set a custom model via flags, use `--set-<role>=<model_id>` along with either `--ollama` or `--openrouter`.
|
||||
* **Notes:** Configuration is stored in `.taskmaster/config.json` in the project root. This command/tool modifies that file. Use `listAvailableModels` or `task-master models` to see internally supported models. OpenRouter custom models are validated against their live API. Ollama custom models are not validated live.
|
||||
* **API note:** API keys for selected AI providers (based on their model) need to exist in the mcp.json file to be accessible in MCP context. The API keys must be present in the local .env file for the CLI to be able to read them.
|
||||
* **Model costs:** The costs in supported models are expressed in dollars. An input/output value of 3 is $3.00. A value of 0.8 is $0.80.
|
||||
* **Warning:** DO NOT MANUALLY EDIT THE .taskmaster/config.json FILE. Use the included commands either in the MCP or CLI format as needed. Always prioritize MCP tools when available and use the CLI as a fallback.
|
||||
|
||||
---
|
||||
|
||||
## Task Listing & Viewing
|
||||
|
||||
### 3. Get Tasks (`get_tasks`)
|
||||
|
||||
* **MCP Tool:** `get_tasks`
|
||||
* **CLI Command:** `task-master list [options]`
|
||||
* **Description:** `List your Taskmaster tasks, optionally filtering by status and showing subtasks.`
|
||||
* **Key Parameters/Options:**
|
||||
* `status`: `Show only Taskmaster tasks matching this status (or multiple statuses, comma-separated), e.g., 'pending' or 'done,in-progress'.` (CLI: `-s, --status <status>`)
|
||||
* `withSubtasks`: `Include subtasks indented under their parent tasks in the list.` (CLI: `--with-subtasks`)
|
||||
* `tag`: `Specify which tag context to list tasks from. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Get an overview of the project status, often used at the start of a work session.
|
||||
|
||||
### 4. Get Next Task (`next_task`)
|
||||
|
||||
* **MCP Tool:** `next_task`
|
||||
* **CLI Command:** `task-master next [options]`
|
||||
* **Description:** `Ask Taskmaster to show the next available task you can work on, based on status and completed dependencies.`
|
||||
* **Key Parameters/Options:**
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* `tag`: `Specify which tag context to use. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* **Usage:** Identify what to work on next according to the plan.
|
||||
|
||||
### 5. Get Task Details (`get_task`)
|
||||
|
||||
* **MCP Tool:** `get_task`
|
||||
* **CLI Command:** `task-master show [id] [options]`
|
||||
* **Description:** `Display detailed information for one or more specific Taskmaster tasks or subtasks by ID.`
|
||||
* **Key Parameters/Options:**
|
||||
* `id`: `Required. The ID of the Taskmaster task (e.g., '15'), subtask (e.g., '15.2'), or a comma-separated list of IDs ('1,5,10.2') you want to view.` (CLI: `[id]` positional or `-i, --id <id>`)
|
||||
* `tag`: `Specify which tag context to get the task(s) from. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Understand the full details for a specific task. When multiple IDs are provided, a summary table is shown.
|
||||
* **CRITICAL INFORMATION** If you need to collect information from multiple tasks, use comma-separated IDs (i.e. 1,2,3) to receive an array of tasks. Do not needlessly get tasks one at a time if you need to get many as that is wasteful.
|
||||
|
||||
---
|
||||
|
||||
## Task Creation & Modification
|
||||
|
||||
### 6. Add Task (`add_task`)
|
||||
|
||||
* **MCP Tool:** `add_task`
|
||||
* **CLI Command:** `task-master add-task [options]`
|
||||
* **Description:** `Add a new task to Taskmaster by describing it; AI will structure it.`
|
||||
* **Key Parameters/Options:**
|
||||
* `prompt`: `Required. Describe the new task you want Taskmaster to create, e.g., "Implement user authentication using JWT".` (CLI: `-p, --prompt <text>`)
|
||||
* `dependencies`: `Specify the IDs of any Taskmaster tasks that must be completed before this new one can start, e.g., '12,14'.` (CLI: `-d, --dependencies <ids>`)
|
||||
* `priority`: `Set the priority for the new task: 'high', 'medium', or 'low'. Default is 'medium'.` (CLI: `--priority <priority>`)
|
||||
* `research`: `Enable Taskmaster to use the research role for potentially more informed task creation.` (CLI: `-r, --research`)
|
||||
* `tag`: `Specify which tag context to add the task to. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Quickly add newly identified tasks during development.
|
||||
* **Important:** This MCP tool makes AI calls and can take up to a minute to complete. Please inform users to hang tight while the operation is in progress.
|
||||
|
||||
### 7. Add Subtask (`add_subtask`)
|
||||
|
||||
* **MCP Tool:** `add_subtask`
|
||||
* **CLI Command:** `task-master add-subtask [options]`
|
||||
* **Description:** `Add a new subtask to a Taskmaster parent task, or convert an existing task into a subtask.`
|
||||
* **Key Parameters/Options:**
|
||||
* `id` / `parent`: `Required. The ID of the Taskmaster task that will be the parent.` (MCP: `id`, CLI: `-p, --parent <id>`)
|
||||
* `taskId`: `Use this if you want to convert an existing top-level Taskmaster task into a subtask of the specified parent.` (CLI: `-i, --task-id <id>`)
|
||||
* `title`: `Required if not using taskId. The title for the new subtask Taskmaster should create.` (CLI: `-t, --title <title>`)
|
||||
* `description`: `A brief description for the new subtask.` (CLI: `-d, --description <text>`)
|
||||
* `details`: `Provide implementation notes or details for the new subtask.` (CLI: `--details <text>`)
|
||||
* `dependencies`: `Specify IDs of other tasks or subtasks, e.g., '15' or '16.1', that must be done before this new subtask.` (CLI: `--dependencies <ids>`)
|
||||
* `status`: `Set the initial status for the new subtask. Default is 'pending'.` (CLI: `-s, --status <status>`)
|
||||
* `skipGenerate`: `Prevent Taskmaster from automatically regenerating markdown task files after adding the subtask.` (CLI: `--skip-generate`)
|
||||
* `tag`: `Specify which tag context to operate on. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Break down tasks manually or reorganize existing tasks.
|
||||
|
||||
### 8. Update Tasks (`update`)
|
||||
|
||||
* **MCP Tool:** `update`
|
||||
* **CLI Command:** `task-master update [options]`
|
||||
* **Description:** `Update multiple upcoming tasks in Taskmaster based on new context or changes, starting from a specific task ID.`
|
||||
* **Key Parameters/Options:**
|
||||
* `from`: `Required. The ID of the first task Taskmaster should update. All tasks with this ID or higher that are not 'done' will be considered.` (CLI: `--from <id>`)
|
||||
* `prompt`: `Required. Explain the change or new context for Taskmaster to apply to the tasks, e.g., "We are now using React Query instead of Redux Toolkit for data fetching".` (CLI: `-p, --prompt <text>`)
|
||||
* `research`: `Enable Taskmaster to use the research role for more informed updates. Requires appropriate API key.` (CLI: `-r, --research`)
|
||||
* `tag`: `Specify which tag context to operate on. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Handle significant implementation changes or pivots that affect multiple future tasks. Example CLI: `task-master update --from='18' --prompt='Switching to React Query.\nNeed to refactor data fetching...'`
|
||||
* **Important:** This MCP tool makes AI calls and can take up to a minute to complete. Please inform users to hang tight while the operation is in progress.
|
||||
|
||||
### 9. Update Task (`update_task`)
|
||||
|
||||
* **MCP Tool:** `update_task`
|
||||
* **CLI Command:** `task-master update-task [options]`
|
||||
* **Description:** `Modify a specific Taskmaster task by ID, incorporating new information or changes. By default, this replaces the existing task details.`
|
||||
* **Key Parameters/Options:**
|
||||
* `id`: `Required. The specific ID of the Taskmaster task, e.g., '15', you want to update.` (CLI: `-i, --id <id>`)
|
||||
* `prompt`: `Required. Explain the specific changes or provide the new information Taskmaster should incorporate into this task.` (CLI: `-p, --prompt <text>`)
|
||||
* `append`: `If true, appends the prompt content to the task's details with a timestamp, rather than replacing them. Behaves like update-subtask.` (CLI: `--append`)
|
||||
* `research`: `Enable Taskmaster to use the research role for more informed updates. Requires appropriate API key.` (CLI: `-r, --research`)
|
||||
* `tag`: `Specify which tag context the task belongs to. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Refine a specific task based on new understanding. Use `--append` to log progress without creating subtasks.
|
||||
* **Important:** This MCP tool makes AI calls and can take up to a minute to complete. Please inform users to hang tight while the operation is in progress.
|
||||
|
||||
### 10. Update Subtask (`update_subtask`)
|
||||
|
||||
* **MCP Tool:** `update_subtask`
|
||||
* **CLI Command:** `task-master update-subtask [options]`
|
||||
* **Description:** `Append timestamped notes or details to a specific Taskmaster subtask without overwriting existing content. Intended for iterative implementation logging.`
|
||||
* **Key Parameters/Options:**
|
||||
* `id`: `Required. The ID of the Taskmaster subtask, e.g., '5.2', to update with new information.` (CLI: `-i, --id <id>`)
|
||||
* `prompt`: `Required. The information, findings, or progress notes to append to the subtask's details with a timestamp.` (CLI: `-p, --prompt <text>`)
|
||||
* `research`: `Enable Taskmaster to use the research role for more informed updates. Requires appropriate API key.` (CLI: `-r, --research`)
|
||||
* `tag`: `Specify which tag context the subtask belongs to. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Log implementation progress, findings, and discoveries during subtask development. Each update is timestamped and appended to preserve the implementation journey.
|
||||
* **Important:** This MCP tool makes AI calls and can take up to a minute to complete. Please inform users to hang tight while the operation is in progress.
|
||||
|
||||
### 11. Set Task Status (`set_task_status`)
|
||||
|
||||
* **MCP Tool:** `set_task_status`
|
||||
* **CLI Command:** `task-master set-status [options]`
|
||||
* **Description:** `Update the status of one or more Taskmaster tasks or subtasks, e.g., 'pending', 'in-progress', 'done'.`
|
||||
* **Key Parameters/Options:**
|
||||
* `id`: `Required. The ID(s) of the Taskmaster task(s) or subtask(s), e.g., '15', '15.2', or '16,17.1', to update.` (CLI: `-i, --id <id>`)
|
||||
* `status`: `Required. The new status to set, e.g., 'done', 'pending', 'in-progress', 'review', 'cancelled'.` (CLI: `-s, --status <status>`)
|
||||
* `tag`: `Specify which tag context to operate on. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Mark progress as tasks move through the development cycle.
|
||||
|
||||
### 12. Remove Task (`remove_task`)
|
||||
|
||||
* **MCP Tool:** `remove_task`
|
||||
* **CLI Command:** `task-master remove-task [options]`
|
||||
* **Description:** `Permanently remove a task or subtask from the Taskmaster tasks list.`
|
||||
* **Key Parameters/Options:**
|
||||
* `id`: `Required. The ID of the Taskmaster task, e.g., '5', or subtask, e.g., '5.2', to permanently remove.` (CLI: `-i, --id <id>`)
|
||||
* `yes`: `Skip the confirmation prompt and immediately delete the task.` (CLI: `-y, --yes`)
|
||||
* `tag`: `Specify which tag context to operate on. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Permanently delete tasks or subtasks that are no longer needed in the project.
|
||||
* **Notes:** Use with caution as this operation cannot be undone. Consider using 'blocked', 'cancelled', or 'deferred' status instead if you just want to exclude a task from active planning but keep it for reference. The command automatically cleans up dependency references in other tasks.
|
||||
|
||||
---
|
||||
|
||||
## Task Structure & Breakdown
|
||||
|
||||
### 13. Expand Task (`expand_task`)
|
||||
|
||||
* **MCP Tool:** `expand_task`
|
||||
* **CLI Command:** `task-master expand [options]`
|
||||
* **Description:** `Use Taskmaster's AI to break down a complex task into smaller, manageable subtasks. Appends subtasks by default.`
|
||||
* **Key Parameters/Options:**
|
||||
* `id`: `The ID of the specific Taskmaster task you want to break down into subtasks.` (CLI: `-i, --id <id>`)
|
||||
* `num`: `Optional: Suggests how many subtasks Taskmaster should aim to create. Uses complexity analysis/defaults otherwise.` (CLI: `-n, --num <number>`)
|
||||
* `research`: `Enable Taskmaster to use the research role for more informed subtask generation. Requires appropriate API key.` (CLI: `-r, --research`)
|
||||
* `prompt`: `Optional: Provide extra context or specific instructions to Taskmaster for generating the subtasks.` (CLI: `-p, --prompt <text>`)
|
||||
* `force`: `Optional: If true, clear existing subtasks before generating new ones. Default is false (append).` (CLI: `--force`)
|
||||
* `tag`: `Specify which tag context the task belongs to. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Generate a detailed implementation plan for a complex task before starting coding. Automatically uses complexity report recommendations if available and `num` is not specified.
|
||||
* **Important:** This MCP tool makes AI calls and can take up to a minute to complete. Please inform users to hang tight while the operation is in progress.
|
||||
|
||||
### 14. Expand All Tasks (`expand_all`)
|
||||
|
||||
* **MCP Tool:** `expand_all`
|
||||
* **CLI Command:** `task-master expand --all [options]` (Note: CLI uses the `expand` command with the `--all` flag)
|
||||
* **Description:** `Tell Taskmaster to automatically expand all eligible pending/in-progress tasks based on complexity analysis or defaults. Appends subtasks by default.`
|
||||
* **Key Parameters/Options:**
|
||||
* `num`: `Optional: Suggests how many subtasks Taskmaster should aim to create per task.` (CLI: `-n, --num <number>`)
|
||||
* `research`: `Enable research role for more informed subtask generation. Requires appropriate API key.` (CLI: `-r, --research`)
|
||||
* `prompt`: `Optional: Provide extra context for Taskmaster to apply generally during expansion.` (CLI: `-p, --prompt <text>`)
|
||||
* `force`: `Optional: If true, clear existing subtasks before generating new ones for each eligible task. Default is false (append).` (CLI: `--force`)
|
||||
* `tag`: `Specify which tag context to expand. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Useful after initial task generation or complexity analysis to break down multiple tasks at once.
|
||||
* **Important:** This MCP tool makes AI calls and can take up to a minute to complete. Please inform users to hang tight while the operation is in progress.
|
||||
|
||||
### 15. Clear Subtasks (`clear_subtasks`)
|
||||
|
||||
* **MCP Tool:** `clear_subtasks`
|
||||
* **CLI Command:** `task-master clear-subtasks [options]`
|
||||
* **Description:** `Remove all subtasks from one or more specified Taskmaster parent tasks.`
|
||||
* **Key Parameters/Options:**
|
||||
* `id`: `The ID(s) of the Taskmaster parent task(s) whose subtasks you want to remove, e.g., '15' or '16,18'. Required unless using `all`.) (CLI: `-i, --id <ids>`)
|
||||
* `all`: `Tell Taskmaster to remove subtasks from all parent tasks.` (CLI: `--all`)
|
||||
* `tag`: `Specify which tag context to operate on. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Used before regenerating subtasks with `expand_task` if the previous breakdown needs replacement.
|
||||
|
||||
### 16. Remove Subtask (`remove_subtask`)
|
||||
|
||||
* **MCP Tool:** `remove_subtask`
|
||||
* **CLI Command:** `task-master remove-subtask [options]`
|
||||
* **Description:** `Remove a subtask from its Taskmaster parent, optionally converting it into a standalone task.`
|
||||
* **Key Parameters/Options:**
|
||||
* `id`: `Required. The ID(s) of the Taskmaster subtask(s) to remove, e.g., '15.2' or '16.1,16.3'.` (CLI: `-i, --id <id>`)
|
||||
* `convert`: `If used, Taskmaster will turn the subtask into a regular top-level task instead of deleting it.` (CLI: `-c, --convert`)
|
||||
* `skipGenerate`: `Prevent Taskmaster from automatically regenerating markdown task files after removing the subtask.` (CLI: `--skip-generate`)
|
||||
* `tag`: `Specify which tag context to operate on. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Delete unnecessary subtasks or promote a subtask to a top-level task.
|
||||
|
||||
### 17. Move Task (`move_task`)
|
||||
|
||||
* **MCP Tool:** `move_task`
|
||||
* **CLI Command:** `task-master move [options]`
|
||||
* **Description:** `Move a task or subtask to a new position within the task hierarchy.`
|
||||
* **Key Parameters/Options:**
|
||||
* `from`: `Required. ID of the task/subtask to move (e.g., "5" or "5.2"). Can be comma-separated for multiple tasks.` (CLI: `--from <id>`)
|
||||
* `to`: `Required. ID of the destination (e.g., "7" or "7.3"). Must match the number of source IDs if comma-separated.` (CLI: `--to <id>`)
|
||||
* `tag`: `Specify which tag context to operate on. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Reorganize tasks by moving them within the hierarchy. Supports various scenarios like:
|
||||
* Moving a task to become a subtask
|
||||
* Moving a subtask to become a standalone task
|
||||
* Moving a subtask to a different parent
|
||||
* Reordering subtasks within the same parent
|
||||
* Moving a task to a new, non-existent ID (automatically creates placeholders)
|
||||
* Moving multiple tasks at once with comma-separated IDs
|
||||
* **Validation Features:**
|
||||
* Allows moving tasks to non-existent destination IDs (creates placeholder tasks)
|
||||
* Prevents moving to existing task IDs that already have content (to avoid overwriting)
|
||||
* Validates that source tasks exist before attempting to move them
|
||||
* Maintains proper parent-child relationships
|
||||
* **Example CLI:** `task-master move --from=5.2 --to=7.3` to move subtask 5.2 to become subtask 7.3.
|
||||
* **Example Multi-Move:** `task-master move --from=10,11,12 --to=16,17,18` to move multiple tasks to new positions.
|
||||
* **Common Use:** Resolving merge conflicts in tasks.json when multiple team members create tasks on different branches.
|
||||
|
||||
---
|
||||
|
||||
## Dependency Management
|
||||
|
||||
### 18. Add Dependency (`add_dependency`)
|
||||
|
||||
* **MCP Tool:** `add_dependency`
|
||||
* **CLI Command:** `task-master add-dependency [options]`
|
||||
* **Description:** `Define a dependency in Taskmaster, making one task a prerequisite for another.`
|
||||
* **Key Parameters/Options:**
|
||||
* `id`: `Required. The ID of the Taskmaster task that will depend on another.` (CLI: `-i, --id <id>`)
|
||||
* `dependsOn`: `Required. The ID of the Taskmaster task that must be completed first, the prerequisite.` (CLI: `-d, --depends-on <id>`)
|
||||
* `tag`: `Specify which tag context to operate on. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <path>`)
|
||||
* **Usage:** Establish the correct order of execution between tasks.
|
||||
|
||||
### 19. Remove Dependency (`remove_dependency`)
|
||||
|
||||
* **MCP Tool:** `remove_dependency`
|
||||
* **CLI Command:** `task-master remove-dependency [options]`
|
||||
* **Description:** `Remove a dependency relationship between two Taskmaster tasks.`
|
||||
* **Key Parameters/Options:**
|
||||
* `id`: `Required. The ID of the Taskmaster task you want to remove a prerequisite from.` (CLI: `-i, --id <id>`)
|
||||
* `dependsOn`: `Required. The ID of the Taskmaster task that should no longer be a prerequisite.` (CLI: `-d, --depends-on <id>`)
|
||||
* `tag`: `Specify which tag context to operate on. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Update task relationships when the order of execution changes.
|
||||
|
||||
### 20. Validate Dependencies (`validate_dependencies`)
|
||||
|
||||
* **MCP Tool:** `validate_dependencies`
|
||||
* **CLI Command:** `task-master validate-dependencies [options]`
|
||||
* **Description:** `Check your Taskmaster tasks for dependency issues (like circular references or links to non-existent tasks) without making changes.`
|
||||
* **Key Parameters/Options:**
|
||||
* `tag`: `Specify which tag context to validate. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Audit the integrity of your task dependencies.
|
||||
|
||||
### 21. Fix Dependencies (`fix_dependencies`)
|
||||
|
||||
* **MCP Tool:** `fix_dependencies`
|
||||
* **CLI Command:** `task-master fix-dependencies [options]`
|
||||
* **Description:** `Automatically fix dependency issues (like circular references or links to non-existent tasks) in your Taskmaster tasks.`
|
||||
* **Key Parameters/Options:**
|
||||
* `tag`: `Specify which tag context to fix dependencies in. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Clean up dependency errors automatically.
|
||||
|
||||
---
|
||||
|
||||
## Analysis & Reporting
|
||||
|
||||
### 22. Analyze Project Complexity (`analyze_project_complexity`)
|
||||
|
||||
* **MCP Tool:** `analyze_project_complexity`
|
||||
* **CLI Command:** `task-master analyze-complexity [options]`
|
||||
* **Description:** `Have Taskmaster analyze your tasks to determine their complexity and suggest which ones need to be broken down further.`
|
||||
* **Key Parameters/Options:**
|
||||
* `output`: `Where to save the complexity analysis report. Default is '.taskmaster/reports/task-complexity-report.json' (or '..._tagname.json' if a tag is used).` (CLI: `-o, --output <file>`)
|
||||
* `threshold`: `The minimum complexity score (1-10) that should trigger a recommendation to expand a task.` (CLI: `-t, --threshold <number>`)
|
||||
* `research`: `Enable research role for more accurate complexity analysis. Requires appropriate API key.` (CLI: `-r, --research`)
|
||||
* `tag`: `Specify which tag context to analyze. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Used before breaking down tasks to identify which ones need the most attention.
|
||||
* **Important:** This MCP tool makes AI calls and can take up to a minute to complete. Please inform users to hang tight while the operation is in progress.
|
||||
|
||||
### 23. View Complexity Report (`complexity_report`)
|
||||
|
||||
* **MCP Tool:** `complexity_report`
|
||||
* **CLI Command:** `task-master complexity-report [options]`
|
||||
* **Description:** `Display the task complexity analysis report in a readable format.`
|
||||
* **Key Parameters/Options:**
|
||||
* `tag`: `Specify which tag context to show the report for. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to the complexity report (default: '.taskmaster/reports/task-complexity-report.json').` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Review and understand the complexity analysis results after running analyze-complexity.
|
||||
|
||||
---
|
||||
|
||||
## File Management
|
||||
|
||||
### 24. Generate Task Files (`generate`)
|
||||
|
||||
* **MCP Tool:** `generate`
|
||||
* **CLI Command:** `task-master generate [options]`
|
||||
* **Description:** `Create or update individual Markdown files for each task based on your tasks.json.`
|
||||
* **Key Parameters/Options:**
|
||||
* `output`: `The directory where Taskmaster should save the task files (default: in a 'tasks' directory).` (CLI: `-o, --output <directory>`)
|
||||
* `tag`: `Specify which tag context to generate files for. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Run this after making changes to tasks.json to keep individual task files up to date. This command is now manual and no longer runs automatically.
|
||||
|
||||
---
|
||||
|
||||
## AI-Powered Research
|
||||
|
||||
### 25. Research (`research`)
|
||||
|
||||
* **MCP Tool:** `research`
|
||||
* **CLI Command:** `task-master research [options]`
|
||||
* **Description:** `Perform AI-powered research queries with project context to get fresh, up-to-date information beyond the AI's knowledge cutoff.`
|
||||
* **Key Parameters/Options:**
|
||||
* `query`: `Required. Research query/prompt (e.g., "What are the latest best practices for React Query v5?").` (CLI: `[query]` positional or `-q, --query <text>`)
|
||||
* `taskIds`: `Comma-separated list of task/subtask IDs from the current tag context (e.g., "15,16.2,17").` (CLI: `-i, --id <ids>`)
|
||||
* `filePaths`: `Comma-separated list of file paths for context (e.g., "src/api.js,docs/readme.md").` (CLI: `-f, --files <paths>`)
|
||||
* `customContext`: `Additional custom context text to include in the research.` (CLI: `-c, --context <text>`)
|
||||
* `includeProjectTree`: `Include project file tree structure in context (default: false).` (CLI: `--tree`)
|
||||
* `detailLevel`: `Detail level for the research response: 'low', 'medium', 'high' (default: medium).` (CLI: `--detail <level>`)
|
||||
* `saveTo`: `Task or subtask ID (e.g., "15", "15.2") to automatically save the research conversation to.` (CLI: `--save-to <id>`)
|
||||
* `saveFile`: `If true, saves the research conversation to a markdown file in '.taskmaster/docs/research/'.` (CLI: `--save-file`)
|
||||
* `noFollowup`: `Disables the interactive follow-up question menu in the CLI.` (CLI: `--no-followup`)
|
||||
* `tag`: `Specify which tag context to use for task-based context gathering. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
* `projectRoot`: `The directory of the project. Must be an absolute path.` (CLI: Determined automatically)
|
||||
* **Usage:** **This is a POWERFUL tool that agents should use FREQUENTLY** to:
|
||||
* Get fresh information beyond knowledge cutoff dates
|
||||
* Research latest best practices, library updates, security patches
|
||||
* Find implementation examples for specific technologies
|
||||
* Validate approaches against current industry standards
|
||||
* Get contextual advice based on project files and tasks
|
||||
* **When to Consider Using Research:**
|
||||
* **Before implementing any task** - Research current best practices
|
||||
* **When encountering new technologies** - Get up-to-date implementation guidance (libraries, apis, etc)
|
||||
* **For security-related tasks** - Find latest security recommendations
|
||||
* **When updating dependencies** - Research breaking changes and migration guides
|
||||
* **For performance optimization** - Get current performance best practices
|
||||
* **When debugging complex issues** - Research known solutions and workarounds
|
||||
* **Research + Action Pattern:**
|
||||
* Use `research` to gather fresh information
|
||||
* Use `update_subtask` to commit findings with timestamps
|
||||
* Use `update_task` to incorporate research into task details
|
||||
* Use `add_task` with research flag for informed task creation
|
||||
* **Important:** This MCP tool makes AI calls and can take up to a minute to complete. The research provides FRESH data beyond the AI's training cutoff, making it invaluable for current best practices and recent developments.
|
||||
|
||||
---
|
||||
|
||||
## Tag Management
|
||||
|
||||
This new suite of commands allows you to manage different task contexts (tags).
|
||||
|
||||
### 26. List Tags (`tags`)
|
||||
|
||||
* **MCP Tool:** `list_tags`
|
||||
* **CLI Command:** `task-master tags [options]`
|
||||
* **Description:** `List all available tags with task counts, completion status, and other metadata.`
|
||||
* **Key Parameters/Options:**
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
* `--show-metadata`: `Include detailed metadata in the output (e.g., creation date, description).` (CLI: `--show-metadata`)
|
||||
|
||||
### 27. Add Tag (`add_tag`)
|
||||
|
||||
* **MCP Tool:** `add_tag`
|
||||
* **CLI Command:** `task-master add-tag <tagName> [options]`
|
||||
* **Description:** `Create a new, empty tag context, or copy tasks from another tag.`
|
||||
* **Key Parameters/Options:**
|
||||
* `tagName`: `Name of the new tag to create (alphanumeric, hyphens, underscores).` (CLI: `<tagName>` positional)
|
||||
* `--from-branch`: `Creates a tag with a name derived from the current git branch, ignoring the <tagName> argument.` (CLI: `--from-branch`)
|
||||
* `--copy-from-current`: `Copy tasks from the currently active tag to the new tag.` (CLI: `--copy-from-current`)
|
||||
* `--copy-from <tag>`: `Copy tasks from a specific source tag to the new tag.` (CLI: `--copy-from <tag>`)
|
||||
* `--description <text>`: `Provide an optional description for the new tag.` (CLI: `-d, --description <text>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
|
||||
### 28. Delete Tag (`delete_tag`)
|
||||
|
||||
* **MCP Tool:** `delete_tag`
|
||||
* **CLI Command:** `task-master delete-tag <tagName> [options]`
|
||||
* **Description:** `Permanently delete a tag and all of its associated tasks.`
|
||||
* **Key Parameters/Options:**
|
||||
* `tagName`: `Name of the tag to delete.` (CLI: `<tagName>` positional)
|
||||
* `--yes`: `Skip the confirmation prompt.` (CLI: `-y, --yes`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
|
||||
### 29. Use Tag (`use_tag`)
|
||||
|
||||
* **MCP Tool:** `use_tag`
|
||||
* **CLI Command:** `task-master use-tag <tagName>`
|
||||
* **Description:** `Switch your active task context to a different tag.`
|
||||
* **Key Parameters/Options:**
|
||||
* `tagName`: `Name of the tag to switch to.` (CLI: `<tagName>` positional)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
|
||||
### 30. Rename Tag (`rename_tag`)
|
||||
|
||||
* **MCP Tool:** `rename_tag`
|
||||
* **CLI Command:** `task-master rename-tag <oldName> <newName>`
|
||||
* **Description:** `Rename an existing tag.`
|
||||
* **Key Parameters/Options:**
|
||||
* `oldName`: `The current name of the tag.` (CLI: `<oldName>` positional)
|
||||
* `newName`: `The new name for the tag.` (CLI: `<newName>` positional)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`)
|
||||
|
||||
### 31. Copy Tag (`copy_tag`)
|
||||
|
||||
* **MCP Tool:** `copy_tag`
|
||||
* **CLI Command:** `task-master copy-tag <sourceName> <targetName> [options]`
|
||||
* **Description:** `Copy an entire tag context, including all its tasks and metadata, to a new tag.`
|
||||
* **Key Parameters/Options:**
|
||||
* `sourceName`: `Name of the tag to copy from.` (CLI: `<sourceName>` positional)
|
||||
* `targetName`: `Name of the new tag to create.` (CLI: `<targetName>` positional)
|
||||
* `--description <text>`: `Optional description for the new tag.` (CLI: `-d, --description <text>`)
|
||||
|
||||
---
|
||||
|
||||
## Miscellaneous
|
||||
|
||||
### 32. Sync Readme (`sync-readme`) -- experimental
|
||||
|
||||
* **MCP Tool:** N/A
|
||||
* **CLI Command:** `task-master sync-readme [options]`
|
||||
* **Description:** `Exports your task list to your project's README.md file, useful for showcasing progress.`
|
||||
* **Key Parameters/Options:**
|
||||
* `status`: `Filter tasks by status (e.g., 'pending', 'done').` (CLI: `-s, --status <status>`)
|
||||
* `withSubtasks`: `Include subtasks in the export.` (CLI: `--with-subtasks`)
|
||||
* `tag`: `Specify which tag context to export from. Defaults to the current active tag.` (CLI: `--tag <name>`)
|
||||
|
||||
---
|
||||
|
||||
## Environment Variables Configuration (Updated)
|
||||
|
||||
Taskmaster primarily uses the **`.taskmaster/config.json`** file (in project root) for configuration (models, parameters, logging level, etc.), managed via `task-master models --setup`.
|
||||
|
||||
Environment variables are used **only** for sensitive API keys related to AI providers and specific overrides like the Ollama base URL:
|
||||
|
||||
* **API Keys (Required for corresponding provider):**
|
||||
* `ANTHROPIC_API_KEY`
|
||||
* `PERPLEXITY_API_KEY`
|
||||
* `OPENAI_API_KEY`
|
||||
* `GOOGLE_API_KEY`
|
||||
* `MISTRAL_API_KEY`
|
||||
* `AZURE_OPENAI_API_KEY` (Requires `AZURE_OPENAI_ENDPOINT` too)
|
||||
* `OPENROUTER_API_KEY`
|
||||
* `XAI_API_KEY`
|
||||
* `OLLAMA_API_KEY` (Requires `OLLAMA_BASE_URL` too)
|
||||
* **Endpoints (Optional/Provider Specific inside .taskmaster/config.json):**
|
||||
* `AZURE_OPENAI_ENDPOINT`
|
||||
* `OLLAMA_BASE_URL` (Default: `http://localhost:11434/api`)
|
||||
|
||||
**Set API keys** in your **`.env`** file in the project root (for CLI use) or within the `env` section of your **`.cursor/mcp.json`** file (for MCP/Cursor integration). All other settings (model choice, max tokens, temperature, log level, custom endpoints) are managed in `.taskmaster/config.json` via `task-master models` command or `models` MCP tool.
|
||||
|
||||
---
|
||||
|
||||
For details on how these commands fit into the development process, see the [Development Workflow Guide](mdc:.cursor/rules/dev_workflow.mdc).
|
||||
@@ -373,4 +373,8 @@ if (process.argv.length <= 2) {
|
||||
}
|
||||
|
||||
// Add exports at the end of the file
|
||||
export { detectCamelCaseFlags };
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = {
|
||||
detectCamelCaseFlags
|
||||
};
|
||||
}
|
||||
|
||||
@@ -302,60 +302,8 @@ task-master delete-tag <tag-name> --yes
|
||||
```bash
|
||||
# Initialize a new project with Task Master structure
|
||||
task-master init
|
||||
|
||||
# Initialize a new project applying specific rules
|
||||
task-master init --rules cursor,windsurf,vscode
|
||||
```
|
||||
|
||||
- The `--rules` flag allows you to specify one or more rule profiles (e.g., `cursor`, `roo`, `windsurf`, `cline`) to apply during initialization.
|
||||
- If omitted, all available rule profiles are installed by default (claude, cline, codex, cursor, roo, trae, vscode, windsurf).
|
||||
- You can use multiple comma-separated profiles in a single command.
|
||||
|
||||
## Manage Rules
|
||||
|
||||
```bash
|
||||
# Add rule profiles to your project
|
||||
# (e.g., .roo/rules, .windsurf/rules)
|
||||
task-master rules add <profile1,profile2,...>
|
||||
|
||||
# Remove rule sets from your project
|
||||
task-master rules remove <profile1,profile2,...>
|
||||
|
||||
# Remove rule sets bypassing safety check (dangerous)
|
||||
task-master rules remove <profile1,profile2,...> --force
|
||||
|
||||
# Launch interactive rules setup to select rules
|
||||
# (does not re-initialize project or ask about shell aliases)
|
||||
task-master rules setup
|
||||
```
|
||||
|
||||
- Adding rules creates the profile and rules directory (e.g., `.roo/rules`) and copies/initializes the rules.
|
||||
- Removing rules deletes the profile and rules directory and associated MCP config.
|
||||
- **Safety Check**: Attempting to remove rule profiles will trigger a critical warning requiring confirmation. Use `--force` to bypass.
|
||||
- You can use multiple comma-separated rules in a single command.
|
||||
- The `setup` action launches an interactive prompt to select which rules to apply. The list of rules is always current with the available profiles, and no manual updates are needed. This command does **not** re-initialize your project or affect shell aliases; it only manages rules interactively.
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
task-master rules add windsurf,roo,vscode
|
||||
task-master rules remove windsurf
|
||||
task-master rules setup
|
||||
```
|
||||
|
||||
### Interactive Rules Setup
|
||||
|
||||
You can launch the interactive rules setup at any time with:
|
||||
|
||||
```bash
|
||||
task-master rules setup
|
||||
```
|
||||
|
||||
This command opens a prompt where you can select which rule profiles (e.g., Cursor, Roo, Windsurf) you want to add to your project. This does **not** re-initialize your project or ask about shell aliases; it only manages rules.
|
||||
|
||||
- Use this command to add rule profiles interactively after project creation.
|
||||
- The same interactive prompt is also used during `init` if you don't specify rules with `--rules`.
|
||||
|
||||
## Configure AI Models
|
||||
|
||||
```bash
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
Taskmaster uses two primary methods for configuration:
|
||||
|
||||
1. **`.taskmaster/config.json` File (Recommended - New Structure)**
|
||||
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.
|
||||
|
||||
@@ -64,7 +64,7 @@ To manually verify that the Roo files are properly included in the package:
|
||||
ls -la .roo/rules
|
||||
ls -la .roo/rules-architect
|
||||
ls -la .roo/rules-ask
|
||||
ls -la .roo/rules-orchestrator
|
||||
ls -la .roo/rules-boomerang
|
||||
ls -la .roo/rules-code
|
||||
ls -la .roo/rules-debug
|
||||
ls -la .roo/rules-test
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Available Models as of June 20, 2025
|
||||
# Available Models as of June 13, 2025
|
||||
|
||||
## Main Models
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
| openai | gpt-4o-mini | 0.3 | 0.15 | 0.6 |
|
||||
| google | gemini-2.5-pro-preview-05-06 | 0.638 | — | — |
|
||||
| google | gemini-2.5-pro-preview-03-25 | 0.638 | — | — |
|
||||
| google | gemini-2.5-flash-preview-04-17 | 0.604 | — | — |
|
||||
| google | gemini-2.0-flash | 0.518 | 0.15 | 0.6 |
|
||||
| google | gemini-2.5-flash-preview-04-17 | — | — | — |
|
||||
| google | gemini-2.0-flash | 0.754 | 0.15 | 0.6 |
|
||||
| google | gemini-2.0-flash-lite | — | — | — |
|
||||
| perplexity | sonar-pro | — | 3 | 15 |
|
||||
| perplexity | sonar-reasoning-pro | 0.211 | 2 | 8 |
|
||||
@@ -89,8 +89,8 @@
|
||||
| openai | o4-mini | 0.45 | 1.1 | 4.4 |
|
||||
| google | gemini-2.5-pro-preview-05-06 | 0.638 | — | — |
|
||||
| google | gemini-2.5-pro-preview-03-25 | 0.638 | — | — |
|
||||
| google | gemini-2.5-flash-preview-04-17 | 0.604 | — | — |
|
||||
| google | gemini-2.0-flash | 0.518 | 0.15 | 0.6 |
|
||||
| google | gemini-2.5-flash-preview-04-17 | — | — | — |
|
||||
| google | gemini-2.0-flash | 0.754 | 0.15 | 0.6 |
|
||||
| google | gemini-2.0-flash-lite | — | — | — |
|
||||
| perplexity | sonar-reasoning-pro | 0.211 | 2 | 8 |
|
||||
| perplexity | sonar-reasoning | 0.211 | 1 | 5 |
|
||||
|
||||
@@ -5,11 +5,9 @@
|
||||
import { clearSubtasks } from '../../../../scripts/modules/task-manager.js';
|
||||
import {
|
||||
enableSilentMode,
|
||||
disableSilentMode,
|
||||
readJSON
|
||||
disableSilentMode
|
||||
} from '../../../../scripts/modules/utils.js';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
/**
|
||||
* Clear subtasks from specified tasks
|
||||
@@ -17,13 +15,12 @@ import path from 'path';
|
||||
* @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
|
||||
* @param {string} [args.id] - Task IDs (comma-separated) to clear subtasks from
|
||||
* @param {boolean} [args.all] - Clear subtasks from all tasks
|
||||
* @param {string} [args.tag] - Tag context to operate on (defaults to current active tag)
|
||||
* @param {Object} log - Logger object
|
||||
* @returns {Promise<{success: boolean, data?: Object, error?: {code: string, message: string}}>}
|
||||
*/
|
||||
export async function clearSubtasksDirect(args, log) {
|
||||
// Destructure expected args
|
||||
const { tasksJsonPath, id, all, tag, projectRoot } = args;
|
||||
const { tasksJsonPath, id, all } = args;
|
||||
try {
|
||||
log.info(`Clearing subtasks with args: ${JSON.stringify(args)}`);
|
||||
|
||||
@@ -67,70 +64,52 @@ export async function clearSubtasksDirect(args, log) {
|
||||
|
||||
let taskIds;
|
||||
|
||||
// Use readJSON which handles silent migration and tag resolution
|
||||
const data = readJSON(tasksPath, projectRoot, tag);
|
||||
|
||||
if (!data || !data.tasks) {
|
||||
return {
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INPUT_VALIDATION_ERROR',
|
||||
message: `No tasks found in tasks file: ${tasksPath}`
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const currentTag = data.tag || 'master';
|
||||
const tasks = data.tasks;
|
||||
|
||||
// If all is specified, get all task IDs
|
||||
if (all) {
|
||||
log.info(`Clearing subtasks from all tasks in tag '${currentTag}'`);
|
||||
if (tasks.length === 0) {
|
||||
log.info('Clearing subtasks from all tasks');
|
||||
const data = JSON.parse(fs.readFileSync(tasksPath, 'utf8'));
|
||||
if (!data || !data.tasks || data.tasks.length === 0) {
|
||||
return {
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INPUT_VALIDATION_ERROR',
|
||||
message: `No tasks found in tag context '${currentTag}'`
|
||||
message: 'No valid tasks found in the tasks file'
|
||||
}
|
||||
};
|
||||
}
|
||||
taskIds = tasks.map((t) => t.id).join(',');
|
||||
taskIds = data.tasks.map((t) => t.id).join(',');
|
||||
} else {
|
||||
// Use the provided task IDs
|
||||
taskIds = id;
|
||||
}
|
||||
|
||||
log.info(`Clearing subtasks from tasks: ${taskIds} in tag '${currentTag}'`);
|
||||
log.info(`Clearing subtasks from tasks: ${taskIds}`);
|
||||
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Call the core function
|
||||
clearSubtasks(tasksPath, taskIds, { projectRoot, tag: currentTag });
|
||||
clearSubtasks(tasksPath, taskIds);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
// Read the updated data to provide a summary
|
||||
const updatedData = readJSON(tasksPath, projectRoot, currentTag);
|
||||
const updatedData = JSON.parse(fs.readFileSync(tasksPath, 'utf8'));
|
||||
const taskIdArray = taskIds.split(',').map((id) => parseInt(id.trim(), 10));
|
||||
|
||||
// Build a summary of what was done
|
||||
const clearedTasksCount = taskIdArray.length;
|
||||
const updatedTasks = updatedData.tasks || [];
|
||||
|
||||
const taskSummary = taskIdArray.map((id) => {
|
||||
const task = updatedTasks.find((t) => t.id === id);
|
||||
const task = updatedData.tasks.find((t) => t.id === id);
|
||||
return task ? { id, title: task.title } : { id, title: 'Task not found' };
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
message: `Successfully cleared subtasks from ${clearedTasksCount} task(s) in tag '${currentTag}'`,
|
||||
tasksCleared: taskSummary,
|
||||
tag: currentTag
|
||||
message: `Successfully cleared subtasks from ${clearedTasksCount} task(s)`,
|
||||
tasksCleared: taskSummary
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
|
||||
@@ -5,13 +5,11 @@ import {
|
||||
// isSilentMode // Not used directly here
|
||||
} from '../../../../scripts/modules/utils.js';
|
||||
import os from 'os'; // Import os module for home directory check
|
||||
import { RULE_PROFILES } from '../../../../src/constants/profiles.js';
|
||||
import { convertAllRulesToProfileRules } from '../../../../src/utils/rule-transformer.js';
|
||||
|
||||
/**
|
||||
* Direct function wrapper for initializing a project.
|
||||
* Derives target directory from session, sets CWD, and calls core init logic.
|
||||
* @param {object} args - Arguments containing initialization options (addAliases, skipInstall, yes, projectRoot, rules)
|
||||
* @param {object} args - Arguments containing initialization options (addAliases, skipInstall, yes, projectRoot)
|
||||
* @param {object} log - The FastMCP logger instance.
|
||||
* @param {object} context - The context object, must contain { session }.
|
||||
* @returns {Promise<{success: boolean, data?: any, error?: {code: string, message: string}}>} - Standard result object.
|
||||
@@ -70,17 +68,6 @@ export async function initializeProjectDirect(args, log, context = {}) {
|
||||
yes: true // Force yes mode
|
||||
};
|
||||
|
||||
// Handle rules option just like CLI
|
||||
if (Array.isArray(args.rules) && args.rules.length > 0) {
|
||||
options.rules = args.rules;
|
||||
log.info(`Including rules: ${args.rules.join(', ')}`);
|
||||
} else {
|
||||
options.rules = RULE_PROFILES;
|
||||
log.info(
|
||||
`No rule profiles specified, defaulting to: ${RULE_PROFILES.join(', ')}`
|
||||
);
|
||||
}
|
||||
|
||||
log.info(`Initializing project with options: ${JSON.stringify(options)}`);
|
||||
const result = await initializeProject(options); // Call core logic
|
||||
|
||||
|
||||
@@ -71,11 +71,7 @@ export async function moveTaskDirect(args, log, context = {}) {
|
||||
tasksPath,
|
||||
args.sourceId,
|
||||
args.destinationId,
|
||||
generateFiles,
|
||||
{
|
||||
projectRoot: args.projectRoot,
|
||||
tag: args.tag
|
||||
}
|
||||
generateFiles
|
||||
);
|
||||
|
||||
// Restore console output
|
||||
|
||||
@@ -1,210 +0,0 @@
|
||||
/**
|
||||
* rules.js
|
||||
* Direct function implementation for adding or removing rules
|
||||
*/
|
||||
|
||||
import {
|
||||
enableSilentMode,
|
||||
disableSilentMode
|
||||
} from '../../../../scripts/modules/utils.js';
|
||||
import {
|
||||
convertAllRulesToProfileRules,
|
||||
removeProfileRules,
|
||||
getRulesProfile,
|
||||
isValidProfile
|
||||
} from '../../../../src/utils/rule-transformer.js';
|
||||
import { RULE_PROFILES } from '../../../../src/constants/profiles.js';
|
||||
import { RULES_ACTIONS } from '../../../../src/constants/rules-actions.js';
|
||||
import {
|
||||
wouldRemovalLeaveNoProfiles,
|
||||
getInstalledProfiles
|
||||
} from '../../../../src/utils/profiles.js';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
/**
|
||||
* Direct function wrapper for adding or removing rules.
|
||||
* @param {Object} args - Command arguments
|
||||
* @param {"add"|"remove"} args.action - Action to perform: add or remove rules
|
||||
* @param {string[]} args.profiles - List of profiles to add or remove
|
||||
* @param {string} args.projectRoot - Absolute path to the project root
|
||||
* @param {boolean} [args.yes=true] - Run non-interactively
|
||||
* @param {Object} log - Logger object
|
||||
* @param {Object} context - Additional context (session)
|
||||
* @returns {Promise<Object>} - Result object { success: boolean, data?: any, error?: { code: string, message: string } }
|
||||
*/
|
||||
export async function rulesDirect(args, log, context = {}) {
|
||||
enableSilentMode();
|
||||
try {
|
||||
const { action, profiles, projectRoot, yes, force } = args;
|
||||
if (
|
||||
!action ||
|
||||
!Array.isArray(profiles) ||
|
||||
profiles.length === 0 ||
|
||||
!projectRoot
|
||||
) {
|
||||
return {
|
||||
success: false,
|
||||
error: {
|
||||
code: 'MISSING_ARGUMENT',
|
||||
message: 'action, profiles, and projectRoot are required.'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const removalResults = [];
|
||||
const addResults = [];
|
||||
|
||||
if (action === RULES_ACTIONS.REMOVE) {
|
||||
// Safety check: Ensure this won't remove all rule profiles (unless forced)
|
||||
if (!force && wouldRemovalLeaveNoProfiles(projectRoot, profiles)) {
|
||||
const installedProfiles = getInstalledProfiles(projectRoot);
|
||||
const remainingProfiles = installedProfiles.filter(
|
||||
(profile) => !profiles.includes(profile)
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
error: {
|
||||
code: 'CRITICAL_REMOVAL_BLOCKED',
|
||||
message: `CRITICAL: This operation would remove ALL remaining rule profiles (${profiles.join(', ')}), leaving your project with no rules configurations. This could significantly impact functionality. Currently installed profiles: ${installedProfiles.join(', ')}. If you're certain you want to proceed, set force: true or use the CLI with --force flag.`
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
for (const profile of profiles) {
|
||||
if (!isValidProfile(profile)) {
|
||||
removalResults.push({
|
||||
profileName: profile,
|
||||
success: false,
|
||||
error: `The requested rule profile for '${profile}' is unavailable. Supported profiles are: ${RULE_PROFILES.join(', ')}.`
|
||||
});
|
||||
continue;
|
||||
}
|
||||
const profileConfig = getRulesProfile(profile);
|
||||
const result = removeProfileRules(projectRoot, profileConfig);
|
||||
removalResults.push(result);
|
||||
}
|
||||
const successes = removalResults
|
||||
.filter((r) => r.success)
|
||||
.map((r) => r.profileName);
|
||||
const skipped = removalResults
|
||||
.filter((r) => r.skipped)
|
||||
.map((r) => r.profileName);
|
||||
const errors = removalResults.filter(
|
||||
(r) => r.error && !r.success && !r.skipped
|
||||
);
|
||||
const withNotices = removalResults.filter((r) => r.notice);
|
||||
|
||||
let summary = '';
|
||||
if (successes.length > 0) {
|
||||
summary += `Successfully removed Task Master rules: ${successes.join(', ')}.`;
|
||||
}
|
||||
if (skipped.length > 0) {
|
||||
summary += `Skipped (default or protected): ${skipped.join(', ')}.`;
|
||||
}
|
||||
if (errors.length > 0) {
|
||||
summary += errors
|
||||
.map((r) => `Error removing ${r.profileName}: ${r.error}`)
|
||||
.join(' ');
|
||||
}
|
||||
if (withNotices.length > 0) {
|
||||
summary += ` Notices: ${withNotices.map((r) => `${r.profileName} - ${r.notice}`).join('; ')}.`;
|
||||
}
|
||||
disableSilentMode();
|
||||
return {
|
||||
success: errors.length === 0,
|
||||
data: { summary, results: removalResults }
|
||||
};
|
||||
} else if (action === RULES_ACTIONS.ADD) {
|
||||
for (const profile of profiles) {
|
||||
if (!isValidProfile(profile)) {
|
||||
addResults.push({
|
||||
profileName: profile,
|
||||
success: false,
|
||||
error: `Profile not found: static import missing for '${profile}'. Valid profiles: ${RULE_PROFILES.join(', ')}`
|
||||
});
|
||||
continue;
|
||||
}
|
||||
const profileConfig = getRulesProfile(profile);
|
||||
const { success, failed } = convertAllRulesToProfileRules(
|
||||
projectRoot,
|
||||
profileConfig
|
||||
);
|
||||
|
||||
// Determine paths
|
||||
const rulesDir = profileConfig.rulesDir;
|
||||
const profileRulesDir = path.join(projectRoot, rulesDir);
|
||||
const profileDir = profileConfig.profileDir;
|
||||
const mcpConfig = profileConfig.mcpConfig !== false;
|
||||
const mcpPath =
|
||||
mcpConfig && profileConfig.mcpConfigPath
|
||||
? path.join(projectRoot, profileConfig.mcpConfigPath)
|
||||
: null;
|
||||
|
||||
// Check what was created
|
||||
const mcpConfigCreated =
|
||||
mcpConfig && mcpPath ? fs.existsSync(mcpPath) : undefined;
|
||||
const rulesDirCreated = fs.existsSync(profileRulesDir);
|
||||
const profileFolderCreated = fs.existsSync(
|
||||
path.join(projectRoot, profileDir)
|
||||
);
|
||||
|
||||
const error =
|
||||
failed > 0 ? `${failed} rule files failed to convert.` : null;
|
||||
const resultObj = {
|
||||
profileName: profile,
|
||||
mcpConfigCreated,
|
||||
rulesDirCreated,
|
||||
profileFolderCreated,
|
||||
skipped: false,
|
||||
error,
|
||||
success:
|
||||
(mcpConfig ? mcpConfigCreated : true) &&
|
||||
rulesDirCreated &&
|
||||
success > 0 &&
|
||||
!error
|
||||
};
|
||||
addResults.push(resultObj);
|
||||
}
|
||||
|
||||
const successes = addResults
|
||||
.filter((r) => r.success)
|
||||
.map((r) => r.profileName);
|
||||
const errors = addResults.filter((r) => r.error && !r.success);
|
||||
|
||||
let summary = '';
|
||||
if (successes.length > 0) {
|
||||
summary += `Successfully added rules: ${successes.join(', ')}.`;
|
||||
}
|
||||
if (errors.length > 0) {
|
||||
summary += errors
|
||||
.map((r) => ` Error adding ${r.profileName}: ${r.error}`)
|
||||
.join(' ');
|
||||
}
|
||||
disableSilentMode();
|
||||
return {
|
||||
success: errors.length === 0,
|
||||
data: { summary, results: addResults }
|
||||
};
|
||||
} else {
|
||||
disableSilentMode();
|
||||
return {
|
||||
success: false,
|
||||
error: {
|
||||
code: 'INVALID_ACTION',
|
||||
message: `Unknown action. Use "${RULES_ACTIONS.ADD}" or "${RULES_ACTIONS.REMOVE}".`
|
||||
}
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
disableSilentMode();
|
||||
log.error(`[rulesDirect] Error: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
error: {
|
||||
code: error.code || 'RULES_ERROR',
|
||||
message: error.message
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -35,8 +35,7 @@ export function registerClearSubtasksTool(server) {
|
||||
),
|
||||
projectRoot: z
|
||||
.string()
|
||||
.describe('The directory of the project. Must be an absolute path.'),
|
||||
tag: z.string().optional().describe('Tag context to operate on')
|
||||
.describe('The directory of the project. Must be an absolute path.')
|
||||
})
|
||||
.refine((data) => data.id || data.all, {
|
||||
message: "Either 'id' or 'all' parameter must be provided",
|
||||
@@ -65,8 +64,7 @@ export function registerClearSubtasksTool(server) {
|
||||
tasksJsonPath: tasksJsonPath,
|
||||
id: args.id,
|
||||
all: args.all,
|
||||
projectRoot: args.projectRoot,
|
||||
tag: args.tag || 'master'
|
||||
projectRoot: args.projectRoot
|
||||
},
|
||||
log,
|
||||
{ session }
|
||||
|
||||
@@ -36,7 +36,6 @@ import { registerUseTagTool } from './use-tag.js';
|
||||
import { registerRenameTagTool } from './rename-tag.js';
|
||||
import { registerCopyTagTool } from './copy-tag.js';
|
||||
import { registerResearchTool } from './research.js';
|
||||
import { registerRulesTool } from './rules.js';
|
||||
|
||||
/**
|
||||
* Register all Task Master tools with the MCP server
|
||||
@@ -49,7 +48,6 @@ export function registerTaskMasterTools(server) {
|
||||
// Group 1: Initialization & Setup
|
||||
registerInitializeProjectTool(server);
|
||||
registerModelsTool(server);
|
||||
registerRulesTool(server);
|
||||
registerParsePRDTool(server);
|
||||
|
||||
// Group 2: Task Analysis & Expansion
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
withNormalizedProjectRoot
|
||||
} from './utils.js';
|
||||
import { initializeProjectDirect } from '../core/task-master-core.js';
|
||||
import { RULE_PROFILES } from '../../../src/constants/profiles.js';
|
||||
|
||||
export function registerInitializeProjectTool(server) {
|
||||
server.addTool({
|
||||
@@ -36,12 +35,6 @@ export function registerInitializeProjectTool(server) {
|
||||
.string()
|
||||
.describe(
|
||||
'The root directory for the project. ALWAYS SET THIS TO THE PROJECT ROOT DIRECTORY. IF NOT SET, THE TOOL WILL NOT WORK.'
|
||||
),
|
||||
rules: z
|
||||
.array(z.enum(RULE_PROFILES))
|
||||
.optional()
|
||||
.describe(
|
||||
`List of rule profiles to include at initialization. If omitted, defaults to all available profiles. Available options: ${RULE_PROFILES.join(', ')}`
|
||||
)
|
||||
}),
|
||||
execute: withNormalizedProjectRoot(async (args, context) => {
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
/**
|
||||
* tools/rules.js
|
||||
* Tool to add or remove rules from a project (MCP server)
|
||||
*/
|
||||
|
||||
import { z } from 'zod';
|
||||
import {
|
||||
createErrorResponse,
|
||||
handleApiResult,
|
||||
withNormalizedProjectRoot
|
||||
} from './utils.js';
|
||||
import { rulesDirect } from '../core/direct-functions/rules.js';
|
||||
import { RULE_PROFILES } from '../../../src/constants/profiles.js';
|
||||
|
||||
/**
|
||||
* Register the rules tool with the MCP server
|
||||
* @param {Object} server - FastMCP server instance
|
||||
*/
|
||||
export function registerRulesTool(server) {
|
||||
server.addTool({
|
||||
name: 'rules',
|
||||
description: 'Add or remove rule profiles from the project.',
|
||||
parameters: z.object({
|
||||
action: z
|
||||
.enum(['add', 'remove'])
|
||||
.describe('Whether to add or remove rule profiles.'),
|
||||
profiles: z
|
||||
.array(z.enum(RULE_PROFILES))
|
||||
.min(1)
|
||||
.describe(
|
||||
`List of rule profiles to add or remove (e.g., [\"cursor\", \"roo\"]). Available options: ${RULE_PROFILES.join(', ')}`
|
||||
),
|
||||
projectRoot: z
|
||||
.string()
|
||||
.describe(
|
||||
'The root directory of the project. Must be an absolute path.'
|
||||
),
|
||||
force: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.default(false)
|
||||
.describe(
|
||||
'DANGEROUS: Force removal even if it would leave no rule profiles. Only use if you are absolutely certain.'
|
||||
)
|
||||
}),
|
||||
execute: withNormalizedProjectRoot(async (args, { log, session }) => {
|
||||
try {
|
||||
log.info(
|
||||
`[rules tool] Executing action: ${args.action} for profiles: ${args.profiles.join(', ')} in ${args.projectRoot}`
|
||||
);
|
||||
const result = await rulesDirect(args, log, { session });
|
||||
return handleApiResult(result, log);
|
||||
} catch (error) {
|
||||
log.error(`[rules tool] Error: ${error.message}`);
|
||||
return createErrorResponse(error.message, { details: error.stack });
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
92
package-lock.json
generated
92
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "task-master-ai",
|
||||
"version": "0.17.0",
|
||||
"version": "0.17.0-rc.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "task-master-ai",
|
||||
"version": "0.17.0",
|
||||
"version": "0.17.0-rc.1",
|
||||
"license": "MIT WITH Commons-Clause",
|
||||
"dependencies": {
|
||||
"@ai-sdk/amazon-bedrock": "^2.2.9",
|
||||
@@ -20,7 +20,6 @@
|
||||
"@ai-sdk/xai": "^1.2.15",
|
||||
"@anthropic-ai/sdk": "^0.39.0",
|
||||
"@aws-sdk/credential-providers": "^3.817.0",
|
||||
"@inquirer/search": "^3.0.15",
|
||||
"@openrouter/ai-sdk-provider": "^0.4.5",
|
||||
"ai": "^4.3.10",
|
||||
"boxen": "^8.0.1",
|
||||
@@ -43,6 +42,7 @@
|
||||
"ollama-ai-provider": "^1.2.0",
|
||||
"openai": "^4.89.0",
|
||||
"ora": "^8.2.0",
|
||||
"task-master-ai": "0.17.0-rc.1",
|
||||
"uuid": "^11.1.0",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
@@ -2697,13 +2697,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/core": {
|
||||
"version": "10.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.13.tgz",
|
||||
"integrity": "sha512-1viSxebkYN2nJULlzCxES6G9/stgHSepZ9LqqfdIGPHj5OHhiBUXVS0a6R0bEC2A+VL4D9w6QB66ebCr6HGllA==",
|
||||
"version": "10.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.9.tgz",
|
||||
"integrity": "sha512-sXhVB8n20NYkUBfDYgizGHlpRVaCRjtuzNZA6xpALIUbkgfd2Hjz+DfEN6+h1BRnuxw0/P4jCIMjMsEOAMwAJw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@inquirer/figures": "^1.0.12",
|
||||
"@inquirer/type": "^3.0.7",
|
||||
"@inquirer/figures": "^1.0.11",
|
||||
"@inquirer/type": "^3.0.5",
|
||||
"ansi-escapes": "^4.3.2",
|
||||
"cli-width": "^4.1.0",
|
||||
"mute-stream": "^2.0.0",
|
||||
@@ -2823,9 +2823,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/figures": {
|
||||
"version": "1.0.12",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.12.tgz",
|
||||
"integrity": "sha512-MJttijd8rMFcKJC8NYmprWr6hD3r9Gd9qUC0XwPNwoEPWSMVJwA2MlXxF+nhZZNMY+HXsWa+o7KY2emWYIn0jQ==",
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.11.tgz",
|
||||
"integrity": "sha512-eOg92lvrn/aRUqbxRyvpEWnrvRuTYRifixHkYVpJiygTgVSBIHDqLh0SrMQXkafvULg3ck11V7xvR+zcgvpHFw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
@@ -2947,14 +2947,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/search": {
|
||||
"version": "3.0.15",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.15.tgz",
|
||||
"integrity": "sha512-YBMwPxYBrADqyvP4nNItpwkBnGGglAvCLVW8u4pRmmvOsHUtCAUIMbUrLX5B3tFL1/WsLGdQ2HNzkqswMs5Uaw==",
|
||||
"version": "3.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.11.tgz",
|
||||
"integrity": "sha512-9CWQT0ikYcg6Ls3TOa7jljsD7PgjcsYEM0bYE+Gkz+uoW9u8eaJCRHJKkucpRE5+xKtaaDbrND+nPDoxzjYyew==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@inquirer/core": "^10.1.13",
|
||||
"@inquirer/figures": "^1.0.12",
|
||||
"@inquirer/type": "^3.0.7",
|
||||
"@inquirer/core": "^10.1.9",
|
||||
"@inquirer/figures": "^1.0.11",
|
||||
"@inquirer/type": "^3.0.5",
|
||||
"yoctocolors-cjs": "^2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
@@ -2994,9 +2994,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/type": {
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.7.tgz",
|
||||
"integrity": "sha512-PfunHQcjwnju84L+ycmcMKB/pTPIngjUJvfnRhKY6FKPuYXlM4aQCb/nIdTFR6BEhMjFvngzvng/vBAJMZpLSA==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.5.tgz",
|
||||
"integrity": "sha512-ZJpeIYYueOz/i/ONzrfof8g89kNdO2hjGuvULROo3O8rlB2CRtSseE5KeirnyE4t/thAn/EwvS/vuQeJCn+NZg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
@@ -11370,6 +11370,58 @@
|
||||
"react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/task-master-ai": {
|
||||
"version": "0.17.0-rc.1",
|
||||
"resolved": "https://registry.npmjs.org/task-master-ai/-/task-master-ai-0.17.0-rc.1.tgz",
|
||||
"integrity": "sha512-4V70dlqedWj/i5RHXyKeqm2IFwNnFYB0Ndq4S8Mc9wghpENkAhXE1qVg27NscP1n+TrEu1HtgXVOfCMDdgvzag==",
|
||||
"license": "MIT WITH Commons-Clause",
|
||||
"dependencies": {
|
||||
"@ai-sdk/amazon-bedrock": "^2.2.9",
|
||||
"@ai-sdk/anthropic": "^1.2.10",
|
||||
"@ai-sdk/azure": "^1.3.17",
|
||||
"@ai-sdk/google": "^1.2.13",
|
||||
"@ai-sdk/google-vertex": "^2.2.23",
|
||||
"@ai-sdk/mistral": "^1.2.7",
|
||||
"@ai-sdk/openai": "^1.3.20",
|
||||
"@ai-sdk/perplexity": "^1.1.7",
|
||||
"@ai-sdk/xai": "^1.2.15",
|
||||
"@anthropic-ai/sdk": "^0.39.0",
|
||||
"@aws-sdk/credential-providers": "^3.817.0",
|
||||
"@openrouter/ai-sdk-provider": "^0.4.5",
|
||||
"ai": "^4.3.10",
|
||||
"boxen": "^8.0.1",
|
||||
"chalk": "^5.4.1",
|
||||
"cli-highlight": "^2.1.11",
|
||||
"cli-table3": "^0.6.5",
|
||||
"commander": "^11.1.0",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.3.1",
|
||||
"express": "^4.21.2",
|
||||
"fastmcp": "^2.2.2",
|
||||
"figlet": "^1.8.0",
|
||||
"fuse.js": "^7.1.0",
|
||||
"gpt-tokens": "^1.3.14",
|
||||
"gradient-string": "^3.0.0",
|
||||
"helmet": "^8.1.0",
|
||||
"inquirer": "^12.5.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"lru-cache": "^10.2.0",
|
||||
"ollama-ai-provider": "^1.2.0",
|
||||
"openai": "^4.89.0",
|
||||
"ora": "^8.2.0",
|
||||
"task-master-ai": "0.17.0-rc.1",
|
||||
"uuid": "^11.1.0",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"bin": {
|
||||
"task-master": "bin/task-master.js",
|
||||
"task-master-ai": "mcp-server/server.js",
|
||||
"task-master-mcp": "mcp-server/server.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/term-size": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "task-master-ai",
|
||||
"version": "0.17.0",
|
||||
"version": "0.17.0-rc.1",
|
||||
"description": "A task management system for ambitious AI-driven development that doesn't overwhelm and confuse Cursor.",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
@@ -50,7 +50,6 @@
|
||||
"@ai-sdk/xai": "^1.2.15",
|
||||
"@anthropic-ai/sdk": "^0.39.0",
|
||||
"@aws-sdk/credential-providers": "^3.817.0",
|
||||
"@inquirer/search": "^3.0.15",
|
||||
"@openrouter/ai-sdk-provider": "^0.4.5",
|
||||
"ai": "^4.3.10",
|
||||
"boxen": "^8.0.1",
|
||||
@@ -73,6 +72,7 @@
|
||||
"ollama-ai-provider": "^1.2.0",
|
||||
"openai": "^4.89.0",
|
||||
"ora": "^8.2.0",
|
||||
"task-master-ai": "0.17.0-rc.1",
|
||||
"uuid": "^11.1.0",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
|
||||
324
scripts/init.js
324
scripts/init.js
@@ -23,12 +23,7 @@ import figlet from 'figlet';
|
||||
import boxen from 'boxen';
|
||||
import gradient from 'gradient-string';
|
||||
import { isSilentMode } from './modules/utils.js';
|
||||
import { RULE_PROFILES } from '../src/constants/profiles.js';
|
||||
import {
|
||||
convertAllRulesToProfileRules,
|
||||
getRulesProfile
|
||||
} from '../src/utils/rule-transformer.js';
|
||||
|
||||
import { convertAllCursorRulesToRooRules } from './modules/rule-transformer.js';
|
||||
import { execSync } from 'child_process';
|
||||
import {
|
||||
EXAMPLE_PRD_FILE,
|
||||
@@ -226,9 +221,70 @@ function copyTemplateFile(templateName, targetPath, replacements = {}) {
|
||||
// case 'scripts_README.md':
|
||||
// sourcePath = path.join(__dirname, '..', 'assets', 'scripts_README.md');
|
||||
// break;
|
||||
case 'dev_workflow.mdc':
|
||||
sourcePath = path.join(
|
||||
__dirname,
|
||||
'..',
|
||||
'.cursor',
|
||||
'rules',
|
||||
'dev_workflow.mdc'
|
||||
);
|
||||
break;
|
||||
case 'taskmaster.mdc':
|
||||
sourcePath = path.join(
|
||||
__dirname,
|
||||
'..',
|
||||
'.cursor',
|
||||
'rules',
|
||||
'taskmaster.mdc'
|
||||
);
|
||||
break;
|
||||
case 'cursor_rules.mdc':
|
||||
sourcePath = path.join(
|
||||
__dirname,
|
||||
'..',
|
||||
'.cursor',
|
||||
'rules',
|
||||
'cursor_rules.mdc'
|
||||
);
|
||||
break;
|
||||
case 'self_improve.mdc':
|
||||
sourcePath = path.join(
|
||||
__dirname,
|
||||
'..',
|
||||
'.cursor',
|
||||
'rules',
|
||||
'self_improve.mdc'
|
||||
);
|
||||
break;
|
||||
// case 'README-task-master.md':
|
||||
// sourcePath = path.join(__dirname, '..', 'README-task-master.md');
|
||||
// break;
|
||||
break;
|
||||
case 'windsurfrules':
|
||||
sourcePath = path.join(__dirname, '..', 'assets', '.windsurfrules');
|
||||
break;
|
||||
case '.roomodes':
|
||||
sourcePath = path.join(__dirname, '..', 'assets', 'roocode', '.roomodes');
|
||||
break;
|
||||
case 'architect-rules':
|
||||
case 'ask-rules':
|
||||
case 'boomerang-rules':
|
||||
case 'code-rules':
|
||||
case 'debug-rules':
|
||||
case 'test-rules': {
|
||||
// Extract the mode name from the template name (e.g., 'architect' from 'architect-rules')
|
||||
const mode = templateName.split('-')[0];
|
||||
sourcePath = path.join(
|
||||
__dirname,
|
||||
'..',
|
||||
'assets',
|
||||
'roocode',
|
||||
'.roo',
|
||||
`rules-${mode}`,
|
||||
templateName
|
||||
);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// For other files like env.example, gitignore, etc. that don't have direct equivalents
|
||||
sourcePath = path.join(__dirname, '..', 'assets', templateName);
|
||||
@@ -278,6 +334,21 @@ function copyTemplateFile(templateName, targetPath, replacements = {}) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle .windsurfrules - append the entire content
|
||||
if (filename === '.windsurfrules') {
|
||||
log(
|
||||
'info',
|
||||
`${targetPath} already exists, appending content instead of overwriting...`
|
||||
);
|
||||
const existingContent = fs.readFileSync(targetPath, 'utf8');
|
||||
|
||||
// Add a separator comment before appending our content
|
||||
const updatedContent = `${existingContent.trim()}\n\n# Added by Task Master - Development Workflow Rules\n\n${content}`;
|
||||
fs.writeFileSync(targetPath, updatedContent);
|
||||
log('success', `Updated ${targetPath} with additional rules`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle README.md - offer to preserve or create a different file
|
||||
if (filename === 'README-task-master.md') {
|
||||
log('info', `${targetPath} already exists`);
|
||||
@@ -304,7 +375,7 @@ function copyTemplateFile(templateName, targetPath, replacements = {}) {
|
||||
log('info', `Created file: ${targetPath}`);
|
||||
}
|
||||
|
||||
// Main function to initialize a new project
|
||||
// Main function to initialize a new project (No longer needs isInteractive logic)
|
||||
async function initializeProject(options = {}) {
|
||||
// Receives options as argument
|
||||
// Only display banner if not in silent mode
|
||||
@@ -326,11 +397,6 @@ async function initializeProject(options = {}) {
|
||||
// console.log('Skip prompts determined:', skipPrompts);
|
||||
// }
|
||||
|
||||
const selectedRuleProfiles =
|
||||
options.rules && Array.isArray(options.rules) && options.rules.length > 0
|
||||
? options.rules
|
||||
: RULE_PROFILES; // Default to all profiles
|
||||
|
||||
if (skipPrompts) {
|
||||
if (!isSilentMode()) {
|
||||
console.log('SKIPPING PROMPTS - Using defaults or provided values');
|
||||
@@ -357,16 +423,16 @@ async function initializeProject(options = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
createProjectStructure(addAliases, dryRun, options, selectedRuleProfiles);
|
||||
createProjectStructure(addAliases, dryRun, options);
|
||||
} else {
|
||||
// Interactive logic
|
||||
log('info', 'Required options not provided, proceeding with prompts.');
|
||||
|
||||
try {
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
try {
|
||||
// Only prompt for shell aliases
|
||||
const addAliasesInput = await promptQuestion(
|
||||
rl,
|
||||
@@ -390,32 +456,14 @@ async function initializeProject(options = {}) {
|
||||
chalk.yellow('\nDo you want to continue with these settings? (Y/n): ')
|
||||
);
|
||||
const shouldContinue = confirmInput.trim().toLowerCase() !== 'n';
|
||||
rl.close();
|
||||
|
||||
if (!shouldContinue) {
|
||||
rl.close();
|
||||
log('info', 'Project initialization cancelled by user');
|
||||
process.exit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Only run interactive rules if rules flag not provided via command line
|
||||
if (options.rulesExplicitlyProvided) {
|
||||
log(
|
||||
'info',
|
||||
`Using rule profiles provided via command line: ${selectedRuleProfiles.join(', ')}`
|
||||
);
|
||||
} else {
|
||||
try {
|
||||
const targetDir = process.cwd();
|
||||
execSync('npx task-master rules setup', {
|
||||
stdio: 'inherit',
|
||||
cwd: targetDir
|
||||
});
|
||||
} catch (error) {
|
||||
log('error', 'Failed to run interactive rules setup:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
const dryRun = options.dryRun || false;
|
||||
|
||||
if (dryRun) {
|
||||
@@ -431,13 +479,7 @@ async function initializeProject(options = {}) {
|
||||
}
|
||||
|
||||
// Create structure using only necessary values
|
||||
createProjectStructure(
|
||||
addAliasesPrompted,
|
||||
dryRun,
|
||||
options,
|
||||
selectedRuleProfiles
|
||||
);
|
||||
rl.close();
|
||||
createProjectStructure(addAliasesPrompted, dryRun, options);
|
||||
} catch (error) {
|
||||
rl.close();
|
||||
log('error', `Error during initialization process: ${error.message}`);
|
||||
@@ -456,15 +498,23 @@ function promptQuestion(rl, question) {
|
||||
}
|
||||
|
||||
// Function to create the project structure
|
||||
function createProjectStructure(
|
||||
addAliases,
|
||||
dryRun,
|
||||
options,
|
||||
selectedRuleProfiles = RULE_PROFILES // Default to all rule profiles
|
||||
) {
|
||||
function createProjectStructure(addAliases, dryRun, options) {
|
||||
const targetDir = process.cwd();
|
||||
log('info', `Initializing project in ${targetDir}`);
|
||||
|
||||
// Define Roo modes locally (external integration, not part of core Task Master)
|
||||
const ROO_MODES = ['architect', 'ask', 'boomerang', 'code', 'debug', 'test'];
|
||||
|
||||
// Create directories
|
||||
ensureDirectoryExists(path.join(targetDir, '.cursor/rules'));
|
||||
|
||||
// Create Roo directories
|
||||
ensureDirectoryExists(path.join(targetDir, '.roo'));
|
||||
ensureDirectoryExists(path.join(targetDir, '.roo/rules'));
|
||||
for (const mode of ROO_MODES) {
|
||||
ensureDirectoryExists(path.join(targetDir, '.roo', `rules-${mode}`));
|
||||
}
|
||||
|
||||
// Create NEW .taskmaster directory structure (using constants)
|
||||
ensureDirectoryExists(path.join(targetDir, TASKMASTER_DIR));
|
||||
ensureDirectoryExists(path.join(targetDir, TASKMASTER_TASKS_DIR));
|
||||
@@ -475,22 +525,14 @@ function createProjectStructure(
|
||||
// Create initial state.json file for tag management
|
||||
createInitialStateFile(targetDir);
|
||||
|
||||
// Setup MCP configuration for integration with Cursor
|
||||
setupMCPConfiguration(targetDir);
|
||||
|
||||
// Copy template files with replacements
|
||||
const replacements = {
|
||||
year: new Date().getFullYear()
|
||||
};
|
||||
|
||||
// Helper function to create rule profiles
|
||||
function _processSingleProfile(profileName) {
|
||||
const profile = getRulesProfile(profileName);
|
||||
if (profile) {
|
||||
convertAllRulesToProfileRules(targetDir, profile);
|
||||
// Also triggers MCP config setup (if applicable)
|
||||
} else {
|
||||
log('warn', `Unknown rule profile: ${profileName}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy .env.example
|
||||
copyTemplateFile(
|
||||
'env.example',
|
||||
@@ -510,6 +552,48 @@ function createProjectStructure(
|
||||
// Copy .gitignore
|
||||
copyTemplateFile('gitignore', path.join(targetDir, GITIGNORE_FILE));
|
||||
|
||||
// Copy dev_workflow.mdc
|
||||
copyTemplateFile(
|
||||
'dev_workflow.mdc',
|
||||
path.join(targetDir, '.cursor/rules/dev_workflow.mdc')
|
||||
);
|
||||
|
||||
// Copy taskmaster.mdc
|
||||
copyTemplateFile(
|
||||
'taskmaster.mdc',
|
||||
path.join(targetDir, '.cursor/rules/taskmaster.mdc')
|
||||
);
|
||||
|
||||
// Copy cursor_rules.mdc
|
||||
copyTemplateFile(
|
||||
'cursor_rules.mdc',
|
||||
path.join(targetDir, '.cursor/rules/cursor_rules.mdc')
|
||||
);
|
||||
|
||||
// Copy self_improve.mdc
|
||||
copyTemplateFile(
|
||||
'self_improve.mdc',
|
||||
path.join(targetDir, '.cursor/rules/self_improve.mdc')
|
||||
);
|
||||
|
||||
// Generate Roo rules from Cursor rules
|
||||
log('info', 'Generating Roo rules from Cursor rules...');
|
||||
convertAllCursorRulesToRooRules(targetDir);
|
||||
|
||||
// Copy .windsurfrules
|
||||
copyTemplateFile('windsurfrules', path.join(targetDir, '.windsurfrules'));
|
||||
|
||||
// Copy .roomodes for Roo Code integration
|
||||
copyTemplateFile('.roomodes', path.join(targetDir, '.roomodes'));
|
||||
|
||||
// Copy Roo rule files for each mode
|
||||
for (const mode of ROO_MODES) {
|
||||
copyTemplateFile(
|
||||
`${mode}-rules`,
|
||||
path.join(targetDir, '.roo', `rules-${mode}`, `${mode}-rules`)
|
||||
);
|
||||
}
|
||||
|
||||
// Copy example_prd.txt to NEW location
|
||||
copyTemplateFile('example_prd.txt', path.join(targetDir, EXAMPLE_PRD_FILE));
|
||||
|
||||
@@ -524,17 +608,6 @@ function createProjectStructure(
|
||||
log('warn', 'Git not available, skipping repository initialization');
|
||||
}
|
||||
|
||||
// Generate profile rules from assets/rules
|
||||
log('info', 'Generating profile rules from assets/rules...');
|
||||
for (const profileName of selectedRuleProfiles) {
|
||||
_processSingleProfile(profileName);
|
||||
}
|
||||
|
||||
// Add shell aliases if requested
|
||||
if (addAliases) {
|
||||
addShellAliases();
|
||||
}
|
||||
|
||||
// Run npm install automatically
|
||||
const npmInstallOptions = {
|
||||
cwd: targetDir,
|
||||
@@ -656,5 +729,114 @@ function createProjectStructure(
|
||||
}
|
||||
}
|
||||
|
||||
// Function to setup MCP configuration for Cursor integration
|
||||
function setupMCPConfiguration(targetDir) {
|
||||
const mcpDirPath = path.join(targetDir, '.cursor');
|
||||
const mcpJsonPath = path.join(mcpDirPath, 'mcp.json');
|
||||
|
||||
log('info', 'Setting up MCP configuration for Cursor integration...');
|
||||
|
||||
// Create .cursor directory if it doesn't exist
|
||||
ensureDirectoryExists(mcpDirPath);
|
||||
|
||||
// New MCP config to be added - references the installed package
|
||||
const newMCPServer = {
|
||||
'task-master-ai': {
|
||||
command: 'npx',
|
||||
args: ['-y', '--package=task-master-ai', 'task-master-ai'],
|
||||
env: {
|
||||
ANTHROPIC_API_KEY: 'ANTHROPIC_API_KEY_HERE',
|
||||
PERPLEXITY_API_KEY: 'PERPLEXITY_API_KEY_HERE',
|
||||
OPENAI_API_KEY: 'OPENAI_API_KEY_HERE',
|
||||
GOOGLE_API_KEY: 'GOOGLE_API_KEY_HERE',
|
||||
XAI_API_KEY: 'XAI_API_KEY_HERE',
|
||||
OPENROUTER_API_KEY: 'OPENROUTER_API_KEY_HERE',
|
||||
MISTRAL_API_KEY: 'MISTRAL_API_KEY_HERE',
|
||||
AZURE_OPENAI_API_KEY: 'AZURE_OPENAI_API_KEY_HERE',
|
||||
OLLAMA_API_KEY: 'OLLAMA_API_KEY_HERE'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Check if mcp.json already existsimage.png
|
||||
if (fs.existsSync(mcpJsonPath)) {
|
||||
log(
|
||||
'info',
|
||||
'MCP configuration file already exists, checking for existing task-master-mcp...'
|
||||
);
|
||||
try {
|
||||
// Read existing config
|
||||
const mcpConfig = JSON.parse(fs.readFileSync(mcpJsonPath, 'utf8'));
|
||||
|
||||
// Initialize mcpServers if it doesn't exist
|
||||
if (!mcpConfig.mcpServers) {
|
||||
mcpConfig.mcpServers = {};
|
||||
}
|
||||
|
||||
// Check if any existing server configuration already has task-master-mcp in its args
|
||||
const hasMCPString = Object.values(mcpConfig.mcpServers).some(
|
||||
(server) =>
|
||||
server.args &&
|
||||
server.args.some(
|
||||
(arg) => typeof arg === 'string' && arg.includes('task-master-ai')
|
||||
)
|
||||
);
|
||||
|
||||
if (hasMCPString) {
|
||||
log(
|
||||
'info',
|
||||
'Found existing task-master-ai MCP configuration in mcp.json, leaving untouched'
|
||||
);
|
||||
return; // Exit early, don't modify the existing configuration
|
||||
}
|
||||
|
||||
// Add the task-master-ai server if it doesn't exist
|
||||
if (!mcpConfig.mcpServers['task-master-ai']) {
|
||||
mcpConfig.mcpServers['task-master-ai'] = newMCPServer['task-master-ai'];
|
||||
log(
|
||||
'info',
|
||||
'Added task-master-ai server to existing MCP configuration'
|
||||
);
|
||||
} else {
|
||||
log('info', 'task-master-ai server already configured in mcp.json');
|
||||
}
|
||||
|
||||
// Write the updated configuration
|
||||
fs.writeFileSync(mcpJsonPath, JSON.stringify(mcpConfig, null, 4));
|
||||
log('success', 'Updated MCP configuration file');
|
||||
} catch (error) {
|
||||
log('error', `Failed to update MCP configuration: ${error.message}`);
|
||||
// Create a backup before potentially modifying
|
||||
const backupPath = `${mcpJsonPath}.backup-${Date.now()}`;
|
||||
if (fs.existsSync(mcpJsonPath)) {
|
||||
fs.copyFileSync(mcpJsonPath, backupPath);
|
||||
log('info', `Created backup of existing mcp.json at ${backupPath}`);
|
||||
}
|
||||
|
||||
// Create new configuration
|
||||
const newMCPConfig = {
|
||||
mcpServers: newMCPServer
|
||||
};
|
||||
|
||||
fs.writeFileSync(mcpJsonPath, JSON.stringify(newMCPConfig, null, 4));
|
||||
log(
|
||||
'warn',
|
||||
'Created new MCP configuration file (backup of original file was created if it existed)'
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// If mcp.json doesn't exist, create it
|
||||
const newMCPConfig = {
|
||||
mcpServers: newMCPServer
|
||||
};
|
||||
|
||||
fs.writeFileSync(mcpJsonPath, JSON.stringify(newMCPConfig, null, 4));
|
||||
log('success', 'Created MCP configuration file for Cursor integration');
|
||||
}
|
||||
|
||||
// Add note to console about MCP integration
|
||||
log('info', 'MCP server will use the installed task-master-ai package');
|
||||
}
|
||||
|
||||
// Ensure necessary functions are exported
|
||||
export { initializeProject, log };
|
||||
export { initializeProject, log }; // Only export what's needed by commands.js
|
||||
|
||||
@@ -11,7 +11,6 @@ import fs from 'fs';
|
||||
import https from 'https';
|
||||
import http from 'http';
|
||||
import inquirer from 'inquirer';
|
||||
import search from '@inquirer/search';
|
||||
import ora from 'ora'; // Import ora
|
||||
|
||||
import {
|
||||
@@ -72,8 +71,6 @@ import {
|
||||
getBaseUrlForRole
|
||||
} from './config-manager.js';
|
||||
|
||||
import { CUSTOM_PROVIDERS } from '../../src/constants/providers.js';
|
||||
|
||||
import {
|
||||
COMPLEXITY_REPORT_FILE,
|
||||
PRD_FILE,
|
||||
@@ -99,14 +96,6 @@ import {
|
||||
displayTaggedTasksFYI,
|
||||
displayCurrentTagIndicator
|
||||
} from './ui.js';
|
||||
import {
|
||||
confirmProfilesRemove,
|
||||
confirmRemoveAllRemainingProfiles
|
||||
} from '../../src/ui/confirm.js';
|
||||
import {
|
||||
wouldRemovalLeaveNoProfiles,
|
||||
getInstalledProfiles
|
||||
} from '../../src/utils/profiles.js';
|
||||
|
||||
import { initializeProject } from '../init.js';
|
||||
import {
|
||||
@@ -119,27 +108,8 @@ import {
|
||||
isValidTaskStatus,
|
||||
TASK_STATUS_OPTIONS
|
||||
} from '../../src/constants/task-status.js';
|
||||
import {
|
||||
isValidRulesAction,
|
||||
RULES_ACTIONS,
|
||||
RULES_SETUP_ACTION
|
||||
} from '../../src/constants/rules-actions.js';
|
||||
import { getTaskMasterVersion } from '../../src/utils/getVersion.js';
|
||||
import { syncTasksToReadme } from './sync-readme.js';
|
||||
import { RULE_PROFILES } from '../../src/constants/profiles.js';
|
||||
import {
|
||||
convertAllRulesToProfileRules,
|
||||
removeProfileRules,
|
||||
isValidProfile,
|
||||
getRulesProfile
|
||||
} from '../../src/utils/rule-transformer.js';
|
||||
import {
|
||||
runInteractiveProfilesSetup,
|
||||
generateProfileSummary,
|
||||
categorizeProfileResults,
|
||||
generateProfileRemovalSummary,
|
||||
categorizeRemovalResults
|
||||
} from '../../src/utils/profiles.js';
|
||||
|
||||
/**
|
||||
* Runs the interactive setup process for model configuration.
|
||||
@@ -309,16 +279,6 @@ async function runInteractiveSetup(projectRoot) {
|
||||
value: '__CUSTOM_BEDROCK__'
|
||||
};
|
||||
|
||||
const customAzureOption = {
|
||||
name: '* Custom Azure OpenAI model', // Add Azure custom option
|
||||
value: '__CUSTOM_AZURE__'
|
||||
};
|
||||
|
||||
const customVertexOption = {
|
||||
name: '* Custom Vertex AI model', // Add Vertex custom option
|
||||
value: '__CUSTOM_VERTEX__'
|
||||
};
|
||||
|
||||
let choices = [];
|
||||
let defaultIndex = 0; // Default to 'Cancel'
|
||||
|
||||
@@ -357,50 +317,43 @@ async function runInteractiveSetup(projectRoot) {
|
||||
);
|
||||
}
|
||||
|
||||
// Construct final choices list with custom options moved to bottom
|
||||
const systemOptions = [];
|
||||
// Construct final choices list based on whether 'None' is allowed
|
||||
const commonPrefix = [];
|
||||
if (noChangeOption) {
|
||||
systemOptions.push(noChangeOption);
|
||||
commonPrefix.push(noChangeOption);
|
||||
}
|
||||
systemOptions.push(cancelOption);
|
||||
commonPrefix.push(cancelOption);
|
||||
commonPrefix.push(customOpenRouterOption);
|
||||
commonPrefix.push(customOllamaOption);
|
||||
commonPrefix.push(customBedrockOption);
|
||||
|
||||
const customOptions = [
|
||||
customOpenRouterOption,
|
||||
customOllamaOption,
|
||||
customBedrockOption,
|
||||
customAzureOption,
|
||||
customVertexOption
|
||||
];
|
||||
|
||||
const systemLength = systemOptions.length;
|
||||
const prefixLength = commonPrefix.length; // Initial prefix length
|
||||
|
||||
if (allowNone) {
|
||||
choices = [
|
||||
...systemOptions,
|
||||
new inquirer.Separator('── Standard Models ──'),
|
||||
{ name: '⚪ None (disable)', value: null },
|
||||
...roleChoices,
|
||||
new inquirer.Separator('── Custom Providers ──'),
|
||||
...customOptions
|
||||
...commonPrefix,
|
||||
new inquirer.Separator(),
|
||||
{ name: '⚪ None (disable)', value: null }, // Symbol updated
|
||||
new inquirer.Separator(),
|
||||
...roleChoices
|
||||
];
|
||||
// Adjust default index: System + Sep1 + None (+2)
|
||||
const noneOptionIndex = systemLength + 1;
|
||||
// Adjust default index: Prefix + Sep1 + None + Sep2 (+3)
|
||||
const noneOptionIndex = prefixLength + 1;
|
||||
defaultIndex =
|
||||
currentChoiceIndex !== -1
|
||||
? currentChoiceIndex + systemLength + 2 // Offset by system options and separators
|
||||
? currentChoiceIndex + prefixLength + 3 // Offset by prefix and separators
|
||||
: noneOptionIndex; // Default to 'None' if no current model matched
|
||||
} else {
|
||||
choices = [
|
||||
...systemOptions,
|
||||
new inquirer.Separator('── Standard Models ──'),
|
||||
...commonPrefix,
|
||||
new inquirer.Separator(),
|
||||
...roleChoices,
|
||||
new inquirer.Separator('── Custom Providers ──'),
|
||||
...customOptions
|
||||
new inquirer.Separator()
|
||||
];
|
||||
// Adjust default index: System + Sep (+1)
|
||||
// Adjust default index: Prefix + Sep (+1)
|
||||
defaultIndex =
|
||||
currentChoiceIndex !== -1
|
||||
? currentChoiceIndex + systemLength + 1 // Offset by system options and separator
|
||||
? currentChoiceIndex + prefixLength + 1 // Offset by prefix and separator
|
||||
: noChangeOption
|
||||
? 1
|
||||
: 0; // Default to 'No Change' if present, else 'Cancel'
|
||||
@@ -423,63 +376,32 @@ async function runInteractiveSetup(projectRoot) {
|
||||
const researchPromptData = getPromptData('research');
|
||||
const fallbackPromptData = getPromptData('fallback', true); // Allow 'None' for fallback
|
||||
|
||||
// Display helpful intro message
|
||||
console.log(chalk.cyan('\n🎯 Interactive Model Setup'));
|
||||
console.log(chalk.gray('━'.repeat(50)));
|
||||
console.log(chalk.yellow('💡 Navigation tips:'));
|
||||
console.log(chalk.gray(' • Type to search and filter options'));
|
||||
console.log(chalk.gray(' • Use ↑↓ arrow keys to navigate results'));
|
||||
console.log(
|
||||
chalk.gray(
|
||||
' • Standard models are listed first, custom providers at bottom'
|
||||
)
|
||||
);
|
||||
console.log(chalk.gray(' • Press Enter to select\n'));
|
||||
|
||||
// Helper function to create search source for models
|
||||
const createSearchSource = (choices, defaultValue) => {
|
||||
return (searchTerm = '') => {
|
||||
const filteredChoices = choices.filter((choice) => {
|
||||
if (choice.type === 'separator') return true; // Always show separators
|
||||
const searchText = choice.name || '';
|
||||
return searchText.toLowerCase().includes(searchTerm.toLowerCase());
|
||||
});
|
||||
return Promise.resolve(filteredChoices);
|
||||
};
|
||||
};
|
||||
|
||||
const answers = {};
|
||||
|
||||
// Main model selection
|
||||
answers.mainModel = await search({
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'mainModel',
|
||||
message: 'Select the main model for generation/updates:',
|
||||
source: createSearchSource(mainPromptData.choices, mainPromptData.default),
|
||||
pageSize: 15
|
||||
});
|
||||
|
||||
if (answers.mainModel !== '__CANCEL__') {
|
||||
// Research model selection
|
||||
answers.researchModel = await search({
|
||||
choices: mainPromptData.choices,
|
||||
default: mainPromptData.default
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'researchModel',
|
||||
message: 'Select the research model:',
|
||||
source: createSearchSource(
|
||||
researchPromptData.choices,
|
||||
researchPromptData.default
|
||||
),
|
||||
pageSize: 15
|
||||
});
|
||||
|
||||
if (answers.researchModel !== '__CANCEL__') {
|
||||
// Fallback model selection
|
||||
answers.fallbackModel = await search({
|
||||
choices: researchPromptData.choices,
|
||||
default: researchPromptData.default,
|
||||
when: (ans) => ans.mainModel !== '__CANCEL__'
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'fallbackModel',
|
||||
message: 'Select the fallback model (optional):',
|
||||
source: createSearchSource(
|
||||
fallbackPromptData.choices,
|
||||
fallbackPromptData.default
|
||||
),
|
||||
pageSize: 15
|
||||
});
|
||||
}
|
||||
choices: fallbackPromptData.choices,
|
||||
default: fallbackPromptData.default,
|
||||
when: (ans) =>
|
||||
ans.mainModel !== '__CANCEL__' && ans.researchModel !== '__CANCEL__'
|
||||
}
|
||||
]);
|
||||
|
||||
let setupSuccess = true;
|
||||
let setupConfigModified = false;
|
||||
@@ -519,7 +441,7 @@ async function runInteractiveSetup(projectRoot) {
|
||||
return true; // Continue setup, but don't set this role
|
||||
}
|
||||
modelIdToSet = customId;
|
||||
providerHint = CUSTOM_PROVIDERS.OPENROUTER;
|
||||
providerHint = 'openrouter';
|
||||
// Validate against live OpenRouter list
|
||||
const openRouterModels = await fetchOpenRouterModelsCLI();
|
||||
if (
|
||||
@@ -548,7 +470,7 @@ async function runInteractiveSetup(projectRoot) {
|
||||
return true; // Continue setup, but don't set this role
|
||||
}
|
||||
modelIdToSet = customId;
|
||||
providerHint = CUSTOM_PROVIDERS.OLLAMA;
|
||||
providerHint = 'ollama';
|
||||
// Get the Ollama base URL from config for this role
|
||||
const ollamaBaseURL = getBaseUrlForRole(role, projectRoot);
|
||||
// Validate against live Ollama list
|
||||
@@ -589,16 +511,16 @@ async function runInteractiveSetup(projectRoot) {
|
||||
return true; // Continue setup, but don't set this role
|
||||
}
|
||||
modelIdToSet = customId;
|
||||
providerHint = CUSTOM_PROVIDERS.BEDROCK;
|
||||
providerHint = 'bedrock';
|
||||
|
||||
// Check if AWS environment variables exist
|
||||
if (
|
||||
!process.env.AWS_ACCESS_KEY_ID ||
|
||||
!process.env.AWS_SECRET_ACCESS_KEY
|
||||
) {
|
||||
console.warn(
|
||||
chalk.yellow(
|
||||
'Warning: AWS_ACCESS_KEY_ID and/or AWS_SECRET_ACCESS_KEY environment variables are missing. Will fallback to system configuration. (ex: aws config files or ec2 instance profiles)'
|
||||
console.error(
|
||||
chalk.red(
|
||||
'Error: AWS_ACCESS_KEY_ID and/or AWS_SECRET_ACCESS_KEY environment variables are missing. Please set them before using custom Bedrock models.'
|
||||
)
|
||||
);
|
||||
setupSuccess = false;
|
||||
@@ -610,76 +532,6 @@ async function runInteractiveSetup(projectRoot) {
|
||||
`Custom Bedrock model "${modelIdToSet}" will be used. No validation performed.`
|
||||
)
|
||||
);
|
||||
} else if (selectedValue === '__CUSTOM_AZURE__') {
|
||||
isCustomSelection = true;
|
||||
const { customId } = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'customId',
|
||||
message: `Enter the custom Azure OpenAI Model ID for the ${role} role (e.g., gpt-4o):`
|
||||
}
|
||||
]);
|
||||
if (!customId) {
|
||||
console.log(chalk.yellow('No custom ID entered. Skipping role.'));
|
||||
return true; // Continue setup, but don't set this role
|
||||
}
|
||||
modelIdToSet = customId;
|
||||
providerHint = CUSTOM_PROVIDERS.AZURE;
|
||||
|
||||
// Check if Azure environment variables exist
|
||||
if (
|
||||
!process.env.AZURE_OPENAI_API_KEY ||
|
||||
!process.env.AZURE_OPENAI_ENDPOINT
|
||||
) {
|
||||
console.error(
|
||||
chalk.red(
|
||||
'Error: AZURE_OPENAI_API_KEY and/or AZURE_OPENAI_ENDPOINT environment variables are missing. Please set them before using custom Azure models.'
|
||||
)
|
||||
);
|
||||
setupSuccess = false;
|
||||
return true; // Continue setup, but mark as failed
|
||||
}
|
||||
|
||||
console.log(
|
||||
chalk.blue(
|
||||
`Custom Azure OpenAI model "${modelIdToSet}" will be used. No validation performed.`
|
||||
)
|
||||
);
|
||||
} else if (selectedValue === '__CUSTOM_VERTEX__') {
|
||||
isCustomSelection = true;
|
||||
const { customId } = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'customId',
|
||||
message: `Enter the custom Vertex AI Model ID for the ${role} role (e.g., gemini-1.5-pro-002):`
|
||||
}
|
||||
]);
|
||||
if (!customId) {
|
||||
console.log(chalk.yellow('No custom ID entered. Skipping role.'));
|
||||
return true; // Continue setup, but don't set this role
|
||||
}
|
||||
modelIdToSet = customId;
|
||||
providerHint = CUSTOM_PROVIDERS.VERTEX;
|
||||
|
||||
// Check if Google/Vertex environment variables exist
|
||||
if (
|
||||
!process.env.GOOGLE_API_KEY &&
|
||||
!process.env.GOOGLE_APPLICATION_CREDENTIALS
|
||||
) {
|
||||
console.error(
|
||||
chalk.red(
|
||||
'Error: Either GOOGLE_API_KEY or GOOGLE_APPLICATION_CREDENTIALS environment variable is required. Please set one before using custom Vertex models.'
|
||||
)
|
||||
);
|
||||
setupSuccess = false;
|
||||
return true; // Continue setup, but mark as failed
|
||||
}
|
||||
|
||||
console.log(
|
||||
chalk.blue(
|
||||
`Custom Vertex AI model "${modelIdToSet}" will be used. No validation performed.`
|
||||
)
|
||||
);
|
||||
} else if (
|
||||
selectedValue &&
|
||||
typeof selectedValue === 'object' &&
|
||||
@@ -1762,7 +1614,7 @@ function registerCommands(programInstance) {
|
||||
.command('research')
|
||||
.description('Perform AI-powered research queries with project context')
|
||||
.argument('[prompt]', 'Research prompt to investigate')
|
||||
.option('--file <file>', 'Path to the tasks file')
|
||||
.option('--file <file>', 'Path to the tasks file', 'tasks/tasks.json')
|
||||
.option(
|
||||
'-i, --id <ids>',
|
||||
'Comma-separated task/subtask IDs to include as context (e.g., "15,16.2")'
|
||||
@@ -1900,8 +1752,7 @@ function registerCommands(programInstance) {
|
||||
const projectRoot = findProjectRoot() || '.';
|
||||
const tag = options.tag || getCurrentTag(projectRoot) || 'master';
|
||||
const tasksPath =
|
||||
options.file ||
|
||||
path.join(projectRoot, '.taskmaster', 'tasks', 'tasks.json');
|
||||
options.file || path.join(projectRoot, 'tasks', 'tasks.json');
|
||||
|
||||
// Show current tag context
|
||||
displayCurrentTagIndicator(tag);
|
||||
@@ -2005,15 +1856,14 @@ function registerCommands(programInstance) {
|
||||
researchOptions,
|
||||
{
|
||||
commandName: 'research',
|
||||
outputType: 'cli',
|
||||
tag: tag
|
||||
outputType: 'cli'
|
||||
},
|
||||
'text',
|
||||
validatedParams.allowFollowUp // Pass follow-up flag
|
||||
);
|
||||
|
||||
// Auto-save to task/subtask if requested and no interactive save occurred
|
||||
if (validatedParams.saveToId && !result.interactiveSaveOccurred) {
|
||||
// Auto-save to task/subtask if requested
|
||||
if (validatedParams.saveToId) {
|
||||
try {
|
||||
const isSubtask = validatedParams.saveToId.includes('.');
|
||||
|
||||
@@ -2042,8 +1892,7 @@ ${result.result}`;
|
||||
{
|
||||
commandName: 'research-save',
|
||||
outputType: 'cli',
|
||||
projectRoot: validatedParams.projectRoot,
|
||||
tag: tag
|
||||
projectRoot: validatedParams.projectRoot
|
||||
},
|
||||
'text'
|
||||
);
|
||||
@@ -2068,8 +1917,7 @@ ${result.result}`;
|
||||
{
|
||||
commandName: 'research-save',
|
||||
outputType: 'cli',
|
||||
projectRoot: validatedParams.projectRoot,
|
||||
tag: tag
|
||||
projectRoot: validatedParams.projectRoot
|
||||
},
|
||||
'text',
|
||||
true // appendMode = true
|
||||
@@ -3359,35 +3207,17 @@ ${result.result}
|
||||
.option('-d, --description <description>', 'Project description')
|
||||
.option('-v, --version <version>', 'Project version', '0.1.0') // Set default here
|
||||
.option('-a, --author <author>', 'Author name')
|
||||
.option(
|
||||
'-r, --rules <rules...>',
|
||||
'List of rules to add (roo, windsurf, cursor, ...). Accepts comma or space separated values.'
|
||||
)
|
||||
.option('--skip-install', 'Skip installing dependencies')
|
||||
.option('--dry-run', 'Show what would be done without making changes')
|
||||
.option('--aliases', 'Add shell aliases (tm, taskmaster)')
|
||||
.action(async (cmdOptions) => {
|
||||
// cmdOptions contains parsed arguments
|
||||
// Parse rules: accept space or comma separated, default to all available rules
|
||||
let selectedProfiles = RULE_PROFILES;
|
||||
let rulesExplicitlyProvided = false;
|
||||
|
||||
if (cmdOptions.rules && Array.isArray(cmdOptions.rules)) {
|
||||
const userSpecifiedProfiles = cmdOptions.rules
|
||||
.flatMap((r) => r.split(','))
|
||||
.map((r) => r.trim())
|
||||
.filter(Boolean);
|
||||
// Only override defaults if user specified valid rules
|
||||
if (userSpecifiedProfiles.length > 0) {
|
||||
selectedProfiles = userSpecifiedProfiles;
|
||||
rulesExplicitlyProvided = true;
|
||||
}
|
||||
}
|
||||
|
||||
cmdOptions.rules = selectedProfiles;
|
||||
cmdOptions.rulesExplicitlyProvided = rulesExplicitlyProvided;
|
||||
|
||||
try {
|
||||
console.log('DEBUG: Running init command action in commands.js');
|
||||
console.log(
|
||||
'DEBUG: Options received by action:',
|
||||
JSON.stringify(cmdOptions)
|
||||
);
|
||||
// Directly call the initializeProject function, passing the parsed options
|
||||
await initializeProject(cmdOptions);
|
||||
// initializeProject handles its own flow, including potential process.exit()
|
||||
@@ -3428,14 +3258,6 @@ ${result.result}
|
||||
'--bedrock',
|
||||
'Allow setting a custom Bedrock model ID (use with --set-*) '
|
||||
)
|
||||
.option(
|
||||
'--azure',
|
||||
'Allow setting a custom Azure OpenAI model ID (use with --set-*) '
|
||||
)
|
||||
.option(
|
||||
'--vertex',
|
||||
'Allow setting a custom Vertex AI model ID (use with --set-*) '
|
||||
)
|
||||
.addHelpText(
|
||||
'after',
|
||||
`
|
||||
@@ -3447,8 +3269,6 @@ Examples:
|
||||
$ task-master models --set-main my-custom-model --ollama # Set custom Ollama model for main role
|
||||
$ task-master models --set-main anthropic.claude-3-sonnet-20240229-v1:0 --bedrock # Set custom Bedrock model for main role
|
||||
$ task-master models --set-main some/other-model --openrouter # Set custom OpenRouter model for main role
|
||||
$ task-master models --set-main gpt-4o --azure # Set custom Azure OpenAI model for main role
|
||||
$ task-master models --set-main claude-3-5-sonnet@20241022 --vertex # Set custom Vertex AI model for main role
|
||||
$ task-master models --setup # Run interactive setup`
|
||||
)
|
||||
.action(async (options) => {
|
||||
@@ -3720,7 +3540,7 @@ Examples:
|
||||
|
||||
try {
|
||||
// Read tasks data once to validate destination IDs
|
||||
const tasksData = readJSON(tasksPath, projectRoot, tag);
|
||||
const tasksData = readJSON(tasksPath);
|
||||
if (!tasksData || !tasksData.tasks) {
|
||||
console.error(
|
||||
chalk.red(`Error: Invalid or missing tasks file at ${tasksPath}`)
|
||||
@@ -3794,254 +3614,6 @@ Examples:
|
||||
}
|
||||
});
|
||||
|
||||
// Add/remove profile rules command
|
||||
programInstance
|
||||
.command('rules [action] [profiles...]')
|
||||
.description(
|
||||
`Add or remove rules for one or more profiles. Valid actions: ${Object.values(RULES_ACTIONS).join(', ')} (e.g., task-master rules ${RULES_ACTIONS.ADD} windsurf roo)`
|
||||
)
|
||||
.option(
|
||||
'-f, --force',
|
||||
'Skip confirmation prompt when removing rules (dangerous)'
|
||||
)
|
||||
.option(
|
||||
`--${RULES_SETUP_ACTION}`,
|
||||
'Run interactive setup to select rule profiles to add'
|
||||
)
|
||||
.addHelpText(
|
||||
'after',
|
||||
`
|
||||
Examples:
|
||||
$ task-master rules ${RULES_ACTIONS.ADD} windsurf roo # Add Windsurf and Roo rule sets
|
||||
$ task-master rules ${RULES_ACTIONS.REMOVE} windsurf # Remove Windsurf rule set
|
||||
$ task-master rules --${RULES_SETUP_ACTION} # Interactive setup to select rule profiles`
|
||||
)
|
||||
.action(async (action, profiles, options) => {
|
||||
const projectDir = process.cwd();
|
||||
|
||||
/**
|
||||
* 'task-master rules --setup' action:
|
||||
*
|
||||
* Launches an interactive prompt to select which rule profiles to add to the current project.
|
||||
* This does NOT perform project initialization or ask about shell aliases—only rules selection.
|
||||
*
|
||||
* Example usage:
|
||||
* $ task-master rules --setup
|
||||
*
|
||||
* Useful for adding rules after project creation.
|
||||
*
|
||||
* The list of profiles is always up-to-date with the available profiles.
|
||||
*/
|
||||
if (options[RULES_SETUP_ACTION]) {
|
||||
// Run interactive rules setup ONLY (no project init)
|
||||
const selectedRuleProfiles = await runInteractiveProfilesSetup();
|
||||
for (const profile of selectedRuleProfiles) {
|
||||
if (!isValidProfile(profile)) {
|
||||
console.warn(
|
||||
`Rule profile for "${profile}" not found. Valid profiles: ${RULE_PROFILES.join(', ')}. Skipping.`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
const profileConfig = getRulesProfile(profile);
|
||||
const addResult = convertAllRulesToProfileRules(
|
||||
projectDir,
|
||||
profileConfig
|
||||
);
|
||||
if (typeof profileConfig.onAddRulesProfile === 'function') {
|
||||
profileConfig.onAddRulesProfile(projectDir);
|
||||
}
|
||||
|
||||
console.log(chalk.green(generateProfileSummary(profile, addResult)));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate action for non-setup mode
|
||||
if (!action || !isValidRulesAction(action)) {
|
||||
console.error(
|
||||
chalk.red(
|
||||
`Error: Invalid or missing action '${action || 'none'}'. Valid actions are: ${Object.values(RULES_ACTIONS).join(', ')}`
|
||||
)
|
||||
);
|
||||
console.error(
|
||||
chalk.yellow(
|
||||
`For interactive setup, use: task-master rules --${RULES_SETUP_ACTION}`
|
||||
)
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!profiles || profiles.length === 0) {
|
||||
console.error(
|
||||
'Please specify at least one rule profile (e.g., windsurf, roo).'
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Support both space- and comma-separated profile lists
|
||||
const expandedProfiles = profiles
|
||||
.flatMap((b) => b.split(',').map((s) => s.trim()))
|
||||
.filter(Boolean);
|
||||
|
||||
if (action === RULES_ACTIONS.REMOVE) {
|
||||
let confirmed = true;
|
||||
if (!options.force) {
|
||||
// Check if this removal would leave no profiles remaining
|
||||
if (wouldRemovalLeaveNoProfiles(projectDir, expandedProfiles)) {
|
||||
const installedProfiles = getInstalledProfiles(projectDir);
|
||||
confirmed = await confirmRemoveAllRemainingProfiles(
|
||||
expandedProfiles,
|
||||
installedProfiles
|
||||
);
|
||||
} else {
|
||||
confirmed = await confirmProfilesRemove(expandedProfiles);
|
||||
}
|
||||
}
|
||||
if (!confirmed) {
|
||||
console.log(chalk.yellow('Aborted: No rules were removed.'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const removalResults = [];
|
||||
const addResults = [];
|
||||
|
||||
for (const profile of expandedProfiles) {
|
||||
if (!isValidProfile(profile)) {
|
||||
console.warn(
|
||||
`Rule profile for "${profile}" not found. Valid profiles: ${RULE_PROFILES.join(', ')}. Skipping.`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
const profileConfig = getRulesProfile(profile);
|
||||
|
||||
if (action === RULES_ACTIONS.ADD) {
|
||||
console.log(chalk.blue(`Adding rules for profile: ${profile}...`));
|
||||
const addResult = convertAllRulesToProfileRules(
|
||||
projectDir,
|
||||
profileConfig
|
||||
);
|
||||
if (typeof profileConfig.onAddRulesProfile === 'function') {
|
||||
const assetsDir = path.join(process.cwd(), 'assets');
|
||||
profileConfig.onAddRulesProfile(projectDir, assetsDir);
|
||||
}
|
||||
console.log(
|
||||
chalk.blue(`Completed adding rules for profile: ${profile}`)
|
||||
);
|
||||
|
||||
// Store result with profile name for summary
|
||||
addResults.push({
|
||||
profileName: profile,
|
||||
success: addResult.success,
|
||||
failed: addResult.failed
|
||||
});
|
||||
|
||||
console.log(chalk.green(generateProfileSummary(profile, addResult)));
|
||||
} else if (action === RULES_ACTIONS.REMOVE) {
|
||||
console.log(chalk.blue(`Removing rules for profile: ${profile}...`));
|
||||
const result = removeProfileRules(projectDir, profileConfig);
|
||||
removalResults.push(result);
|
||||
console.log(
|
||||
chalk.green(generateProfileRemovalSummary(profile, result))
|
||||
);
|
||||
} else {
|
||||
console.error(
|
||||
`Unknown action. Use "${RULES_ACTIONS.ADD}" or "${RULES_ACTIONS.REMOVE}".`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Print summary for additions
|
||||
if (action === RULES_ACTIONS.ADD && addResults.length > 0) {
|
||||
const {
|
||||
allSuccessfulProfiles,
|
||||
totalSuccess,
|
||||
totalFailed,
|
||||
simpleProfiles
|
||||
} = categorizeProfileResults(addResults);
|
||||
|
||||
if (allSuccessfulProfiles.length > 0) {
|
||||
console.log(
|
||||
chalk.green(
|
||||
`\nSuccessfully added rules for: ${allSuccessfulProfiles.join(', ')}`
|
||||
)
|
||||
);
|
||||
|
||||
// Create a more descriptive summary
|
||||
if (totalSuccess > 0 && simpleProfiles.length > 0) {
|
||||
console.log(
|
||||
chalk.green(
|
||||
`Total: ${totalSuccess} rules added, ${totalFailed} failed, ${simpleProfiles.length} integration guide(s) copied.`
|
||||
)
|
||||
);
|
||||
} else if (totalSuccess > 0) {
|
||||
console.log(
|
||||
chalk.green(
|
||||
`Total: ${totalSuccess} rules added, ${totalFailed} failed.`
|
||||
)
|
||||
);
|
||||
} else if (simpleProfiles.length > 0) {
|
||||
console.log(
|
||||
chalk.green(
|
||||
`Total: ${simpleProfiles.length} integration guide(s) copied.`
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Print summary for removals
|
||||
if (action === RULES_ACTIONS.REMOVE && removalResults.length > 0) {
|
||||
const {
|
||||
successfulRemovals,
|
||||
skippedRemovals,
|
||||
failedRemovals,
|
||||
removalsWithNotices
|
||||
} = categorizeRemovalResults(removalResults);
|
||||
|
||||
if (successfulRemovals.length > 0) {
|
||||
console.log(
|
||||
chalk.green(
|
||||
`\nSuccessfully removed profiles for: ${successfulRemovals.join(', ')}`
|
||||
)
|
||||
);
|
||||
}
|
||||
if (skippedRemovals.length > 0) {
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
`Skipped (default or protected): ${skippedRemovals.join(', ')}`
|
||||
)
|
||||
);
|
||||
}
|
||||
if (failedRemovals.length > 0) {
|
||||
console.log(chalk.red('\nErrors occurred:'));
|
||||
failedRemovals.forEach((r) => {
|
||||
console.log(chalk.red(` ${r.profileName}: ${r.error}`));
|
||||
});
|
||||
}
|
||||
// Display notices about preserved files/configurations
|
||||
if (removalsWithNotices.length > 0) {
|
||||
console.log(chalk.cyan('\nNotices:'));
|
||||
removalsWithNotices.forEach((r) => {
|
||||
console.log(chalk.cyan(` ${r.profileName}: ${r.notice}`));
|
||||
});
|
||||
}
|
||||
|
||||
// Overall summary
|
||||
const totalProcessed = removalResults.length;
|
||||
const totalSuccessful = successfulRemovals.length;
|
||||
const totalSkipped = skippedRemovals.length;
|
||||
const totalFailed = failedRemovals.length;
|
||||
|
||||
console.log(
|
||||
chalk.blue(
|
||||
`\nTotal: ${totalProcessed} profile(s) processed - ${totalSuccessful} removed, ${totalSkipped} skipped, ${totalFailed} failed.`
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
programInstance
|
||||
.command('migrate')
|
||||
.description(
|
||||
|
||||
@@ -5,12 +5,6 @@ import { fileURLToPath } from 'url';
|
||||
import { log, findProjectRoot, resolveEnvVariable } from './utils.js';
|
||||
import { LEGACY_CONFIG_FILE } from '../../src/constants/paths.js';
|
||||
import { findConfigPath } from '../../src/utils/path-utils.js';
|
||||
import {
|
||||
VALIDATED_PROVIDERS,
|
||||
CUSTOM_PROVIDERS,
|
||||
CUSTOM_PROVIDERS_ARRAY,
|
||||
ALL_PROVIDERS
|
||||
} from '../../src/constants/providers.js';
|
||||
|
||||
// Calculate __dirname in ESM
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
@@ -35,6 +29,9 @@ try {
|
||||
process.exit(1); // Exit if models can't be loaded
|
||||
}
|
||||
|
||||
// Define valid providers dynamically from the loaded MODEL_MAP
|
||||
const VALID_PROVIDERS = Object.keys(MODEL_MAP || {});
|
||||
|
||||
// Default configuration values (used if config file is missing or incomplete)
|
||||
const DEFAULTS = {
|
||||
models: {
|
||||
@@ -236,25 +233,12 @@ function getConfig(explicitRoot = null, forceReload = false) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if a provider name is supported.
|
||||
* Custom providers (azure, vertex, bedrock, openrouter, ollama) are always allowed.
|
||||
* Validated providers must exist in the MODEL_MAP from supported-models.json.
|
||||
* Validates if a provider name is in the list of supported providers.
|
||||
* @param {string} providerName The name of the provider.
|
||||
* @returns {boolean} True if the provider is valid, false otherwise.
|
||||
*/
|
||||
function validateProvider(providerName) {
|
||||
// Custom providers are always allowed
|
||||
if (CUSTOM_PROVIDERS_ARRAY.includes(providerName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Validated providers must exist in MODEL_MAP
|
||||
if (VALIDATED_PROVIDERS.includes(providerName)) {
|
||||
return !!(MODEL_MAP && MODEL_MAP[providerName]);
|
||||
}
|
||||
|
||||
// Unknown providers are not allowed
|
||||
return false;
|
||||
return VALID_PROVIDERS.includes(providerName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -496,14 +480,7 @@ function getParametersForRole(role, explicitRoot = null) {
|
||||
*/
|
||||
function isApiKeySet(providerName, session = null, projectRoot = null) {
|
||||
// Define the expected environment variable name for each provider
|
||||
|
||||
// Providers that don't require API keys for authentication
|
||||
const providersWithoutApiKeys = [
|
||||
CUSTOM_PROVIDERS.OLLAMA,
|
||||
CUSTOM_PROVIDERS.BEDROCK
|
||||
];
|
||||
|
||||
if (providersWithoutApiKeys.includes(providerName?.toLowerCase())) {
|
||||
if (providerName?.toLowerCase() === 'ollama') {
|
||||
return true; // Indicate key status is effectively "OK"
|
||||
}
|
||||
|
||||
@@ -759,11 +736,11 @@ function getUserId(explicitRoot = null) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of all known provider names (both validated and custom).
|
||||
* @returns {string[]} An array of all provider names.
|
||||
* Gets a list of all provider names defined in the MODEL_MAP.
|
||||
* @returns {string[]} An array of provider names.
|
||||
*/
|
||||
function getAllProviders() {
|
||||
return ALL_PROVIDERS;
|
||||
return Object.keys(MODEL_MAP || {});
|
||||
}
|
||||
|
||||
function getBaseUrlForRole(role, explicitRoot = null) {
|
||||
@@ -782,9 +759,7 @@ export {
|
||||
// Validation
|
||||
validateProvider,
|
||||
validateProviderModelCombination,
|
||||
VALIDATED_PROVIDERS,
|
||||
CUSTOM_PROVIDERS,
|
||||
ALL_PROVIDERS,
|
||||
VALID_PROVIDERS,
|
||||
MODEL_MAP,
|
||||
getAvailableModels,
|
||||
// Role-specific getters (No env var overrides)
|
||||
|
||||
314
scripts/modules/rule-transformer.js
Normal file
314
scripts/modules/rule-transformer.js
Normal file
@@ -0,0 +1,314 @@
|
||||
/**
|
||||
* Rule Transformer Module
|
||||
* Handles conversion of Cursor rules to Roo rules
|
||||
*
|
||||
* This module procedurally generates .roo/rules files from .cursor/rules files,
|
||||
* eliminating the need to maintain both sets of files manually.
|
||||
*/
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { log } from './utils.js';
|
||||
|
||||
// Configuration for term conversions - centralized for easier future updates
|
||||
const conversionConfig = {
|
||||
// Product and brand name replacements
|
||||
brandTerms: [
|
||||
{ from: /cursor\.so/g, to: 'roocode.com' },
|
||||
{ from: /\[cursor\.so\]/g, to: '[roocode.com]' },
|
||||
{ from: /href="https:\/\/cursor\.so/g, to: 'href="https://roocode.com' },
|
||||
{ from: /\(https:\/\/cursor\.so/g, to: '(https://roocode.com' },
|
||||
{
|
||||
from: /\bcursor\b/gi,
|
||||
to: (match) => (match === 'Cursor' ? 'Roo Code' : 'roo')
|
||||
},
|
||||
{ from: /Cursor/g, to: 'Roo Code' }
|
||||
],
|
||||
|
||||
// File extension replacements
|
||||
fileExtensions: [{ from: /\.mdc\b/g, to: '.md' }],
|
||||
|
||||
// Documentation URL replacements
|
||||
docUrls: [
|
||||
{
|
||||
from: /https:\/\/docs\.cursor\.com\/[^\s)'"]+/g,
|
||||
to: (match) => match.replace('docs.cursor.com', 'docs.roocode.com')
|
||||
},
|
||||
{ from: /https:\/\/docs\.roo\.com\//g, to: 'https://docs.roocode.com/' }
|
||||
],
|
||||
|
||||
// Tool references - direct replacements
|
||||
toolNames: {
|
||||
search: 'search_files',
|
||||
read_file: 'read_file',
|
||||
edit_file: 'apply_diff',
|
||||
create_file: 'write_to_file',
|
||||
run_command: 'execute_command',
|
||||
terminal_command: 'execute_command',
|
||||
use_mcp: 'use_mcp_tool',
|
||||
switch_mode: 'switch_mode'
|
||||
},
|
||||
|
||||
// Tool references in context - more specific replacements
|
||||
toolContexts: [
|
||||
{ from: /\bsearch tool\b/g, to: 'search_files tool' },
|
||||
{ from: /\bedit_file tool\b/g, to: 'apply_diff tool' },
|
||||
{ from: /\buse the search\b/g, to: 'use the search_files' },
|
||||
{ from: /\bThe edit_file\b/g, to: 'The apply_diff' },
|
||||
{ from: /\brun_command executes\b/g, to: 'execute_command executes' },
|
||||
{ from: /\buse_mcp connects\b/g, to: 'use_mcp_tool connects' },
|
||||
// Additional contextual patterns for flexibility
|
||||
{ from: /\bCursor search\b/g, to: 'Roo Code search_files' },
|
||||
{ from: /\bCursor edit\b/g, to: 'Roo Code apply_diff' },
|
||||
{ from: /\bCursor create\b/g, to: 'Roo Code write_to_file' },
|
||||
{ from: /\bCursor run\b/g, to: 'Roo Code execute_command' }
|
||||
],
|
||||
|
||||
// Tool group and category names
|
||||
toolGroups: [
|
||||
{ from: /\bSearch tools\b/g, to: 'Read Group tools' },
|
||||
{ from: /\bEdit tools\b/g, to: 'Edit Group tools' },
|
||||
{ from: /\bRun tools\b/g, to: 'Command Group tools' },
|
||||
{ from: /\bMCP servers\b/g, to: 'MCP Group tools' },
|
||||
{ from: /\bSearch Group\b/g, to: 'Read Group' },
|
||||
{ from: /\bEdit Group\b/g, to: 'Edit Group' },
|
||||
{ from: /\bRun Group\b/g, to: 'Command Group' }
|
||||
],
|
||||
|
||||
// File references in markdown links
|
||||
fileReferences: {
|
||||
pathPattern: /\[(.+?)\]\(mdc:\.cursor\/rules\/(.+?)\.mdc\)/g,
|
||||
replacement: (match, text, filePath) => {
|
||||
// Get the base filename
|
||||
const baseName = path.basename(filePath, '.mdc');
|
||||
|
||||
// Get the new filename (either from mapping or by replacing extension)
|
||||
const newFileName = fileMap[`${baseName}.mdc`] || `${baseName}.md`;
|
||||
|
||||
// Return the updated link
|
||||
return `[${text}](mdc:.roo/rules/${newFileName})`;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// File name mapping (specific files with naming changes)
|
||||
const fileMap = {
|
||||
'cursor_rules.mdc': 'roo_rules.md',
|
||||
'dev_workflow.mdc': 'dev_workflow.md',
|
||||
'self_improve.mdc': 'self_improve.md',
|
||||
'taskmaster.mdc': 'taskmaster.md'
|
||||
// Add other mappings as needed
|
||||
};
|
||||
|
||||
/**
|
||||
* Replace basic Cursor terms with Roo equivalents
|
||||
*/
|
||||
function replaceBasicTerms(content) {
|
||||
let result = content;
|
||||
|
||||
// Apply brand term replacements
|
||||
conversionConfig.brandTerms.forEach((pattern) => {
|
||||
if (typeof pattern.to === 'function') {
|
||||
result = result.replace(pattern.from, pattern.to);
|
||||
} else {
|
||||
result = result.replace(pattern.from, pattern.to);
|
||||
}
|
||||
});
|
||||
|
||||
// Apply file extension replacements
|
||||
conversionConfig.fileExtensions.forEach((pattern) => {
|
||||
result = result.replace(pattern.from, pattern.to);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace Cursor tool references with Roo tool equivalents
|
||||
*/
|
||||
function replaceToolReferences(content) {
|
||||
let result = content;
|
||||
|
||||
// Basic pattern for direct tool name replacements
|
||||
const toolNames = conversionConfig.toolNames;
|
||||
const toolReferencePattern = new RegExp(
|
||||
`\\b(${Object.keys(toolNames).join('|')})\\b`,
|
||||
'g'
|
||||
);
|
||||
|
||||
// Apply direct tool name replacements
|
||||
result = result.replace(toolReferencePattern, (match, toolName) => {
|
||||
return toolNames[toolName] || toolName;
|
||||
});
|
||||
|
||||
// Apply contextual tool replacements
|
||||
conversionConfig.toolContexts.forEach((pattern) => {
|
||||
result = result.replace(pattern.from, pattern.to);
|
||||
});
|
||||
|
||||
// Apply tool group replacements
|
||||
conversionConfig.toolGroups.forEach((pattern) => {
|
||||
result = result.replace(pattern.from, pattern.to);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update documentation URLs to point to Roo documentation
|
||||
*/
|
||||
function updateDocReferences(content) {
|
||||
let result = content;
|
||||
|
||||
// Apply documentation URL replacements
|
||||
conversionConfig.docUrls.forEach((pattern) => {
|
||||
if (typeof pattern.to === 'function') {
|
||||
result = result.replace(pattern.from, pattern.to);
|
||||
} else {
|
||||
result = result.replace(pattern.from, pattern.to);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update file references in markdown links
|
||||
*/
|
||||
function updateFileReferences(content) {
|
||||
const { pathPattern, replacement } = conversionConfig.fileReferences;
|
||||
return content.replace(pathPattern, replacement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main transformation function that applies all conversions
|
||||
*/
|
||||
function transformCursorToRooRules(content) {
|
||||
// Apply all transformations in appropriate order
|
||||
let result = content;
|
||||
result = replaceBasicTerms(result);
|
||||
result = replaceToolReferences(result);
|
||||
result = updateDocReferences(result);
|
||||
result = updateFileReferences(result);
|
||||
|
||||
// Super aggressive failsafe pass to catch any variations we might have missed
|
||||
// This ensures critical transformations are applied even in contexts we didn't anticipate
|
||||
|
||||
// 1. Handle cursor.so in any possible context
|
||||
result = result.replace(/cursor\.so/gi, 'roocode.com');
|
||||
// Edge case: URL with different formatting
|
||||
result = result.replace(/cursor\s*\.\s*so/gi, 'roocode.com');
|
||||
result = result.replace(/https?:\/\/cursor\.so/gi, 'https://roocode.com');
|
||||
result = result.replace(
|
||||
/https?:\/\/www\.cursor\.so/gi,
|
||||
'https://www.roocode.com'
|
||||
);
|
||||
|
||||
// 2. Handle tool references - even partial ones
|
||||
result = result.replace(/\bedit_file\b/gi, 'apply_diff');
|
||||
result = result.replace(/\bsearch tool\b/gi, 'search_files tool');
|
||||
result = result.replace(/\bSearch Tool\b/g, 'Search_Files Tool');
|
||||
|
||||
// 3. Handle basic terms (with case handling)
|
||||
result = result.replace(/\bcursor\b/gi, (match) =>
|
||||
match.charAt(0) === 'C' ? 'Roo Code' : 'roo'
|
||||
);
|
||||
result = result.replace(/Cursor/g, 'Roo Code');
|
||||
result = result.replace(/CURSOR/g, 'ROO CODE');
|
||||
|
||||
// 4. Handle file extensions
|
||||
result = result.replace(/\.mdc\b/g, '.md');
|
||||
|
||||
// 5. Handle any missed URL patterns
|
||||
result = result.replace(/docs\.cursor\.com/gi, 'docs.roocode.com');
|
||||
result = result.replace(/docs\.roo\.com/gi, 'docs.roocode.com');
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a single Cursor rule file to Roo rule format
|
||||
*/
|
||||
function convertCursorRuleToRooRule(sourcePath, targetPath) {
|
||||
try {
|
||||
log(
|
||||
'info',
|
||||
`Converting Cursor rule ${path.basename(sourcePath)} to Roo rule ${path.basename(targetPath)}`
|
||||
);
|
||||
|
||||
// Read source content
|
||||
const content = fs.readFileSync(sourcePath, 'utf8');
|
||||
|
||||
// Transform content
|
||||
const transformedContent = transformCursorToRooRules(content);
|
||||
|
||||
// Ensure target directory exists
|
||||
const targetDir = path.dirname(targetPath);
|
||||
if (!fs.existsSync(targetDir)) {
|
||||
fs.mkdirSync(targetDir, { recursive: true });
|
||||
}
|
||||
|
||||
// Write transformed content
|
||||
fs.writeFileSync(targetPath, transformedContent);
|
||||
log(
|
||||
'success',
|
||||
`Successfully converted ${path.basename(sourcePath)} to ${path.basename(targetPath)}`
|
||||
);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
log(
|
||||
'error',
|
||||
`Failed to convert rule file ${path.basename(sourcePath)}: ${error.message}`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process all Cursor rules and convert to Roo rules
|
||||
*/
|
||||
function convertAllCursorRulesToRooRules(projectDir) {
|
||||
const cursorRulesDir = path.join(projectDir, '.cursor', 'rules');
|
||||
const rooRulesDir = path.join(projectDir, '.roo', 'rules');
|
||||
|
||||
if (!fs.existsSync(cursorRulesDir)) {
|
||||
log('warn', `Cursor rules directory not found: ${cursorRulesDir}`);
|
||||
return { success: 0, failed: 0 };
|
||||
}
|
||||
|
||||
// Ensure Roo rules directory exists
|
||||
if (!fs.existsSync(rooRulesDir)) {
|
||||
fs.mkdirSync(rooRulesDir, { recursive: true });
|
||||
log('info', `Created Roo rules directory: ${rooRulesDir}`);
|
||||
}
|
||||
|
||||
// Count successful and failed conversions
|
||||
let success = 0;
|
||||
let failed = 0;
|
||||
|
||||
// Process each file in the Cursor rules directory
|
||||
fs.readdirSync(cursorRulesDir).forEach((file) => {
|
||||
if (file.endsWith('.mdc')) {
|
||||
const sourcePath = path.join(cursorRulesDir, file);
|
||||
|
||||
// Determine target file name (either from mapping or by replacing extension)
|
||||
const targetFilename = fileMap[file] || file.replace('.mdc', '.md');
|
||||
const targetPath = path.join(rooRulesDir, targetFilename);
|
||||
|
||||
// Convert the file
|
||||
if (convertCursorRuleToRooRule(sourcePath, targetPath)) {
|
||||
success++;
|
||||
} else {
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
log(
|
||||
'info',
|
||||
`Rule conversion complete: ${success} successful, ${failed} failed`
|
||||
);
|
||||
return { success, failed };
|
||||
}
|
||||
|
||||
export { convertAllCursorRulesToRooRules, convertCursorRuleToRooRule };
|
||||
@@ -1,58 +1,30 @@
|
||||
{
|
||||
"bedrock": [
|
||||
{
|
||||
"id": "us.anthropic.claude-3-7-sonnet-20250219-v1:0",
|
||||
"swe_score": 0.623,
|
||||
"cost_per_1m_tokens": { "input": 3, "output": 15 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 65536
|
||||
},
|
||||
{
|
||||
"id": "us.deepseek.r1-v1:0",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 1.35, "output": 5.4 },
|
||||
"allowed_roles": ["research"],
|
||||
"max_tokens": 65536
|
||||
}
|
||||
],
|
||||
"anthropic": [
|
||||
{
|
||||
"id": "claude-sonnet-4-20250514",
|
||||
"swe_score": 0.727,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 3.0,
|
||||
"output": 15.0
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 3.0, "output": 15.0 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 64000
|
||||
},
|
||||
{
|
||||
"id": "claude-opus-4-20250514",
|
||||
"swe_score": 0.725,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 15.0,
|
||||
"output": 75.0
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 15.0, "output": 75.0 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 32000
|
||||
},
|
||||
{
|
||||
"id": "claude-3-7-sonnet-20250219",
|
||||
"swe_score": 0.623,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 3.0,
|
||||
"output": 15.0
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 3.0, "output": 15.0 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 120000
|
||||
},
|
||||
{
|
||||
"id": "claude-3-5-sonnet-20241022",
|
||||
"swe_score": 0.49,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 3.0,
|
||||
"output": 15.0
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 3.0, "output": 15.0 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 64000
|
||||
}
|
||||
@@ -61,120 +33,81 @@
|
||||
{
|
||||
"id": "gpt-4o",
|
||||
"swe_score": 0.332,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 2.5,
|
||||
"output": 10.0
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 2.5, "output": 10.0 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 16384
|
||||
},
|
||||
{
|
||||
"id": "o1",
|
||||
"swe_score": 0.489,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 15.0,
|
||||
"output": 60.0
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 15.0, "output": 60.0 },
|
||||
"allowed_roles": ["main"]
|
||||
},
|
||||
{
|
||||
"id": "o3",
|
||||
"swe_score": 0.5,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 2.0,
|
||||
"output": 8.0
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 2.0, "output": 8.0 },
|
||||
"allowed_roles": ["main", "fallback"]
|
||||
},
|
||||
{
|
||||
"id": "o3-mini",
|
||||
"swe_score": 0.493,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 1.1,
|
||||
"output": 4.4
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 1.1, "output": 4.4 },
|
||||
"allowed_roles": ["main"],
|
||||
"max_tokens": 100000
|
||||
},
|
||||
{
|
||||
"id": "o4-mini",
|
||||
"swe_score": 0.45,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 1.1,
|
||||
"output": 4.4
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 1.1, "output": 4.4 },
|
||||
"allowed_roles": ["main", "fallback"]
|
||||
},
|
||||
{
|
||||
"id": "o1-mini",
|
||||
"swe_score": 0.4,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 1.1,
|
||||
"output": 4.4
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 1.1, "output": 4.4 },
|
||||
"allowed_roles": ["main"]
|
||||
},
|
||||
{
|
||||
"id": "o1-pro",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 150.0,
|
||||
"output": 600.0
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 150.0, "output": 600.0 },
|
||||
"allowed_roles": ["main"]
|
||||
},
|
||||
{
|
||||
"id": "gpt-4-5-preview",
|
||||
"swe_score": 0.38,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 75.0,
|
||||
"output": 150.0
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 75.0, "output": 150.0 },
|
||||
"allowed_roles": ["main"]
|
||||
},
|
||||
{
|
||||
"id": "gpt-4-1-mini",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.4,
|
||||
"output": 1.6
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 0.4, "output": 1.6 },
|
||||
"allowed_roles": ["main"]
|
||||
},
|
||||
{
|
||||
"id": "gpt-4-1-nano",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.1,
|
||||
"output": 0.4
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 0.1, "output": 0.4 },
|
||||
"allowed_roles": ["main"]
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-mini",
|
||||
"swe_score": 0.3,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.15,
|
||||
"output": 0.6
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 0.15, "output": 0.6 },
|
||||
"allowed_roles": ["main"]
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-search-preview",
|
||||
"swe_score": 0.33,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 2.5,
|
||||
"output": 10.0
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 2.5, "output": 10.0 },
|
||||
"allowed_roles": ["research"]
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-mini-search-preview",
|
||||
"swe_score": 0.3,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.15,
|
||||
"output": 0.6
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 0.15, "output": 0.6 },
|
||||
"allowed_roles": ["research"]
|
||||
}
|
||||
],
|
||||
@@ -195,18 +128,15 @@
|
||||
},
|
||||
{
|
||||
"id": "gemini-2.5-flash-preview-04-17",
|
||||
"swe_score": 0.604,
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": null,
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 1048000
|
||||
},
|
||||
{
|
||||
"id": "gemini-2.0-flash",
|
||||
"swe_score": 0.518,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.15,
|
||||
"output": 0.6
|
||||
},
|
||||
"swe_score": 0.754,
|
||||
"cost_per_1m_tokens": { "input": 0.15, "output": 0.6 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 1048000
|
||||
},
|
||||
@@ -222,50 +152,35 @@
|
||||
{
|
||||
"id": "sonar-pro",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 3,
|
||||
"output": 15
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 3, "output": 15 },
|
||||
"allowed_roles": ["main", "research"],
|
||||
"max_tokens": 8700
|
||||
},
|
||||
{
|
||||
"id": "sonar",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 1,
|
||||
"output": 1
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 1, "output": 1 },
|
||||
"allowed_roles": ["research"],
|
||||
"max_tokens": 8700
|
||||
},
|
||||
{
|
||||
"id": "deep-research",
|
||||
"swe_score": 0.211,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 2,
|
||||
"output": 8
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 2, "output": 8 },
|
||||
"allowed_roles": ["research"],
|
||||
"max_tokens": 8700
|
||||
},
|
||||
{
|
||||
"id": "sonar-reasoning-pro",
|
||||
"swe_score": 0.211,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 2,
|
||||
"output": 8
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 2, "output": 8 },
|
||||
"allowed_roles": ["main", "research", "fallback"],
|
||||
"max_tokens": 8700
|
||||
},
|
||||
{
|
||||
"id": "sonar-reasoning",
|
||||
"swe_score": 0.211,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 1,
|
||||
"output": 5
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 1, "output": 5 },
|
||||
"allowed_roles": ["main", "research", "fallback"],
|
||||
"max_tokens": 8700
|
||||
}
|
||||
@@ -275,10 +190,7 @@
|
||||
"id": "grok-3",
|
||||
"name": "Grok 3",
|
||||
"swe_score": null,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 3,
|
||||
"output": 15
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 3, "output": 15 },
|
||||
"allowed_roles": ["main", "fallback", "research"],
|
||||
"max_tokens": 131072
|
||||
},
|
||||
@@ -286,10 +198,7 @@
|
||||
"id": "grok-3-fast",
|
||||
"name": "Grok 3 Fast",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 5,
|
||||
"output": 25
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 5, "output": 25 },
|
||||
"allowed_roles": ["main", "fallback", "research"],
|
||||
"max_tokens": 131072
|
||||
}
|
||||
@@ -298,64 +207,43 @@
|
||||
{
|
||||
"id": "devstral:latest",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0,
|
||||
"output": 0
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 0, "output": 0 },
|
||||
"allowed_roles": ["main", "fallback"]
|
||||
},
|
||||
{
|
||||
"id": "qwen3:latest",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0,
|
||||
"output": 0
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 0, "output": 0 },
|
||||
"allowed_roles": ["main", "fallback"]
|
||||
},
|
||||
{
|
||||
"id": "qwen3:14b",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0,
|
||||
"output": 0
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 0, "output": 0 },
|
||||
"allowed_roles": ["main", "fallback"]
|
||||
},
|
||||
{
|
||||
"id": "qwen3:32b",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0,
|
||||
"output": 0
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 0, "output": 0 },
|
||||
"allowed_roles": ["main", "fallback"]
|
||||
},
|
||||
{
|
||||
"id": "mistral-small3.1:latest",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0,
|
||||
"output": 0
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 0, "output": 0 },
|
||||
"allowed_roles": ["main", "fallback"]
|
||||
},
|
||||
{
|
||||
"id": "llama3.3:latest",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0,
|
||||
"output": 0
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 0, "output": 0 },
|
||||
"allowed_roles": ["main", "fallback"]
|
||||
},
|
||||
{
|
||||
"id": "phi4:latest",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0,
|
||||
"output": 0
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 0, "output": 0 },
|
||||
"allowed_roles": ["main", "fallback"]
|
||||
}
|
||||
],
|
||||
@@ -363,250 +251,175 @@
|
||||
{
|
||||
"id": "google/gemini-2.5-flash-preview-05-20",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.15,
|
||||
"output": 0.6
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 0.15, "output": 0.6 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 1048576
|
||||
},
|
||||
{
|
||||
"id": "google/gemini-2.5-flash-preview-05-20:thinking",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.15,
|
||||
"output": 3.5
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 0.15, "output": 3.5 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 1048576
|
||||
},
|
||||
{
|
||||
"id": "google/gemini-2.5-pro-exp-03-25",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0,
|
||||
"output": 0
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 0, "output": 0 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 1000000
|
||||
},
|
||||
{
|
||||
"id": "deepseek/deepseek-chat-v3-0324:free",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0,
|
||||
"output": 0
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 0, "output": 0 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 163840
|
||||
},
|
||||
{
|
||||
"id": "deepseek/deepseek-chat-v3-0324",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.27,
|
||||
"output": 1.1
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 0.27, "output": 1.1 },
|
||||
"allowed_roles": ["main"],
|
||||
"max_tokens": 64000
|
||||
},
|
||||
{
|
||||
"id": "openai/gpt-4.1",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 2,
|
||||
"output": 8
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 2, "output": 8 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 1000000
|
||||
},
|
||||
{
|
||||
"id": "openai/gpt-4.1-mini",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.4,
|
||||
"output": 1.6
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 0.4, "output": 1.6 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 1000000
|
||||
},
|
||||
{
|
||||
"id": "openai/gpt-4.1-nano",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.1,
|
||||
"output": 0.4
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 0.1, "output": 0.4 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 1000000
|
||||
},
|
||||
{
|
||||
"id": "openai/o3",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 10,
|
||||
"output": 40
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 10, "output": 40 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 200000
|
||||
},
|
||||
{
|
||||
"id": "openai/codex-mini",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 1.5,
|
||||
"output": 6
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 1.5, "output": 6 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 100000
|
||||
},
|
||||
{
|
||||
"id": "openai/gpt-4o-mini",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.15,
|
||||
"output": 0.6
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 0.15, "output": 0.6 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 100000
|
||||
},
|
||||
{
|
||||
"id": "openai/o4-mini",
|
||||
"swe_score": 0.45,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 1.1,
|
||||
"output": 4.4
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 1.1, "output": 4.4 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 100000
|
||||
},
|
||||
{
|
||||
"id": "openai/o4-mini-high",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 1.1,
|
||||
"output": 4.4
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 1.1, "output": 4.4 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 100000
|
||||
},
|
||||
{
|
||||
"id": "openai/o1-pro",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 150,
|
||||
"output": 600
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 150, "output": 600 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 100000
|
||||
},
|
||||
{
|
||||
"id": "meta-llama/llama-3.3-70b-instruct",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 120,
|
||||
"output": 600
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 120, "output": 600 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 1048576
|
||||
},
|
||||
{
|
||||
"id": "meta-llama/llama-4-maverick",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.18,
|
||||
"output": 0.6
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 0.18, "output": 0.6 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 1000000
|
||||
},
|
||||
{
|
||||
"id": "meta-llama/llama-4-scout",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.08,
|
||||
"output": 0.3
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 0.08, "output": 0.3 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 1000000
|
||||
},
|
||||
{
|
||||
"id": "qwen/qwen-max",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 1.6,
|
||||
"output": 6.4
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 1.6, "output": 6.4 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 32768
|
||||
},
|
||||
{
|
||||
"id": "qwen/qwen-turbo",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.05,
|
||||
"output": 0.2
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 0.05, "output": 0.2 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 1000000
|
||||
},
|
||||
{
|
||||
"id": "qwen/qwen3-235b-a22b",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.14,
|
||||
"output": 2
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 0.14, "output": 2 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 24000
|
||||
},
|
||||
{
|
||||
"id": "mistralai/mistral-small-3.1-24b-instruct:free",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0,
|
||||
"output": 0
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 0, "output": 0 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 96000
|
||||
},
|
||||
{
|
||||
"id": "mistralai/mistral-small-3.1-24b-instruct",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.1,
|
||||
"output": 0.3
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 0.1, "output": 0.3 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 128000
|
||||
},
|
||||
{
|
||||
"id": "mistralai/devstral-small",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.1,
|
||||
"output": 0.3
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 0.1, "output": 0.3 },
|
||||
"allowed_roles": ["main"],
|
||||
"max_tokens": 110000
|
||||
},
|
||||
{
|
||||
"id": "mistralai/mistral-nemo",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0.03,
|
||||
"output": 0.07
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 0.03, "output": 0.07 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 100000
|
||||
},
|
||||
{
|
||||
"id": "thudm/glm-4-32b:free",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 0,
|
||||
"output": 0
|
||||
},
|
||||
"cost_per_1m_tokens": { "input": 0, "output": 0 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 32768
|
||||
}
|
||||
|
||||
@@ -558,14 +558,14 @@ async function addTask(
|
||||
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');
|
||||
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') {
|
||||
|
||||
@@ -5,6 +5,7 @@ import Table from 'cli-table3';
|
||||
|
||||
import { log, readJSON, writeJSON, truncate, isSilentMode } from '../utils.js';
|
||||
import { displayBanner } from '../ui.js';
|
||||
import generateTaskFiles from './generate-task-files.js';
|
||||
|
||||
/**
|
||||
* Clear subtasks from specified tasks
|
||||
@@ -98,6 +99,10 @@ function clearSubtasks(tasksPath, taskIds, context = {}) {
|
||||
console.log(summaryTable.toString());
|
||||
}
|
||||
|
||||
// Regenerate task files to reflect changes
|
||||
log('info', 'Regenerating task files...');
|
||||
generateTaskFiles(tasksPath, path.dirname(tasksPath), { projectRoot, tag });
|
||||
|
||||
// Success message
|
||||
if (!isSilentMode()) {
|
||||
console.log(
|
||||
|
||||
@@ -669,7 +669,7 @@ async function expandTask(
|
||||
|
||||
data.tasks[taskIndex] = task; // Assign the modified task back
|
||||
writeJSON(tasksPath, data);
|
||||
// await generateTaskFiles(tasksPath, path.dirname(tasksPath));
|
||||
await generateTaskFiles(tasksPath, path.dirname(tasksPath));
|
||||
|
||||
// Display AI Usage Summary for CLI
|
||||
if (
|
||||
|
||||
@@ -23,7 +23,6 @@ import {
|
||||
} from '../config-manager.js';
|
||||
import { findConfigPath } from '../../../src/utils/path-utils.js';
|
||||
import { log } from '../utils.js';
|
||||
import { CUSTOM_PROVIDERS } from '../../../src/constants/providers.js';
|
||||
|
||||
/**
|
||||
* Fetches the list of models from OpenRouter API.
|
||||
@@ -441,7 +440,7 @@ async function setModel(role, modelId, options = {}) {
|
||||
} else {
|
||||
// Either not found internally, OR found but under a DIFFERENT provider than hinted.
|
||||
// Proceed with custom logic based ONLY on the hint.
|
||||
if (providerHint === CUSTOM_PROVIDERS.OPENROUTER) {
|
||||
if (providerHint === 'openrouter') {
|
||||
// Check OpenRouter ONLY because hint was openrouter
|
||||
report('info', `Checking OpenRouter for ${modelId} (as hinted)...`);
|
||||
const openRouterModels = await fetchOpenRouterModels();
|
||||
@@ -450,7 +449,7 @@ async function setModel(role, modelId, options = {}) {
|
||||
openRouterModels &&
|
||||
openRouterModels.some((m) => m.id === modelId)
|
||||
) {
|
||||
determinedProvider = CUSTOM_PROVIDERS.OPENROUTER;
|
||||
determinedProvider = 'openrouter';
|
||||
|
||||
// Check if this is a free model (ends with :free)
|
||||
if (modelId.endsWith(':free')) {
|
||||
@@ -466,7 +465,7 @@ async function setModel(role, modelId, options = {}) {
|
||||
`Model ID "${modelId}" not found in the live OpenRouter model list. Please verify the ID and ensure it's available on OpenRouter.`
|
||||
);
|
||||
}
|
||||
} else if (providerHint === CUSTOM_PROVIDERS.OLLAMA) {
|
||||
} else if (providerHint === 'ollama') {
|
||||
// Check Ollama ONLY because hint was ollama
|
||||
report('info', `Checking Ollama for ${modelId} (as hinted)...`);
|
||||
|
||||
@@ -480,7 +479,7 @@ async function setModel(role, modelId, options = {}) {
|
||||
`Unable to connect to Ollama server at ${ollamaBaseURL}. Please ensure Ollama is running and try again.`
|
||||
);
|
||||
} else if (ollamaModels.some((m) => m.model === modelId)) {
|
||||
determinedProvider = CUSTOM_PROVIDERS.OLLAMA;
|
||||
determinedProvider = 'ollama';
|
||||
warningMessage = `Warning: Custom Ollama model '${modelId}' set. Ensure your Ollama server is running and has pulled this model. Taskmaster cannot guarantee compatibility.`;
|
||||
report('warn', warningMessage);
|
||||
} else {
|
||||
@@ -490,23 +489,13 @@ async function setModel(role, modelId, options = {}) {
|
||||
`Model ID "${modelId}" not found in the Ollama instance. Please verify the model is pulled and available. You can check available models with: curl ${tagsUrl}`
|
||||
);
|
||||
}
|
||||
} else if (providerHint === CUSTOM_PROVIDERS.BEDROCK) {
|
||||
} else if (providerHint === 'bedrock') {
|
||||
// Set provider without model validation since Bedrock models are managed by AWS
|
||||
determinedProvider = CUSTOM_PROVIDERS.BEDROCK;
|
||||
determinedProvider = 'bedrock';
|
||||
warningMessage = `Warning: Custom Bedrock model '${modelId}' set. Please ensure the model ID is valid and accessible in your AWS account.`;
|
||||
report('warn', warningMessage);
|
||||
} else if (providerHint === CUSTOM_PROVIDERS.AZURE) {
|
||||
// Set provider without model validation since Azure models are managed by Azure
|
||||
determinedProvider = CUSTOM_PROVIDERS.AZURE;
|
||||
warningMessage = `Warning: Custom Azure model '${modelId}' set. Please ensure the model deployment is valid and accessible in your Azure account.`;
|
||||
report('warn', warningMessage);
|
||||
} else if (providerHint === CUSTOM_PROVIDERS.VERTEX) {
|
||||
// Set provider without model validation since Vertex models are managed by Google Cloud
|
||||
determinedProvider = CUSTOM_PROVIDERS.VERTEX;
|
||||
warningMessage = `Warning: Custom Vertex AI model '${modelId}' set. Please ensure the model is valid and accessible in your Google Cloud project.`;
|
||||
report('warn', warningMessage);
|
||||
} else {
|
||||
// Invalid provider hint - should not happen with our constants
|
||||
// Invalid provider hint - should not happen
|
||||
throw new Error(`Invalid provider hint received: ${providerHint}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ async function moveTask(
|
||||
|
||||
// Always write the data object, never the _rawTaggedData directly
|
||||
// The writeJSON function will filter out _rawTaggedData automatically
|
||||
writeJSON(tasksPath, rawData, options.projectRoot, currentTag);
|
||||
writeJSON(tasksPath, rawData);
|
||||
|
||||
if (generateFiles) {
|
||||
await generateTaskFiles(tasksPath, path.dirname(tasksPath));
|
||||
|
||||
@@ -107,13 +107,8 @@ async function performResearch(
|
||||
let autoDiscoveredIds = [];
|
||||
|
||||
try {
|
||||
const tasksPath = path.join(
|
||||
projectRoot,
|
||||
'.taskmaster',
|
||||
'tasks',
|
||||
'tasks.json'
|
||||
);
|
||||
const tasksData = await readJSON(tasksPath, projectRoot);
|
||||
const tasksPath = path.join(projectRoot, 'tasks', 'tasks.json');
|
||||
const tasksData = await readJSON(tasksPath);
|
||||
|
||||
if (tasksData && tasksData.tasks && tasksData.tasks.length > 0) {
|
||||
// Flatten tasks to include subtasks for fuzzy search
|
||||
@@ -255,9 +250,6 @@ async function performResearch(
|
||||
const tagInfo = aiResult.tagInfo;
|
||||
|
||||
// Format and display results
|
||||
// Initialize interactive save tracking
|
||||
let interactiveSaveInfo = { interactiveSaveOccurred: false };
|
||||
|
||||
if (outputFormat === 'text') {
|
||||
displayResearchResults(
|
||||
researchResult,
|
||||
@@ -273,7 +265,7 @@ async function performResearch(
|
||||
|
||||
// Offer follow-up question option (only for initial CLI queries, not MCP)
|
||||
if (allowFollowUp && !isMCP) {
|
||||
interactiveSaveInfo = await handleFollowUpQuestions(
|
||||
await handleFollowUpQuestions(
|
||||
options,
|
||||
context,
|
||||
outputFormat,
|
||||
@@ -316,8 +308,7 @@ async function performResearch(
|
||||
detailLevel,
|
||||
telemetryData,
|
||||
tagInfo,
|
||||
savedFilePath,
|
||||
interactiveSaveOccurred: false // MCP save-to-file doesn't count as interactive save
|
||||
savedFilePath
|
||||
};
|
||||
}
|
||||
|
||||
@@ -334,9 +325,7 @@ async function performResearch(
|
||||
totalInputTokens,
|
||||
detailLevel,
|
||||
telemetryData,
|
||||
tagInfo,
|
||||
interactiveSaveOccurred:
|
||||
interactiveSaveInfo?.interactiveSaveOccurred || false
|
||||
tagInfo
|
||||
};
|
||||
} catch (error) {
|
||||
logFn.error(`Research query failed: ${error.message}`);
|
||||
@@ -654,8 +643,6 @@ async function handleFollowUpQuestions(
|
||||
initialQuery,
|
||||
initialResult
|
||||
) {
|
||||
let interactiveSaveOccurred = false;
|
||||
|
||||
try {
|
||||
// Import required modules for saving
|
||||
const { readJSON } = await import('../utils.js');
|
||||
@@ -706,15 +693,12 @@ async function handleFollowUpQuestions(
|
||||
|
||||
if (action === 'save') {
|
||||
// Handle save functionality
|
||||
const saveResult = await handleSaveToTask(
|
||||
await handleSaveToTask(
|
||||
conversationHistory,
|
||||
projectRoot,
|
||||
context,
|
||||
logFn
|
||||
);
|
||||
if (saveResult) {
|
||||
interactiveSaveOccurred = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -778,8 +762,6 @@ async function handleFollowUpQuestions(
|
||||
// silently continue without follow-up functionality
|
||||
logFn.debug(`Follow-up questions not available: ${error.message}`);
|
||||
}
|
||||
|
||||
return { interactiveSaveOccurred };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -846,10 +828,8 @@ async function handleSaveToTask(
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate ID exists - use tag from context
|
||||
const { getCurrentTag } = await import('../utils.js');
|
||||
const tag = context.tag || getCurrentTag(projectRoot) || 'master';
|
||||
const data = readJSON(tasksPath, projectRoot, tag);
|
||||
// Validate ID exists
|
||||
const data = readJSON(tasksPath, projectRoot);
|
||||
if (!data || !data.tasks) {
|
||||
console.log(chalk.red('❌ No valid tasks found.'));
|
||||
return;
|
||||
@@ -883,7 +863,7 @@ async function handleSaveToTask(
|
||||
trimmedTaskId,
|
||||
conversationThread,
|
||||
false, // useResearch = false for simple append
|
||||
{ ...context, tag },
|
||||
context,
|
||||
'text'
|
||||
);
|
||||
|
||||
@@ -910,7 +890,7 @@ async function handleSaveToTask(
|
||||
taskIdNum,
|
||||
conversationThread,
|
||||
false, // useResearch = false for simple append
|
||||
{ ...context, tag },
|
||||
context,
|
||||
'text',
|
||||
true // appendMode = true
|
||||
);
|
||||
@@ -919,12 +899,9 @@ async function handleSaveToTask(
|
||||
chalk.green(`✅ Research conversation saved to task ${trimmedTaskId}`)
|
||||
);
|
||||
}
|
||||
|
||||
return true; // Indicate successful save
|
||||
} catch (error) {
|
||||
console.log(chalk.red(`❌ Error saving conversation: ${error.message}`));
|
||||
logFn.error(`Error saving conversation: ${error.message}`);
|
||||
return false; // Indicate failed save
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,7 @@ import {
|
||||
truncate,
|
||||
isSilentMode,
|
||||
findProjectRoot,
|
||||
flattenTasksWithSubtasks,
|
||||
getCurrentTag
|
||||
flattenTasksWithSubtasks
|
||||
} from '../utils.js';
|
||||
import { generateTextService } from '../ai-services-unified.js';
|
||||
import { getDebugFlag } from '../config-manager.js';
|
||||
@@ -47,7 +46,7 @@ async function updateSubtaskById(
|
||||
context = {},
|
||||
outputFormat = context.mcpLog ? 'json' : 'text'
|
||||
) {
|
||||
const { session, mcpLog, projectRoot: providedProjectRoot, tag } = context;
|
||||
const { session, mcpLog, projectRoot: providedProjectRoot } = context;
|
||||
const logFn = mcpLog || consoleLog;
|
||||
const isMCP = !!mcpLog;
|
||||
|
||||
@@ -91,10 +90,7 @@ async function updateSubtaskById(
|
||||
throw new Error('Could not determine project root directory');
|
||||
}
|
||||
|
||||
// Determine the tag to use
|
||||
const currentTag = tag || getCurrentTag(projectRoot) || 'master';
|
||||
|
||||
const data = readJSON(tasksPath, projectRoot, currentTag);
|
||||
const data = readJSON(tasksPath, projectRoot);
|
||||
if (!data || !data.tasks) {
|
||||
throw new Error(
|
||||
`No valid tasks found in ${tasksPath}. The file may be corrupted or have an invalid format.`
|
||||
@@ -335,7 +331,7 @@ Output Requirements:
|
||||
if (outputFormat === 'text' && getDebugFlag(session)) {
|
||||
console.log('>>> DEBUG: About to call writeJSON with updated data...');
|
||||
}
|
||||
writeJSON(tasksPath, data, projectRoot, currentTag);
|
||||
writeJSON(tasksPath, data, projectRoot);
|
||||
if (outputFormat === 'text' && getDebugFlag(session)) {
|
||||
console.log('>>> DEBUG: writeJSON call completed.');
|
||||
}
|
||||
|
||||
@@ -12,8 +12,7 @@ import {
|
||||
truncate,
|
||||
isSilentMode,
|
||||
flattenTasksWithSubtasks,
|
||||
findProjectRoot,
|
||||
getCurrentTag
|
||||
findProjectRoot
|
||||
} from '../utils.js';
|
||||
|
||||
import {
|
||||
@@ -24,7 +23,11 @@ import {
|
||||
} from '../ui.js';
|
||||
|
||||
import { generateTextService } from '../ai-services-unified.js';
|
||||
import { getDebugFlag, isApiKeySet } from '../config-manager.js';
|
||||
import {
|
||||
getDebugFlag,
|
||||
isApiKeySet // Keep this check
|
||||
} from '../config-manager.js';
|
||||
import generateTaskFiles from './generate-task-files.js';
|
||||
import { ContextGatherer } from '../utils/contextGatherer.js';
|
||||
import { FuzzyTaskSearch } from '../utils/fuzzyTaskSearch.js';
|
||||
|
||||
@@ -220,7 +223,7 @@ async function updateTaskById(
|
||||
outputFormat = 'text',
|
||||
appendMode = false
|
||||
) {
|
||||
const { session, mcpLog, projectRoot: providedProjectRoot, tag } = context;
|
||||
const { session, mcpLog, projectRoot: providedProjectRoot } = context;
|
||||
const logFn = mcpLog || consoleLog;
|
||||
const isMCP = !!mcpLog;
|
||||
|
||||
@@ -265,11 +268,8 @@ async function updateTaskById(
|
||||
throw new Error('Could not determine project root directory');
|
||||
}
|
||||
|
||||
// Determine the tag to use
|
||||
const currentTag = tag || getCurrentTag(projectRoot) || 'master';
|
||||
|
||||
// --- Task Loading and Status Check (Keep existing) ---
|
||||
const data = readJSON(tasksPath, projectRoot, currentTag);
|
||||
const data = readJSON(tasksPath, projectRoot);
|
||||
if (!data || !data.tasks)
|
||||
throw new Error(`No valid tasks found in ${tasksPath}.`);
|
||||
const taskIndex = data.tasks.findIndex((task) => task.id === taskId);
|
||||
@@ -510,7 +510,7 @@ The changes described in the prompt should be thoughtfully applied to make the t
|
||||
|
||||
// Write the updated task back to file
|
||||
data.tasks[taskIndex] = taskToUpdate;
|
||||
writeJSON(tasksPath, data, projectRoot, currentTag);
|
||||
writeJSON(tasksPath, data);
|
||||
report('success', `Successfully appended to task ${taskId}`);
|
||||
|
||||
// Display success message for CLI
|
||||
@@ -624,7 +624,7 @@ The changes described in the prompt should be thoughtfully applied to make the t
|
||||
// --- End Update Task Data ---
|
||||
|
||||
// --- Write File and Generate (Unchanged) ---
|
||||
writeJSON(tasksPath, data, projectRoot, currentTag);
|
||||
writeJSON(tasksPath, data);
|
||||
report('success', `Successfully updated task ${taskId}`);
|
||||
// await generateTaskFiles(tasksPath, path.dirname(tasksPath));
|
||||
// --- End Write File ---
|
||||
|
||||
@@ -123,7 +123,7 @@ export class ContextGatherer {
|
||||
|
||||
// Dependency Graph Analysis
|
||||
if (dependencyTasks.length > 0) {
|
||||
const dependencyResults = this._buildDependencyGraphs(dependencyTasks);
|
||||
const dependencyResults = this._buildDependencyContext(dependencyTasks);
|
||||
dependencyResults.allRelatedTaskIds.forEach((id) =>
|
||||
finalTaskIds.add(String(id))
|
||||
);
|
||||
|
||||
@@ -21,10 +21,18 @@ export class BedrockAIProvider extends BaseAIProvider {
|
||||
*/
|
||||
getClient(params) {
|
||||
try {
|
||||
const credentialProvider = fromNodeProviderChain();
|
||||
const {
|
||||
profile = process.env.AWS_PROFILE || 'default',
|
||||
region = process.env.AWS_DEFAULT_REGION || 'us-east-1',
|
||||
baseURL
|
||||
} = params;
|
||||
|
||||
const credentialProvider = fromNodeProviderChain({ profile });
|
||||
|
||||
return createAmazonBedrock({
|
||||
credentialProvider
|
||||
region,
|
||||
credentialProvider,
|
||||
...(baseURL && { baseURL })
|
||||
});
|
||||
} catch (error) {
|
||||
this.handleError('client initialization', error);
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
/**
|
||||
* @typedef {'claude' | 'cline' | 'codex' | 'cursor' | 'roo' | 'trae' | 'windsurf' | 'vscode'} RulesProfile
|
||||
*/
|
||||
|
||||
/**
|
||||
* Available rule profiles for project initialization and rules command
|
||||
*
|
||||
* ⚠️ SINGLE SOURCE OF TRUTH: This is the authoritative list of all supported rule profiles.
|
||||
* This constant is used directly throughout the codebase (previously aliased as PROFILE_NAMES).
|
||||
*
|
||||
* @type {RulesProfile[]}
|
||||
* @description Defines possible rule profile sets:
|
||||
* - claude: Claude Code integration
|
||||
* - cline: Cline IDE rules
|
||||
* - codex: Codex integration
|
||||
* - cursor: Cursor IDE rules
|
||||
* - roo: Roo Code IDE rules
|
||||
* - trae: Trae IDE rules
|
||||
* - vscode: VS Code with GitHub Copilot integration
|
||||
* - windsurf: Windsurf IDE rules
|
||||
*
|
||||
* To add a new rule profile:
|
||||
* 1. Add the profile name to this array
|
||||
* 2. Create a profile file in src/profiles/{profile}.js
|
||||
* 3. Export it as {profile}Profile in src/profiles/index.js
|
||||
*/
|
||||
export const RULE_PROFILES = [
|
||||
'claude',
|
||||
'cline',
|
||||
'codex',
|
||||
'cursor',
|
||||
'roo',
|
||||
'trae',
|
||||
'vscode',
|
||||
'windsurf'
|
||||
];
|
||||
|
||||
/**
|
||||
* Centralized enum for all supported Roo agent modes
|
||||
* @type {string[]}
|
||||
* @description Available Roo Code IDE modes for rule generation
|
||||
*/
|
||||
export const ROO_MODES = [
|
||||
'architect',
|
||||
'ask',
|
||||
'orchestrator',
|
||||
'code',
|
||||
'debug',
|
||||
'test'
|
||||
];
|
||||
|
||||
/**
|
||||
* Check if a given rule profile is valid
|
||||
* @param {string} rulesProfile - The rule profile to check
|
||||
* @returns {boolean} True if the rule profile is valid, false otherwise
|
||||
*/
|
||||
export function isValidRulesProfile(rulesProfile) {
|
||||
return RULE_PROFILES.includes(rulesProfile);
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/**
|
||||
* Provider validation constants
|
||||
* Defines which providers should be validated against the supported-models.json file
|
||||
*/
|
||||
|
||||
// Providers that have predefined model lists and should be validated
|
||||
export const VALIDATED_PROVIDERS = [
|
||||
'anthropic',
|
||||
'openai',
|
||||
'google',
|
||||
'perplexity',
|
||||
'xai',
|
||||
'mistral'
|
||||
];
|
||||
|
||||
// Custom providers object for easy named access
|
||||
export const CUSTOM_PROVIDERS = {
|
||||
AZURE: 'azure',
|
||||
VERTEX: 'vertex',
|
||||
BEDROCK: 'bedrock',
|
||||
OPENROUTER: 'openrouter',
|
||||
OLLAMA: 'ollama'
|
||||
};
|
||||
|
||||
// Custom providers array (for backward compatibility and iteration)
|
||||
export const CUSTOM_PROVIDERS_ARRAY = Object.values(CUSTOM_PROVIDERS);
|
||||
|
||||
// All known providers (for reference)
|
||||
export const ALL_PROVIDERS = [
|
||||
...VALIDATED_PROVIDERS,
|
||||
...CUSTOM_PROVIDERS_ARRAY
|
||||
];
|
||||
@@ -1,25 +0,0 @@
|
||||
/**
|
||||
* @typedef {'add' | 'remove'} RulesAction
|
||||
*/
|
||||
|
||||
/**
|
||||
* Individual rules action constants
|
||||
*/
|
||||
export const RULES_ACTIONS = {
|
||||
ADD: 'add',
|
||||
REMOVE: 'remove'
|
||||
};
|
||||
|
||||
/**
|
||||
* Special rules command (not a CRUD operation)
|
||||
*/
|
||||
export const RULES_SETUP_ACTION = 'setup';
|
||||
|
||||
/**
|
||||
* Check if a given action is a valid rules action
|
||||
* @param {string} action - The action to check
|
||||
* @returns {boolean} True if the action is valid, false otherwise
|
||||
*/
|
||||
export function isValidRulesAction(action) {
|
||||
return Object.values(RULES_ACTIONS).includes(action);
|
||||
}
|
||||
@@ -1,249 +0,0 @@
|
||||
// Base profile factory for rule-transformer
|
||||
import path from 'path';
|
||||
|
||||
/**
|
||||
* Creates a standardized profile configuration for different editors
|
||||
* @param {Object} editorConfig - Editor-specific configuration
|
||||
* @param {string} editorConfig.name - Profile name (e.g., 'cursor', 'vscode')
|
||||
* @param {string} [editorConfig.displayName] - Display name for the editor (defaults to name)
|
||||
* @param {string} editorConfig.url - Editor website URL
|
||||
* @param {string} editorConfig.docsUrl - Editor documentation URL
|
||||
* @param {string} editorConfig.profileDir - Directory for profile configuration
|
||||
* @param {string} [editorConfig.rulesDir] - Directory for rules files (defaults to profileDir/rules)
|
||||
* @param {boolean} [editorConfig.mcpConfig=true] - Whether to create MCP configuration
|
||||
* @param {string} [editorConfig.mcpConfigName='mcp.json'] - Name of MCP config file
|
||||
* @param {string} [editorConfig.fileExtension='.mdc'] - Source file extension
|
||||
* @param {string} [editorConfig.targetExtension='.md'] - Target file extension
|
||||
* @param {Object} [editorConfig.toolMappings={}] - Tool name mappings
|
||||
* @param {Array} [editorConfig.customReplacements=[]] - Custom text replacements
|
||||
* @param {Object} [editorConfig.customFileMap={}] - Custom file name mappings
|
||||
* @param {boolean} [editorConfig.supportsRulesSubdirectories=false] - Whether to use taskmaster/ subdirectory for taskmaster-specific rules (only Cursor uses this by default)
|
||||
* @param {Function} [editorConfig.onAdd] - Lifecycle hook for profile addition
|
||||
* @param {Function} [editorConfig.onRemove] - Lifecycle hook for profile removal
|
||||
* @param {Function} [editorConfig.onPostConvert] - Lifecycle hook for post-conversion
|
||||
* @returns {Object} - Complete profile configuration
|
||||
*/
|
||||
export function createProfile(editorConfig) {
|
||||
const {
|
||||
name,
|
||||
displayName = name,
|
||||
url,
|
||||
docsUrl,
|
||||
profileDir,
|
||||
rulesDir = `${profileDir}/rules`,
|
||||
mcpConfig = true,
|
||||
mcpConfigName = 'mcp.json',
|
||||
fileExtension = '.mdc',
|
||||
targetExtension = '.md',
|
||||
toolMappings = {},
|
||||
customReplacements = [],
|
||||
customFileMap = {},
|
||||
supportsRulesSubdirectories = false,
|
||||
onAdd,
|
||||
onRemove,
|
||||
onPostConvert
|
||||
} = editorConfig;
|
||||
|
||||
const mcpConfigPath = `${profileDir}/${mcpConfigName}`;
|
||||
|
||||
// Standard file mapping with custom overrides
|
||||
// Use taskmaster subdirectory only if profile supports it
|
||||
const taskmasterPrefix = supportsRulesSubdirectories ? 'taskmaster/' : '';
|
||||
const defaultFileMap = {
|
||||
'cursor_rules.mdc': `${name.toLowerCase()}_rules${targetExtension}`,
|
||||
'dev_workflow.mdc': `${taskmasterPrefix}dev_workflow${targetExtension}`,
|
||||
'self_improve.mdc': `self_improve${targetExtension}`,
|
||||
'taskmaster.mdc': `${taskmasterPrefix}taskmaster${targetExtension}`
|
||||
};
|
||||
|
||||
const fileMap = { ...defaultFileMap, ...customFileMap };
|
||||
|
||||
// Base global replacements that work for all editors
|
||||
const baseGlobalReplacements = [
|
||||
// Handle URLs in any context
|
||||
{ from: /cursor\.so/gi, to: url },
|
||||
{ from: /cursor\s*\.\s*so/gi, to: url },
|
||||
{ from: /https?:\/\/cursor\.so/gi, to: `https://${url}` },
|
||||
{ from: /https?:\/\/www\.cursor\.so/gi, to: `https://www.${url}` },
|
||||
|
||||
// Handle tool references
|
||||
{ from: /\bedit_file\b/gi, to: toolMappings.edit_file || 'edit_file' },
|
||||
{
|
||||
from: /\bsearch tool\b/gi,
|
||||
to: `${toolMappings.search || 'search'} tool`
|
||||
},
|
||||
{ from: /\bSearch Tool\b/g, to: `${toolMappings.search || 'Search'} Tool` },
|
||||
|
||||
// Handle basic terms with proper case handling
|
||||
{
|
||||
from: /\bcursor\b/gi,
|
||||
to: (match) =>
|
||||
match.charAt(0) === 'C' ? displayName : name.toLowerCase()
|
||||
},
|
||||
{ from: /Cursor/g, to: displayName },
|
||||
{ from: /CURSOR/g, to: displayName.toUpperCase() },
|
||||
|
||||
// Handle file extensions if different
|
||||
...(targetExtension !== fileExtension
|
||||
? [
|
||||
{
|
||||
from: new RegExp(`\\${fileExtension}(?!\\])\\b`, 'g'),
|
||||
to: targetExtension
|
||||
}
|
||||
]
|
||||
: []),
|
||||
|
||||
// Handle documentation URLs
|
||||
{ from: /docs\.cursor\.com/gi, to: docsUrl },
|
||||
|
||||
// Custom editor-specific replacements
|
||||
...customReplacements
|
||||
];
|
||||
|
||||
// Standard tool mappings
|
||||
const defaultToolMappings = {
|
||||
search: 'search',
|
||||
read_file: 'read_file',
|
||||
edit_file: 'edit_file',
|
||||
create_file: 'create_file',
|
||||
run_command: 'run_command',
|
||||
terminal_command: 'terminal_command',
|
||||
use_mcp: 'use_mcp',
|
||||
switch_mode: 'switch_mode',
|
||||
...toolMappings
|
||||
};
|
||||
|
||||
// Create conversion config
|
||||
const conversionConfig = {
|
||||
// Profile name replacements
|
||||
profileTerms: [
|
||||
{ from: /cursor\.so/g, to: url },
|
||||
{ from: /\[cursor\.so\]/g, to: `[${url}]` },
|
||||
{ from: /href="https:\/\/cursor\.so/g, to: `href="https://${url}` },
|
||||
{ from: /\(https:\/\/cursor\.so/g, to: `(https://${url}` },
|
||||
{
|
||||
from: /\bcursor\b/gi,
|
||||
to: (match) => (match === 'Cursor' ? displayName : name.toLowerCase())
|
||||
},
|
||||
{ from: /Cursor/g, to: displayName }
|
||||
],
|
||||
|
||||
// File extension replacements
|
||||
fileExtensions:
|
||||
targetExtension !== fileExtension
|
||||
? [
|
||||
{
|
||||
from: new RegExp(`\\${fileExtension}\\b`, 'g'),
|
||||
to: targetExtension
|
||||
}
|
||||
]
|
||||
: [],
|
||||
|
||||
// Documentation URL replacements
|
||||
docUrls: [
|
||||
{
|
||||
from: new RegExp(`https:\\/\\/docs\\.cursor\\.com\\/[^\\s)'\"]+`, 'g'),
|
||||
to: (match) => match.replace('docs.cursor.com', docsUrl)
|
||||
},
|
||||
{
|
||||
from: new RegExp(`https:\\/\\/${docsUrl}\\/`, 'g'),
|
||||
to: `https://${docsUrl}/`
|
||||
}
|
||||
],
|
||||
|
||||
// Tool references - direct replacements
|
||||
toolNames: defaultToolMappings,
|
||||
|
||||
// Tool references in context - more specific replacements
|
||||
toolContexts: Object.entries(defaultToolMappings).flatMap(
|
||||
([original, mapped]) => [
|
||||
{
|
||||
from: new RegExp(`\\b${original} tool\\b`, 'g'),
|
||||
to: `${mapped} tool`
|
||||
},
|
||||
{ from: new RegExp(`\\bthe ${original}\\b`, 'g'), to: `the ${mapped}` },
|
||||
{ from: new RegExp(`\\bThe ${original}\\b`, 'g'), to: `The ${mapped}` },
|
||||
{
|
||||
from: new RegExp(`\\bCursor ${original}\\b`, 'g'),
|
||||
to: `${displayName} ${mapped}`
|
||||
}
|
||||
]
|
||||
),
|
||||
|
||||
// Tool group and category names
|
||||
toolGroups: [
|
||||
{ from: /\bSearch tools\b/g, to: 'Read Group tools' },
|
||||
{ from: /\bEdit tools\b/g, to: 'Edit Group tools' },
|
||||
{ from: /\bRun tools\b/g, to: 'Command Group tools' },
|
||||
{ from: /\bMCP servers\b/g, to: 'MCP Group tools' },
|
||||
{ from: /\bSearch Group\b/g, to: 'Read Group' },
|
||||
{ from: /\bEdit Group\b/g, to: 'Edit Group' },
|
||||
{ from: /\bRun Group\b/g, to: 'Command Group' }
|
||||
],
|
||||
|
||||
// File references in markdown links
|
||||
fileReferences: {
|
||||
pathPattern: /\[(.+?)\]\(mdc:\.cursor\/rules\/(.+?)\.mdc\)/g,
|
||||
replacement: (match, text, filePath) => {
|
||||
const baseName = path.basename(filePath, '.mdc');
|
||||
const newFileName =
|
||||
fileMap[`${baseName}.mdc`] || `${baseName}${targetExtension}`;
|
||||
// Update the link text to match the new filename (strip directory path for display)
|
||||
const newLinkText = path.basename(newFileName);
|
||||
// For Cursor, keep the mdc: protocol; for others, use standard relative paths
|
||||
if (name.toLowerCase() === 'cursor') {
|
||||
return `[${newLinkText}](mdc:${rulesDir}/${newFileName})`;
|
||||
} else {
|
||||
return `[${newLinkText}](${rulesDir}/${newFileName})`;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function getTargetRuleFilename(sourceFilename) {
|
||||
if (fileMap[sourceFilename]) {
|
||||
return fileMap[sourceFilename];
|
||||
}
|
||||
return targetExtension !== fileExtension
|
||||
? sourceFilename.replace(
|
||||
new RegExp(`\\${fileExtension}$`),
|
||||
targetExtension
|
||||
)
|
||||
: sourceFilename;
|
||||
}
|
||||
|
||||
return {
|
||||
profileName: name, // Use name for programmatic access (tests expect this)
|
||||
displayName: displayName, // Keep displayName for UI purposes
|
||||
profileDir,
|
||||
rulesDir,
|
||||
mcpConfig,
|
||||
mcpConfigName,
|
||||
mcpConfigPath,
|
||||
supportsRulesSubdirectories,
|
||||
fileMap,
|
||||
globalReplacements: baseGlobalReplacements,
|
||||
conversionConfig,
|
||||
getTargetRuleFilename,
|
||||
// Optional lifecycle hooks
|
||||
...(onAdd && { onAddRulesProfile: onAdd }),
|
||||
...(onRemove && { onRemoveRulesProfile: onRemove }),
|
||||
...(onPostConvert && { onPostConvertRulesProfile: onPostConvert })
|
||||
};
|
||||
}
|
||||
|
||||
// Common tool mappings for editors that share similar tool sets
|
||||
export const COMMON_TOOL_MAPPINGS = {
|
||||
// Most editors (Cursor, Cline, Windsurf) keep original tool names
|
||||
STANDARD: {},
|
||||
|
||||
// Roo Code uses different tool names
|
||||
ROO_STYLE: {
|
||||
edit_file: 'apply_diff',
|
||||
search: 'search_files',
|
||||
create_file: 'write_to_file',
|
||||
run_command: 'execute_command',
|
||||
terminal_command: 'execute_command',
|
||||
use_mcp: 'use_mcp_tool'
|
||||
}
|
||||
};
|
||||
@@ -1,59 +0,0 @@
|
||||
// Claude Code profile for rule-transformer
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { isSilentMode, log } from '../../scripts/modules/utils.js';
|
||||
|
||||
// Lifecycle functions for Claude Code profile
|
||||
function onAddRulesProfile(targetDir, assetsDir) {
|
||||
// Use the provided assets directory to find the source file
|
||||
const sourceFile = path.join(assetsDir, 'AGENTS.md');
|
||||
const destFile = path.join(targetDir, 'CLAUDE.md');
|
||||
|
||||
if (fs.existsSync(sourceFile)) {
|
||||
try {
|
||||
fs.copyFileSync(sourceFile, destFile);
|
||||
log('debug', `[Claude] Copied AGENTS.md to ${destFile}`);
|
||||
} catch (err) {
|
||||
log('error', `[Claude] Failed to copy AGENTS.md: ${err.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onRemoveRulesProfile(targetDir) {
|
||||
const claudeFile = path.join(targetDir, 'CLAUDE.md');
|
||||
if (fs.existsSync(claudeFile)) {
|
||||
try {
|
||||
fs.rmSync(claudeFile, { force: true });
|
||||
log('debug', `[Claude] Removed CLAUDE.md from ${claudeFile}`);
|
||||
} catch (err) {
|
||||
log('error', `[Claude] Failed to remove CLAUDE.md: ${err.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onPostConvertRulesProfile(targetDir, assetsDir) {
|
||||
onAddRulesProfile(targetDir, assetsDir);
|
||||
}
|
||||
|
||||
// Simple filename function
|
||||
function getTargetRuleFilename(sourceFilename) {
|
||||
return sourceFilename;
|
||||
}
|
||||
|
||||
// Simple profile configuration - bypasses base-profile system
|
||||
export const claudeProfile = {
|
||||
profileName: 'claude',
|
||||
displayName: 'Claude Code',
|
||||
profileDir: '.', // Root directory
|
||||
rulesDir: '.', // No rules directory needed
|
||||
mcpConfig: false, // No MCP config needed
|
||||
mcpConfigName: null,
|
||||
mcpConfigPath: null,
|
||||
conversionConfig: {},
|
||||
fileMap: {},
|
||||
globalReplacements: [],
|
||||
getTargetRuleFilename,
|
||||
onAddRulesProfile,
|
||||
onRemoveRulesProfile,
|
||||
onPostConvertRulesProfile
|
||||
};
|
||||
@@ -1,20 +0,0 @@
|
||||
// Cline conversion profile for rule-transformer
|
||||
import { createProfile, COMMON_TOOL_MAPPINGS } from './base-profile.js';
|
||||
|
||||
// Create and export cline profile using the base factory
|
||||
export const clineProfile = createProfile({
|
||||
name: 'cline',
|
||||
displayName: 'Cline',
|
||||
url: 'cline.bot',
|
||||
docsUrl: 'docs.cline.bot',
|
||||
profileDir: '.clinerules',
|
||||
rulesDir: '.clinerules',
|
||||
mcpConfig: false,
|
||||
mcpConfigName: 'cline_mcp_settings.json',
|
||||
fileExtension: '.mdc',
|
||||
targetExtension: '.md',
|
||||
toolMappings: COMMON_TOOL_MAPPINGS.STANDARD, // Cline uses standard tool names
|
||||
customFileMap: {
|
||||
'cursor_rules.mdc': 'cline_rules.md'
|
||||
}
|
||||
});
|
||||
@@ -1,59 +0,0 @@
|
||||
// Codex profile for rule-transformer
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { isSilentMode, log } from '../../scripts/modules/utils.js';
|
||||
|
||||
// Lifecycle functions for Codex profile
|
||||
function onAddRulesProfile(targetDir, assetsDir) {
|
||||
// Use the provided assets directory to find the source file
|
||||
const sourceFile = path.join(assetsDir, 'AGENTS.md');
|
||||
const destFile = path.join(targetDir, 'AGENTS.md');
|
||||
|
||||
if (fs.existsSync(sourceFile)) {
|
||||
try {
|
||||
fs.copyFileSync(sourceFile, destFile);
|
||||
log('debug', `[Codex] Copied AGENTS.md to ${destFile}`);
|
||||
} catch (err) {
|
||||
log('error', `[Codex] Failed to copy AGENTS.md: ${err.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onRemoveRulesProfile(targetDir) {
|
||||
const agentsFile = path.join(targetDir, 'AGENTS.md');
|
||||
if (fs.existsSync(agentsFile)) {
|
||||
try {
|
||||
fs.rmSync(agentsFile, { force: true });
|
||||
log('debug', `[Codex] Removed AGENTS.md from ${agentsFile}`);
|
||||
} catch (err) {
|
||||
log('error', `[Codex] Failed to remove AGENTS.md: ${err.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onPostConvertRulesProfile(targetDir, assetsDir) {
|
||||
onAddRulesProfile(targetDir, assetsDir);
|
||||
}
|
||||
|
||||
// Simple filename function
|
||||
function getTargetRuleFilename(sourceFilename) {
|
||||
return sourceFilename;
|
||||
}
|
||||
|
||||
// Simple profile configuration - bypasses base-profile system
|
||||
export const codexProfile = {
|
||||
profileName: 'codex',
|
||||
displayName: 'Codex',
|
||||
profileDir: '.', // Root directory
|
||||
rulesDir: '.', // No rules directory needed
|
||||
mcpConfig: false, // No MCP config needed
|
||||
mcpConfigName: null,
|
||||
mcpConfigPath: null,
|
||||
conversionConfig: {},
|
||||
fileMap: {},
|
||||
globalReplacements: [],
|
||||
getTargetRuleFilename,
|
||||
onAddRulesProfile,
|
||||
onRemoveRulesProfile,
|
||||
onPostConvertRulesProfile
|
||||
};
|
||||
@@ -1,21 +0,0 @@
|
||||
// Cursor conversion profile for rule-transformer
|
||||
import { createProfile, COMMON_TOOL_MAPPINGS } from './base-profile.js';
|
||||
|
||||
// Create and export cursor profile using the base factory
|
||||
export const cursorProfile = createProfile({
|
||||
name: 'cursor',
|
||||
displayName: 'Cursor',
|
||||
url: 'cursor.so',
|
||||
docsUrl: 'docs.cursor.com',
|
||||
profileDir: '.cursor',
|
||||
rulesDir: '.cursor/rules',
|
||||
mcpConfig: true,
|
||||
mcpConfigName: 'mcp.json',
|
||||
fileExtension: '.mdc',
|
||||
targetExtension: '.mdc', // Cursor keeps .mdc extension
|
||||
toolMappings: COMMON_TOOL_MAPPINGS.STANDARD,
|
||||
supportsRulesSubdirectories: true,
|
||||
customFileMap: {
|
||||
'cursor_rules.mdc': 'cursor_rules.mdc' // Keep the same name for cursor
|
||||
}
|
||||
});
|
||||
@@ -1,9 +0,0 @@
|
||||
// Profile exports for centralized importing
|
||||
export { claudeProfile } from './claude.js';
|
||||
export { clineProfile } from './cline.js';
|
||||
export { codexProfile } from './codex.js';
|
||||
export { cursorProfile } from './cursor.js';
|
||||
export { rooProfile } from './roo.js';
|
||||
export { traeProfile } from './trae.js';
|
||||
export { vscodeProfile } from './vscode.js';
|
||||
export { windsurfProfile } from './windsurf.js';
|
||||
@@ -1,129 +0,0 @@
|
||||
// Roo Code conversion profile for rule-transformer
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { isSilentMode, log } from '../../scripts/modules/utils.js';
|
||||
import { createProfile, COMMON_TOOL_MAPPINGS } from './base-profile.js';
|
||||
import { ROO_MODES } from '../constants/profiles.js';
|
||||
|
||||
// Lifecycle functions for Roo profile
|
||||
function onAddRulesProfile(targetDir, assetsDir) {
|
||||
// Use the provided assets directory to find the roocode directory
|
||||
const sourceDir = path.join(assetsDir, 'roocode');
|
||||
|
||||
if (!fs.existsSync(sourceDir)) {
|
||||
log('error', `[Roo] Source directory does not exist: ${sourceDir}`);
|
||||
return;
|
||||
}
|
||||
|
||||
copyRecursiveSync(sourceDir, targetDir);
|
||||
log('debug', `[Roo] Copied roocode directory to ${targetDir}`);
|
||||
|
||||
const rooModesDir = path.join(sourceDir, '.roo');
|
||||
|
||||
// Copy .roomodes to project root
|
||||
const roomodesSrc = path.join(sourceDir, '.roomodes');
|
||||
const roomodesDest = path.join(targetDir, '.roomodes');
|
||||
if (fs.existsSync(roomodesSrc)) {
|
||||
try {
|
||||
fs.copyFileSync(roomodesSrc, roomodesDest);
|
||||
log('debug', `[Roo] Copied .roomodes to ${roomodesDest}`);
|
||||
} catch (err) {
|
||||
log('error', `[Roo] Failed to copy .roomodes: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
for (const mode of ROO_MODES) {
|
||||
const src = path.join(rooModesDir, `rules-${mode}`, `${mode}-rules`);
|
||||
const dest = path.join(targetDir, '.roo', `rules-${mode}`, `${mode}-rules`);
|
||||
if (fs.existsSync(src)) {
|
||||
try {
|
||||
const destDir = path.dirname(dest);
|
||||
if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });
|
||||
fs.copyFileSync(src, dest);
|
||||
log('debug', `[Roo] Copied ${mode}-rules to ${dest}`);
|
||||
} catch (err) {
|
||||
log('error', `[Roo] Failed to copy ${src} to ${dest}: ${err.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function copyRecursiveSync(src, dest) {
|
||||
const exists = fs.existsSync(src);
|
||||
const stats = exists && fs.statSync(src);
|
||||
const isDirectory = exists && stats.isDirectory();
|
||||
if (isDirectory) {
|
||||
if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
|
||||
fs.readdirSync(src).forEach((childItemName) => {
|
||||
copyRecursiveSync(
|
||||
path.join(src, childItemName),
|
||||
path.join(dest, childItemName)
|
||||
);
|
||||
});
|
||||
} else {
|
||||
fs.copyFileSync(src, dest);
|
||||
}
|
||||
}
|
||||
|
||||
function onRemoveRulesProfile(targetDir) {
|
||||
const roomodesPath = path.join(targetDir, '.roomodes');
|
||||
if (fs.existsSync(roomodesPath)) {
|
||||
try {
|
||||
fs.rmSync(roomodesPath, { force: true });
|
||||
log('debug', `[Roo] Removed .roomodes from ${roomodesPath}`);
|
||||
} catch (err) {
|
||||
log('error', `[Roo] Failed to remove .roomodes: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
const rooDir = path.join(targetDir, '.roo');
|
||||
if (fs.existsSync(rooDir)) {
|
||||
fs.readdirSync(rooDir).forEach((entry) => {
|
||||
if (entry.startsWith('rules-')) {
|
||||
const modeDir = path.join(rooDir, entry);
|
||||
try {
|
||||
fs.rmSync(modeDir, { recursive: true, force: true });
|
||||
log('debug', `[Roo] Removed ${entry} directory from ${modeDir}`);
|
||||
} catch (err) {
|
||||
log('error', `[Roo] Failed to remove ${modeDir}: ${err.message}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (fs.readdirSync(rooDir).length === 0) {
|
||||
try {
|
||||
fs.rmSync(rooDir, { recursive: true, force: true });
|
||||
log('debug', `[Roo] Removed empty .roo directory from ${rooDir}`);
|
||||
} catch (err) {
|
||||
log('error', `[Roo] Failed to remove .roo directory: ${err.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onPostConvertRulesProfile(targetDir, assetsDir) {
|
||||
onAddRulesProfile(targetDir, assetsDir);
|
||||
}
|
||||
|
||||
// Create and export roo profile using the base factory
|
||||
export const rooProfile = createProfile({
|
||||
name: 'roo',
|
||||
displayName: 'Roo Code',
|
||||
url: 'roocode.com',
|
||||
docsUrl: 'docs.roocode.com',
|
||||
profileDir: '.roo',
|
||||
rulesDir: '.roo/rules',
|
||||
mcpConfig: true,
|
||||
mcpConfigName: 'mcp.json',
|
||||
fileExtension: '.mdc',
|
||||
targetExtension: '.md',
|
||||
toolMappings: COMMON_TOOL_MAPPINGS.ROO_STYLE,
|
||||
customFileMap: {
|
||||
'cursor_rules.mdc': 'roo_rules.md'
|
||||
},
|
||||
onAdd: onAddRulesProfile,
|
||||
onRemove: onRemoveRulesProfile,
|
||||
onPostConvert: onPostConvertRulesProfile
|
||||
});
|
||||
|
||||
// Export lifecycle functions separately to avoid naming conflicts
|
||||
export { onAddRulesProfile, onRemoveRulesProfile, onPostConvertRulesProfile };
|
||||
@@ -1,17 +0,0 @@
|
||||
// Trae conversion profile for rule-transformer
|
||||
import { createProfile, COMMON_TOOL_MAPPINGS } from './base-profile.js';
|
||||
|
||||
// Create and export trae profile using the base factory
|
||||
export const traeProfile = createProfile({
|
||||
name: 'trae',
|
||||
displayName: 'Trae',
|
||||
url: 'trae.ai',
|
||||
docsUrl: 'docs.trae.ai',
|
||||
profileDir: '.trae',
|
||||
rulesDir: '.trae/rules',
|
||||
mcpConfig: false,
|
||||
mcpConfigName: 'trae_mcp_settings.json',
|
||||
fileExtension: '.mdc',
|
||||
targetExtension: '.md',
|
||||
toolMappings: COMMON_TOOL_MAPPINGS.STANDARD // Trae uses standard tool names
|
||||
});
|
||||
@@ -1,41 +0,0 @@
|
||||
// VS Code conversion profile for rule-transformer
|
||||
import { createProfile, COMMON_TOOL_MAPPINGS } from './base-profile.js';
|
||||
|
||||
// Create and export vscode profile using the base factory
|
||||
export const vscodeProfile = createProfile({
|
||||
name: 'vscode',
|
||||
displayName: 'VS Code',
|
||||
url: 'code.visualstudio.com',
|
||||
docsUrl: 'code.visualstudio.com/docs',
|
||||
profileDir: '.vscode', // MCP config location
|
||||
rulesDir: '.github/instructions', // VS Code instructions location
|
||||
mcpConfig: true,
|
||||
mcpConfigName: 'mcp.json',
|
||||
fileExtension: '.mdc',
|
||||
targetExtension: '.md',
|
||||
toolMappings: COMMON_TOOL_MAPPINGS.STANDARD, // VS Code uses standard tool names
|
||||
customFileMap: {
|
||||
'cursor_rules.mdc': 'vscode_rules.md' // Rename cursor_rules to vscode_rules
|
||||
},
|
||||
customReplacements: [
|
||||
// Core VS Code directory structure changes
|
||||
{ from: /\.cursor\/rules/g, to: '.github/instructions' },
|
||||
{ from: /\.cursor\/mcp\.json/g, to: '.vscode/mcp.json' },
|
||||
|
||||
// Fix any remaining vscode/rules references that might be created during transformation
|
||||
{ from: /\.vscode\/rules/g, to: '.github/instructions' },
|
||||
|
||||
// VS Code custom instructions format - use applyTo with quoted patterns instead of globs
|
||||
{ from: /^globs:\s*(.+)$/gm, to: 'applyTo: "$1"' },
|
||||
|
||||
// Essential markdown link transformations for VS Code structure
|
||||
{
|
||||
from: /\[(.+?)\]\(mdc:\.cursor\/rules\/(.+?)\.mdc\)/g,
|
||||
to: '[$1](.github/instructions/$2.md)'
|
||||
},
|
||||
|
||||
// VS Code specific terminology
|
||||
{ from: /rules directory/g, to: 'instructions directory' },
|
||||
{ from: /cursor rules/gi, to: 'VS Code instructions' }
|
||||
]
|
||||
});
|
||||
@@ -1,17 +0,0 @@
|
||||
// Windsurf conversion profile for rule-transformer
|
||||
import { createProfile, COMMON_TOOL_MAPPINGS } from './base-profile.js';
|
||||
|
||||
// Create and export windsurf profile using the base factory
|
||||
export const windsurfProfile = createProfile({
|
||||
name: 'windsurf',
|
||||
displayName: 'Windsurf',
|
||||
url: 'windsurf.com',
|
||||
docsUrl: 'docs.windsurf.com',
|
||||
profileDir: '.windsurf',
|
||||
rulesDir: '.windsurf/rules',
|
||||
mcpConfig: true,
|
||||
mcpConfigName: 'mcp.json',
|
||||
fileExtension: '.mdc',
|
||||
targetExtension: '.md',
|
||||
toolMappings: COMMON_TOOL_MAPPINGS.STANDARD // Windsurf uses standard tool names
|
||||
});
|
||||
@@ -1,100 +0,0 @@
|
||||
import chalk from 'chalk';
|
||||
import boxen from 'boxen';
|
||||
|
||||
/**
|
||||
* Confirm removing profile rules (destructive operation)
|
||||
* @param {string[]} profiles - Array of profile names to remove
|
||||
* @returns {Promise<boolean>} - Promise resolving to true if user confirms, false otherwise
|
||||
*/
|
||||
async function confirmProfilesRemove(profiles) {
|
||||
const profileList = profiles
|
||||
.map((b) => b.charAt(0).toUpperCase() + b.slice(1))
|
||||
.join(', ');
|
||||
console.log(
|
||||
boxen(
|
||||
chalk.yellow(
|
||||
`WARNING: This will selectively remove Task Master components for: ${profileList}.
|
||||
|
||||
What will be removed:
|
||||
• Task Master specific rule files (e.g., cursor_rules.mdc, taskmaster.mdc, etc.)
|
||||
• Task Master MCP server configuration (if no other MCP servers exist)
|
||||
|
||||
What will be preserved:
|
||||
• Your existing custom rule files
|
||||
• Other MCP server configurations
|
||||
• The profile directory itself (unless completely empty after removal)
|
||||
|
||||
The .[profile] directory will only be removed if ALL of the following are true:
|
||||
• All rules in the directory were Task Master rules (no custom rules)
|
||||
• No other files or folders exist in the profile directory
|
||||
• The MCP configuration was completely removed (no other servers)
|
||||
|
||||
Are you sure you want to proceed?`
|
||||
),
|
||||
{ padding: 1, borderColor: 'yellow', borderStyle: 'round' }
|
||||
)
|
||||
);
|
||||
const inquirer = await import('inquirer');
|
||||
const { confirm } = await inquirer.default.prompt([
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
message: 'Type y to confirm selective removal, or n to abort:',
|
||||
default: false
|
||||
}
|
||||
]);
|
||||
return confirm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm removing ALL remaining profile rules (extremely critical operation)
|
||||
* @param {string[]} profiles - Array of profile names to remove
|
||||
* @param {string[]} remainingProfiles - Array of profiles that would be left after removal
|
||||
* @returns {Promise<boolean>} - Promise resolving to true if user confirms, false otherwise
|
||||
*/
|
||||
async function confirmRemoveAllRemainingProfiles(profiles, remainingProfiles) {
|
||||
const profileList = profiles
|
||||
.map((p) => p.charAt(0).toUpperCase() + p.slice(1))
|
||||
.join(', ');
|
||||
|
||||
console.log(
|
||||
boxen(
|
||||
chalk.red.bold(
|
||||
`⚠️ CRITICAL WARNING: REMOVING ALL TASK MASTER RULE PROFILES ⚠️\n\n` +
|
||||
`You are about to remove Task Master components for: ${profileList}\n` +
|
||||
`This will leave your project with NO Task Master rule profiles remaining!\n\n` +
|
||||
`What will be removed:\n` +
|
||||
`• All Task Master specific rule files\n` +
|
||||
`• Task Master MCP server configurations\n` +
|
||||
`• Profile directories (only if completely empty after removal)\n\n` +
|
||||
`What will be preserved:\n` +
|
||||
`• Your existing custom rule files\n` +
|
||||
`• Other MCP server configurations\n` +
|
||||
`• Profile directories with custom content\n\n` +
|
||||
`This could impact Task Master functionality but will preserve your custom configurations.\n\n` +
|
||||
`Are you absolutely sure you want to proceed?`
|
||||
),
|
||||
{
|
||||
padding: 1,
|
||||
borderColor: 'red',
|
||||
borderStyle: 'double',
|
||||
title: '🚨 CRITICAL OPERATION',
|
||||
titleAlignment: 'center'
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
const inquirer = await import('inquirer');
|
||||
const { confirm } = await inquirer.default.prompt([
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
message:
|
||||
'Type y to confirm removing ALL Task Master rule profiles, or n to abort:',
|
||||
default: false
|
||||
}
|
||||
]);
|
||||
return confirm;
|
||||
}
|
||||
|
||||
export { confirmProfilesRemove, confirmRemoveAllRemainingProfiles };
|
||||
@@ -1,264 +0,0 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { log } from '../../scripts/modules/utils.js';
|
||||
|
||||
// Return JSON with existing mcp.json formatting style
|
||||
function formatJSONWithTabs(obj) {
|
||||
let json = JSON.stringify(obj, null, '\t');
|
||||
|
||||
json = json.replace(
|
||||
/(\[\n\t+)([^[\]]+?)(\n\t+\])/g,
|
||||
(match, openBracket, content, closeBracket) => {
|
||||
// Only convert to single line if content doesn't contain nested objects/arrays
|
||||
if (!content.includes('{') && !content.includes('[')) {
|
||||
const singleLineContent = content
|
||||
.replace(/\n\t+/g, ' ')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim();
|
||||
return `[${singleLineContent}]`;
|
||||
}
|
||||
return match;
|
||||
}
|
||||
);
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
// Structure matches project conventions (see scripts/init.js)
|
||||
export function setupMCPConfiguration(projectDir, mcpConfigPath) {
|
||||
// Handle null mcpConfigPath (e.g., for Claude/Codex profiles)
|
||||
if (!mcpConfigPath) {
|
||||
log(
|
||||
'debug',
|
||||
'[MCP Config] No mcpConfigPath provided, skipping MCP configuration setup'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Build the full path to the MCP config file
|
||||
const mcpPath = path.join(projectDir, mcpConfigPath);
|
||||
const configDir = path.dirname(mcpPath);
|
||||
|
||||
log('info', `Setting up MCP configuration at ${mcpPath}...`);
|
||||
|
||||
// New MCP config to be added - references the installed package
|
||||
const newMCPServer = {
|
||||
'task-master-ai': {
|
||||
command: 'npx',
|
||||
args: ['-y', '--package=task-master-ai', 'task-master-ai'],
|
||||
env: {
|
||||
ANTHROPIC_API_KEY: 'ANTHROPIC_API_KEY_HERE',
|
||||
PERPLEXITY_API_KEY: 'PERPLEXITY_API_KEY_HERE',
|
||||
OPENAI_API_KEY: 'OPENAI_API_KEY_HERE',
|
||||
GOOGLE_API_KEY: 'GOOGLE_API_KEY_HERE',
|
||||
XAI_API_KEY: 'XAI_API_KEY_HERE',
|
||||
OPENROUTER_API_KEY: 'OPENROUTER_API_KEY_HERE',
|
||||
MISTRAL_API_KEY: 'MISTRAL_API_KEY_HERE',
|
||||
AZURE_OPENAI_API_KEY: 'AZURE_OPENAI_API_KEY_HERE',
|
||||
OLLAMA_API_KEY: 'OLLAMA_API_KEY_HERE'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Create config directory if it doesn't exist
|
||||
if (!fs.existsSync(configDir)) {
|
||||
fs.mkdirSync(configDir, { recursive: true });
|
||||
}
|
||||
|
||||
if (fs.existsSync(mcpPath)) {
|
||||
log(
|
||||
'info',
|
||||
'MCP configuration file already exists, checking for existing task-master-ai...'
|
||||
);
|
||||
try {
|
||||
// Read existing config
|
||||
const mcpConfig = JSON.parse(fs.readFileSync(mcpPath, 'utf8'));
|
||||
// Initialize mcpServers if it doesn't exist
|
||||
if (!mcpConfig.mcpServers) {
|
||||
mcpConfig.mcpServers = {};
|
||||
}
|
||||
// Check if any existing server configuration already has task-master-ai in its args
|
||||
const hasMCPString = Object.values(mcpConfig.mcpServers).some(
|
||||
(server) =>
|
||||
server.args &&
|
||||
Array.isArray(server.args) &&
|
||||
server.args.some(
|
||||
(arg) => typeof arg === 'string' && arg.includes('task-master-ai')
|
||||
)
|
||||
);
|
||||
if (hasMCPString) {
|
||||
log(
|
||||
'info',
|
||||
'Found existing task-master-ai MCP configuration in mcp.json, leaving untouched'
|
||||
);
|
||||
return; // Exit early, don't modify the existing configuration
|
||||
}
|
||||
// Add the task-master-ai server if it doesn't exist
|
||||
if (!mcpConfig.mcpServers['task-master-ai']) {
|
||||
mcpConfig.mcpServers['task-master-ai'] = newMCPServer['task-master-ai'];
|
||||
log(
|
||||
'info',
|
||||
'Added task-master-ai server to existing MCP configuration'
|
||||
);
|
||||
} else {
|
||||
log('info', 'task-master-ai server already configured in mcp.json');
|
||||
}
|
||||
// Write the updated configuration
|
||||
fs.writeFileSync(mcpPath, formatJSONWithTabs(mcpConfig) + '\n');
|
||||
log('success', 'Updated MCP configuration file');
|
||||
} catch (error) {
|
||||
log('error', `Failed to update MCP configuration: ${error.message}`);
|
||||
// Create a backup before potentially modifying
|
||||
const backupPath = `${mcpPath}.backup-${Date.now()}`;
|
||||
if (fs.existsSync(mcpPath)) {
|
||||
fs.copyFileSync(mcpPath, backupPath);
|
||||
log('info', `Created backup of existing mcp.json at ${backupPath}`);
|
||||
}
|
||||
// Create new configuration
|
||||
const newMCPConfig = {
|
||||
mcpServers: newMCPServer
|
||||
};
|
||||
fs.writeFileSync(mcpPath, formatJSONWithTabs(newMCPConfig) + '\n');
|
||||
log(
|
||||
'warn',
|
||||
'Created new MCP configuration file (backup of original file was created if it existed)'
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// If mcp.json doesn't exist, create it
|
||||
const newMCPConfig = {
|
||||
mcpServers: newMCPServer
|
||||
};
|
||||
fs.writeFileSync(mcpPath, formatJSONWithTabs(newMCPConfig) + '\n');
|
||||
log('success', `Created MCP configuration file at ${mcpPath}`);
|
||||
}
|
||||
|
||||
// Add note to console about MCP integration
|
||||
log('info', 'MCP server will use the installed task-master-ai package');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Task Master MCP server configuration from an existing mcp.json file
|
||||
* Only removes Task Master entries, preserving other MCP servers
|
||||
* @param {string} projectDir - Target project directory
|
||||
* @param {string} mcpConfigPath - Relative path to MCP config file (e.g., '.cursor/mcp.json')
|
||||
* @returns {Object} Result object with success status and details
|
||||
*/
|
||||
export function removeTaskMasterMCPConfiguration(projectDir, mcpConfigPath) {
|
||||
// Handle null mcpConfigPath (e.g., for Claude/Codex profiles)
|
||||
if (!mcpConfigPath) {
|
||||
return {
|
||||
success: true,
|
||||
removed: false,
|
||||
deleted: false,
|
||||
error: null,
|
||||
hasOtherServers: false
|
||||
};
|
||||
}
|
||||
|
||||
const mcpPath = path.join(projectDir, mcpConfigPath);
|
||||
|
||||
let result = {
|
||||
success: false,
|
||||
removed: false,
|
||||
deleted: false,
|
||||
error: null,
|
||||
hasOtherServers: false
|
||||
};
|
||||
|
||||
if (!fs.existsSync(mcpPath)) {
|
||||
result.success = true;
|
||||
result.removed = false;
|
||||
log('debug', `[MCP Config] MCP config file does not exist: ${mcpPath}`);
|
||||
return result;
|
||||
}
|
||||
|
||||
try {
|
||||
// Read existing config
|
||||
const mcpConfig = JSON.parse(fs.readFileSync(mcpPath, 'utf8'));
|
||||
|
||||
if (!mcpConfig.mcpServers) {
|
||||
result.success = true;
|
||||
result.removed = false;
|
||||
log('debug', `[MCP Config] No mcpServers section found in: ${mcpPath}`);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Check if Task Master is configured
|
||||
const hasTaskMaster =
|
||||
mcpConfig.mcpServers['task-master-ai'] ||
|
||||
Object.values(mcpConfig.mcpServers).some(
|
||||
(server) =>
|
||||
server.args &&
|
||||
Array.isArray(server.args) &&
|
||||
server.args.some(
|
||||
(arg) => typeof arg === 'string' && arg.includes('task-master-ai')
|
||||
)
|
||||
);
|
||||
|
||||
if (!hasTaskMaster) {
|
||||
result.success = true;
|
||||
result.removed = false;
|
||||
log(
|
||||
'debug',
|
||||
`[MCP Config] Task Master not found in MCP config: ${mcpPath}`
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Remove task-master-ai server
|
||||
delete mcpConfig.mcpServers['task-master-ai'];
|
||||
|
||||
// Also remove any servers that have task-master-ai in their args
|
||||
Object.keys(mcpConfig.mcpServers).forEach((serverName) => {
|
||||
const server = mcpConfig.mcpServers[serverName];
|
||||
if (
|
||||
server.args &&
|
||||
Array.isArray(server.args) &&
|
||||
server.args.some(
|
||||
(arg) => typeof arg === 'string' && arg.includes('task-master-ai')
|
||||
)
|
||||
) {
|
||||
delete mcpConfig.mcpServers[serverName];
|
||||
log(
|
||||
'debug',
|
||||
`[MCP Config] Removed server '${serverName}' containing task-master-ai`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Check if there are other MCP servers remaining
|
||||
const remainingServers = Object.keys(mcpConfig.mcpServers);
|
||||
result.hasOtherServers = remainingServers.length > 0;
|
||||
|
||||
if (result.hasOtherServers) {
|
||||
// Write back the modified config with remaining servers
|
||||
fs.writeFileSync(mcpPath, formatJSONWithTabs(mcpConfig) + '\n');
|
||||
result.success = true;
|
||||
result.removed = true;
|
||||
result.deleted = false;
|
||||
log(
|
||||
'info',
|
||||
`[MCP Config] Removed Task Master from MCP config, preserving other servers: ${remainingServers.join(', ')}`
|
||||
);
|
||||
} else {
|
||||
// No other servers, delete the entire file
|
||||
fs.rmSync(mcpPath, { force: true });
|
||||
result.success = true;
|
||||
result.removed = true;
|
||||
result.deleted = true;
|
||||
log(
|
||||
'info',
|
||||
`[MCP Config] Removed MCP config file (no other servers remaining): ${mcpPath}`
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
result.error = error.message;
|
||||
log(
|
||||
'error',
|
||||
`[MCP Config] Failed to remove Task Master from MCP config: ${error.message}`
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1,283 +0,0 @@
|
||||
/**
|
||||
* Profiles Utility
|
||||
* Consolidated utilities for profile detection, setup, and summary generation
|
||||
*/
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import inquirer from 'inquirer';
|
||||
import chalk from 'chalk';
|
||||
import boxen from 'boxen';
|
||||
import { log } from '../../scripts/modules/utils.js';
|
||||
import { getRulesProfile } from './rule-transformer.js';
|
||||
import { RULE_PROFILES } from '../constants/profiles.js';
|
||||
|
||||
// =============================================================================
|
||||
// PROFILE DETECTION
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Detect which profiles are currently installed in the project
|
||||
* @param {string} projectRoot - Project root directory
|
||||
* @returns {string[]} Array of installed profile names
|
||||
*/
|
||||
export function getInstalledProfiles(projectRoot) {
|
||||
const installedProfiles = [];
|
||||
|
||||
for (const profileName of RULE_PROFILES) {
|
||||
const profileConfig = getRulesProfile(profileName);
|
||||
if (!profileConfig) continue;
|
||||
|
||||
// Check if the profile directory exists
|
||||
const profileDir = path.join(projectRoot, profileConfig.profileDir);
|
||||
const rulesDir = path.join(projectRoot, profileConfig.rulesDir);
|
||||
|
||||
// A profile is considered installed if either the profile dir or rules dir exists
|
||||
if (fs.existsSync(profileDir) || fs.existsSync(rulesDir)) {
|
||||
installedProfiles.push(profileName);
|
||||
}
|
||||
}
|
||||
|
||||
return installedProfiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if removing the specified profiles would result in no profiles remaining
|
||||
* @param {string} projectRoot - Project root directory
|
||||
* @param {string[]} profilesToRemove - Array of profile names to remove
|
||||
* @returns {boolean} True if removal would result in no profiles remaining
|
||||
*/
|
||||
export function wouldRemovalLeaveNoProfiles(projectRoot, profilesToRemove) {
|
||||
const installedProfiles = getInstalledProfiles(projectRoot);
|
||||
const remainingProfiles = installedProfiles.filter(
|
||||
(profile) => !profilesToRemove.includes(profile)
|
||||
);
|
||||
|
||||
return remainingProfiles.length === 0 && installedProfiles.length > 0;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// PROFILE SETUP
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Get the display name for a profile
|
||||
*/
|
||||
function getProfileDisplayName(name) {
|
||||
const profile = getRulesProfile(name);
|
||||
return profile?.displayName || name.charAt(0).toUpperCase() + name.slice(1);
|
||||
}
|
||||
|
||||
// Note: Profile choices are now generated dynamically within runInteractiveProfilesSetup()
|
||||
// to ensure proper alphabetical sorting and pagination configuration
|
||||
|
||||
/**
|
||||
* Launches an interactive prompt for selecting which rule profiles to include in your project.
|
||||
*
|
||||
* This function dynamically lists all available profiles (from RULE_PROFILES) and presents them as checkboxes.
|
||||
* The user must select at least one profile (no defaults are pre-selected). The result is an array of selected profile names.
|
||||
*
|
||||
* Used by both project initialization (init) and the CLI 'task-master rules setup' command.
|
||||
*
|
||||
* @returns {Promise<string[]>} Array of selected profile names (e.g., ['cursor', 'windsurf'])
|
||||
*/
|
||||
export async function runInteractiveProfilesSetup() {
|
||||
// Generate the profile list dynamically with proper display names, alphabetized
|
||||
const profileDescriptions = RULE_PROFILES.map((profileName) => {
|
||||
const displayName = getProfileDisplayName(profileName);
|
||||
const profile = getRulesProfile(profileName);
|
||||
|
||||
// Determine description based on profile type
|
||||
let description;
|
||||
if (Object.keys(profile.fileMap).length === 0) {
|
||||
// Simple profiles (Claude, Codex) - specify the target file
|
||||
const targetFileName =
|
||||
profileName === 'claude' ? 'CLAUDE.md' : 'AGENTS.md';
|
||||
description = `Integration guide (${targetFileName})`;
|
||||
} else {
|
||||
// Full profiles with rules - check if they have MCP config
|
||||
const hasMcpConfig = profile.mcpConfig === true;
|
||||
if (hasMcpConfig) {
|
||||
// Special case for Roo to mention agent modes
|
||||
if (profileName === 'roo') {
|
||||
description = 'Rule profile, MCP config, and agent modes';
|
||||
} else {
|
||||
description = 'Rule profile and MCP config';
|
||||
}
|
||||
} else {
|
||||
description = 'Rule profile';
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
profileName,
|
||||
displayName,
|
||||
description
|
||||
};
|
||||
}).sort((a, b) => a.displayName.localeCompare(b.displayName));
|
||||
|
||||
const profileListText = profileDescriptions
|
||||
.map(
|
||||
({ displayName, description }) =>
|
||||
`${chalk.white('• ')}${chalk.yellow(displayName)}${chalk.white(` - ${description}`)}`
|
||||
)
|
||||
.join('\n');
|
||||
|
||||
console.log(
|
||||
boxen(
|
||||
`${chalk.white.bold('Rule Profiles Setup')}\n\n${chalk.white(
|
||||
'Rule profiles help enforce best practices and conventions for Task Master.\n' +
|
||||
'Each profile provides coding guidelines tailored for specific AI coding environments.\n\n'
|
||||
)}${chalk.cyan('Available Profiles:')}\n${profileListText}`,
|
||||
{
|
||||
padding: 1,
|
||||
borderColor: 'blue',
|
||||
borderStyle: 'round',
|
||||
margin: { top: 1, bottom: 1 }
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Generate choices in the same order as the display text above
|
||||
const sortedChoices = profileDescriptions.map(
|
||||
({ profileName, displayName }) => ({
|
||||
name: displayName,
|
||||
value: profileName
|
||||
})
|
||||
);
|
||||
|
||||
const ruleProfilesQuestion = {
|
||||
type: 'checkbox',
|
||||
name: 'ruleProfiles',
|
||||
message: 'Which rule profiles would you like to add to your project?',
|
||||
choices: sortedChoices,
|
||||
pageSize: sortedChoices.length, // Show all options without pagination
|
||||
loop: false, // Disable loop scrolling
|
||||
validate: (input) => input.length > 0 || 'You must select at least one.'
|
||||
};
|
||||
const { ruleProfiles } = await inquirer.prompt([ruleProfilesQuestion]);
|
||||
return ruleProfiles;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// PROFILE SUMMARY
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Generate appropriate summary message for a profile based on its type
|
||||
* @param {string} profileName - Name of the profile
|
||||
* @param {Object} addResult - Result object with success/failed counts
|
||||
* @returns {string} Formatted summary message
|
||||
*/
|
||||
export function generateProfileSummary(profileName, addResult) {
|
||||
const profileConfig = getRulesProfile(profileName);
|
||||
const isSimpleProfile = Object.keys(profileConfig.fileMap).length === 0;
|
||||
|
||||
if (isSimpleProfile) {
|
||||
// Simple profiles like Claude and Codex only copy AGENTS.md
|
||||
const targetFileName = profileName === 'claude' ? 'CLAUDE.md' : 'AGENTS.md';
|
||||
return `Summary for ${profileName}: Integration guide copied to ${targetFileName}`;
|
||||
} else {
|
||||
return `Summary for ${profileName}: ${addResult.success} rules added, ${addResult.failed} failed.`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate appropriate summary message for profile removal
|
||||
* @param {string} profileName - Name of the profile
|
||||
* @param {Object} removeResult - Result object from removal operation
|
||||
* @returns {string} Formatted summary message
|
||||
*/
|
||||
export function generateProfileRemovalSummary(profileName, removeResult) {
|
||||
const profileConfig = getRulesProfile(profileName);
|
||||
const isSimpleProfile = Object.keys(profileConfig.fileMap).length === 0;
|
||||
|
||||
if (removeResult.skipped) {
|
||||
return `Summary for ${profileName}: Skipped (default or protected files)`;
|
||||
}
|
||||
|
||||
if (removeResult.error && !removeResult.success) {
|
||||
return `Summary for ${profileName}: Failed to remove - ${removeResult.error}`;
|
||||
}
|
||||
|
||||
if (isSimpleProfile) {
|
||||
// Simple profiles like Claude and Codex only have an integration guide
|
||||
const targetFileName = profileName === 'claude' ? 'CLAUDE.md' : 'AGENTS.md';
|
||||
return `Summary for ${profileName}: Integration guide (${targetFileName}) removed`;
|
||||
} else {
|
||||
// Full profiles have rules directories and potentially MCP configs
|
||||
const baseMessage = `Summary for ${profileName}: Rules directory removed`;
|
||||
if (removeResult.notice) {
|
||||
return `${baseMessage} (${removeResult.notice})`;
|
||||
}
|
||||
return baseMessage;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Categorize profiles and generate final summary statistics
|
||||
* @param {Array} addResults - Array of add result objects
|
||||
* @returns {Object} Object with categorized profiles and totals
|
||||
*/
|
||||
export function categorizeProfileResults(addResults) {
|
||||
const successfulProfiles = [];
|
||||
const simpleProfiles = [];
|
||||
let totalSuccess = 0;
|
||||
let totalFailed = 0;
|
||||
|
||||
addResults.forEach((r) => {
|
||||
totalSuccess += r.success;
|
||||
totalFailed += r.failed;
|
||||
|
||||
const profileConfig = getRulesProfile(r.profileName);
|
||||
const isSimpleProfile = Object.keys(profileConfig.fileMap).length === 0;
|
||||
|
||||
if (isSimpleProfile) {
|
||||
// Simple profiles are successful if they completed without error
|
||||
simpleProfiles.push(r.profileName);
|
||||
} else if (r.success > 0) {
|
||||
// Full profiles are successful if they added rules
|
||||
successfulProfiles.push(r.profileName);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
successfulProfiles,
|
||||
simpleProfiles,
|
||||
allSuccessfulProfiles: [...successfulProfiles, ...simpleProfiles],
|
||||
totalSuccess,
|
||||
totalFailed
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Categorize removal results and generate final summary statistics
|
||||
* @param {Array} removalResults - Array of removal result objects
|
||||
* @returns {Object} Object with categorized removal results
|
||||
*/
|
||||
export function categorizeRemovalResults(removalResults) {
|
||||
const successfulRemovals = [];
|
||||
const skippedRemovals = [];
|
||||
const failedRemovals = [];
|
||||
const removalsWithNotices = [];
|
||||
|
||||
removalResults.forEach((result) => {
|
||||
if (result.success) {
|
||||
successfulRemovals.push(result.profileName);
|
||||
} else if (result.skipped) {
|
||||
skippedRemovals.push(result.profileName);
|
||||
} else if (result.error) {
|
||||
failedRemovals.push(result);
|
||||
}
|
||||
|
||||
if (result.notice) {
|
||||
removalsWithNotices.push(result);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
successfulRemovals,
|
||||
skippedRemovals,
|
||||
failedRemovals,
|
||||
removalsWithNotices
|
||||
};
|
||||
}
|
||||
@@ -1,503 +0,0 @@
|
||||
/**
|
||||
* Rule Transformer Module
|
||||
* Handles conversion of Cursor rules to profile rules
|
||||
*
|
||||
* This module procedurally generates .{profile}/rules files from assets/rules files,
|
||||
* eliminating the need to maintain both sets of files manually.
|
||||
*/
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { log } from '../../scripts/modules/utils.js';
|
||||
|
||||
// Import the shared MCP configuration helper
|
||||
import {
|
||||
setupMCPConfiguration,
|
||||
removeTaskMasterMCPConfiguration
|
||||
} from './create-mcp-config.js';
|
||||
|
||||
// Import profile constants (single source of truth)
|
||||
import { RULE_PROFILES } from '../constants/profiles.js';
|
||||
|
||||
// --- Profile Imports ---
|
||||
import * as profilesModule from '../profiles/index.js';
|
||||
|
||||
export function isValidProfile(profile) {
|
||||
return RULE_PROFILES.includes(profile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get rule profile by name
|
||||
* @param {string} name - Profile name
|
||||
* @returns {Object|null} Profile object or null if not found
|
||||
*/
|
||||
export function getRulesProfile(name) {
|
||||
if (!isValidProfile(name)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get the profile from the imported profiles module
|
||||
const profileKey = `${name}Profile`;
|
||||
const profile = profilesModule[profileKey];
|
||||
|
||||
if (!profile) {
|
||||
throw new Error(
|
||||
`Profile not found: static import missing for '${name}'. Valid profiles: ${RULE_PROFILES.join(', ')}`
|
||||
);
|
||||
}
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace basic Cursor terms with profile equivalents
|
||||
*/
|
||||
function replaceBasicTerms(content, conversionConfig) {
|
||||
let result = content;
|
||||
|
||||
// Apply profile term replacements
|
||||
conversionConfig.profileTerms.forEach((pattern) => {
|
||||
if (typeof pattern.to === 'function') {
|
||||
result = result.replace(pattern.from, pattern.to);
|
||||
} else {
|
||||
result = result.replace(pattern.from, pattern.to);
|
||||
}
|
||||
});
|
||||
|
||||
// Apply file extension replacements
|
||||
conversionConfig.fileExtensions.forEach((pattern) => {
|
||||
result = result.replace(pattern.from, pattern.to);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace Cursor tool references with profile tool equivalents
|
||||
*/
|
||||
function replaceToolReferences(content, conversionConfig) {
|
||||
let result = content;
|
||||
|
||||
// Basic pattern for direct tool name replacements
|
||||
const toolNames = conversionConfig.toolNames;
|
||||
const toolReferencePattern = new RegExp(
|
||||
`\\b(${Object.keys(toolNames).join('|')})\\b`,
|
||||
'g'
|
||||
);
|
||||
|
||||
// Apply direct tool name replacements
|
||||
result = result.replace(toolReferencePattern, (match, toolName) => {
|
||||
return toolNames[toolName] || toolName;
|
||||
});
|
||||
|
||||
// Apply contextual tool replacements
|
||||
conversionConfig.toolContexts.forEach((pattern) => {
|
||||
result = result.replace(pattern.from, pattern.to);
|
||||
});
|
||||
|
||||
// Apply tool group replacements
|
||||
conversionConfig.toolGroups.forEach((pattern) => {
|
||||
result = result.replace(pattern.from, pattern.to);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update documentation URLs to point to profile documentation
|
||||
*/
|
||||
function updateDocReferences(content, conversionConfig) {
|
||||
let result = content;
|
||||
|
||||
// Apply documentation URL replacements
|
||||
conversionConfig.docUrls.forEach((pattern) => {
|
||||
if (typeof pattern.to === 'function') {
|
||||
result = result.replace(pattern.from, pattern.to);
|
||||
} else {
|
||||
result = result.replace(pattern.from, pattern.to);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update file references in markdown links
|
||||
*/
|
||||
function updateFileReferences(content, conversionConfig) {
|
||||
const { pathPattern, replacement } = conversionConfig.fileReferences;
|
||||
return content.replace(pathPattern, replacement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform rule content to profile-specific rules
|
||||
* @param {string} content - The content to transform
|
||||
* @param {Object} conversionConfig - The conversion configuration
|
||||
* @param {Object} globalReplacements - Global text replacements
|
||||
* @returns {string} - The transformed content
|
||||
*/
|
||||
function transformRuleContent(content, conversionConfig, globalReplacements) {
|
||||
let result = content;
|
||||
|
||||
// Apply all transformations in appropriate order
|
||||
result = updateFileReferences(result, conversionConfig);
|
||||
result = replaceBasicTerms(result, conversionConfig);
|
||||
result = replaceToolReferences(result, conversionConfig);
|
||||
result = updateDocReferences(result, conversionConfig);
|
||||
|
||||
// Apply any global/catch-all replacements from the profile
|
||||
// Super aggressive failsafe pass to catch any variations we might have missed
|
||||
// This ensures critical transformations are applied even in contexts we didn't anticipate
|
||||
globalReplacements.forEach((pattern) => {
|
||||
if (typeof pattern.to === 'function') {
|
||||
result = result.replace(pattern.from, pattern.to);
|
||||
} else {
|
||||
result = result.replace(pattern.from, pattern.to);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Cursor rule file to a profile-specific rule file
|
||||
* @param {string} sourcePath - Path to the source .mdc file
|
||||
* @param {string} targetPath - Path to the target file
|
||||
* @param {Object} profile - The profile configuration
|
||||
* @returns {boolean} - Success status
|
||||
*/
|
||||
export function convertRuleToProfileRule(sourcePath, targetPath, profile) {
|
||||
const { conversionConfig, globalReplacements } = profile;
|
||||
try {
|
||||
// Read source content
|
||||
const content = fs.readFileSync(sourcePath, 'utf8');
|
||||
|
||||
// Transform content
|
||||
const transformedContent = transformRuleContent(
|
||||
content,
|
||||
conversionConfig,
|
||||
globalReplacements
|
||||
);
|
||||
|
||||
// Ensure target directory exists
|
||||
const targetDir = path.dirname(targetPath);
|
||||
if (!fs.existsSync(targetDir)) {
|
||||
fs.mkdirSync(targetDir, { recursive: true });
|
||||
}
|
||||
|
||||
// Write transformed content
|
||||
fs.writeFileSync(targetPath, transformedContent);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error(`Error converting rule file: ${error.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert all Cursor rules to profile rules for a specific profile
|
||||
*/
|
||||
export function convertAllRulesToProfileRules(projectDir, profile) {
|
||||
// Handle simple profiles (Claude, Codex) that just copy files to root
|
||||
const isSimpleProfile = Object.keys(profile.fileMap).length === 0;
|
||||
if (isSimpleProfile) {
|
||||
// For simple profiles, just call their post-processing hook and return
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const assetsDir = path.join(__dirname, '..', '..', 'assets');
|
||||
if (typeof profile.onPostConvertRulesProfile === 'function') {
|
||||
profile.onPostConvertRulesProfile(projectDir, assetsDir);
|
||||
}
|
||||
return { success: 1, failed: 0 };
|
||||
}
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const sourceDir = path.join(__dirname, '..', '..', 'assets', 'rules');
|
||||
const targetDir = path.join(projectDir, profile.rulesDir);
|
||||
|
||||
// Ensure target directory exists
|
||||
if (!fs.existsSync(targetDir)) {
|
||||
fs.mkdirSync(targetDir, { recursive: true });
|
||||
}
|
||||
|
||||
// Setup MCP configuration if enabled
|
||||
if (profile.mcpConfig !== false) {
|
||||
setupMCPConfiguration(projectDir, profile.mcpConfigPath);
|
||||
}
|
||||
|
||||
let success = 0;
|
||||
let failed = 0;
|
||||
|
||||
// Use fileMap to determine which files to copy
|
||||
const sourceFiles = Object.keys(profile.fileMap);
|
||||
|
||||
for (const sourceFile of sourceFiles) {
|
||||
try {
|
||||
const sourcePath = path.join(sourceDir, sourceFile);
|
||||
|
||||
// Check if source file exists
|
||||
if (!fs.existsSync(sourcePath)) {
|
||||
log(
|
||||
'warn',
|
||||
`[Rule Transformer] Source file not found: ${sourceFile}, skipping`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const targetFilename = profile.fileMap[sourceFile];
|
||||
const targetPath = path.join(targetDir, targetFilename);
|
||||
|
||||
// Ensure target subdirectory exists (for rules like taskmaster/dev_workflow.md)
|
||||
const targetFileDir = path.dirname(targetPath);
|
||||
if (!fs.existsSync(targetFileDir)) {
|
||||
fs.mkdirSync(targetFileDir, { recursive: true });
|
||||
}
|
||||
|
||||
// Read source content
|
||||
let content = fs.readFileSync(sourcePath, 'utf8');
|
||||
|
||||
// Apply transformations
|
||||
content = transformRuleContent(
|
||||
content,
|
||||
profile.conversionConfig,
|
||||
profile.globalReplacements
|
||||
);
|
||||
|
||||
// Write to target
|
||||
fs.writeFileSync(targetPath, content, 'utf8');
|
||||
success++;
|
||||
|
||||
log(
|
||||
'debug',
|
||||
`[Rule Transformer] Converted ${sourceFile} -> ${targetFilename} for ${profile.profileName}`
|
||||
);
|
||||
} catch (error) {
|
||||
failed++;
|
||||
log(
|
||||
'error',
|
||||
`[Rule Transformer] Failed to convert ${sourceFile} for ${profile.profileName}: ${error.message}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Call post-processing hook if defined (e.g., for Roo's rules-*mode* folders)
|
||||
if (typeof profile.onPostConvertRulesProfile === 'function') {
|
||||
const assetsDir = path.join(__dirname, '..', '..', 'assets');
|
||||
profile.onPostConvertRulesProfile(projectDir, assetsDir);
|
||||
}
|
||||
|
||||
return { success, failed };
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove only Task Master specific files from a profile, leaving other existing rules intact
|
||||
* @param {string} projectDir - Target project directory
|
||||
* @param {Object} profile - Profile configuration
|
||||
* @returns {Object} Result object
|
||||
*/
|
||||
export function removeProfileRules(projectDir, profile) {
|
||||
const targetDir = path.join(projectDir, profile.rulesDir);
|
||||
const profileDir = path.join(projectDir, profile.profileDir);
|
||||
|
||||
const result = {
|
||||
profileName: profile.profileName,
|
||||
success: false,
|
||||
skipped: false,
|
||||
error: null,
|
||||
filesRemoved: [],
|
||||
mcpResult: null,
|
||||
profileDirRemoved: false,
|
||||
notice: null
|
||||
};
|
||||
|
||||
try {
|
||||
// Handle simple profiles (Claude, Codex) that just copy files to root
|
||||
const isSimpleProfile = Object.keys(profile.fileMap).length === 0;
|
||||
|
||||
if (isSimpleProfile) {
|
||||
// For simple profiles, just call their removal hook and return
|
||||
if (typeof profile.onRemoveRulesProfile === 'function') {
|
||||
profile.onRemoveRulesProfile(projectDir);
|
||||
}
|
||||
result.success = true;
|
||||
log(
|
||||
'debug',
|
||||
`[Rule Transformer] Successfully removed ${profile.profileName} files from ${projectDir}`
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Check if profile directory exists at all (for full profiles)
|
||||
if (!fs.existsSync(profileDir)) {
|
||||
result.success = true;
|
||||
result.skipped = true;
|
||||
log(
|
||||
'debug',
|
||||
`[Rule Transformer] Profile directory does not exist: ${profileDir}`
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 1. Remove only Task Master specific files from the rules directory
|
||||
let hasOtherRulesFiles = false;
|
||||
if (fs.existsSync(targetDir)) {
|
||||
const taskmasterFiles = Object.values(profile.fileMap);
|
||||
const removedFiles = [];
|
||||
|
||||
// Helper function to recursively check and remove Task Master files
|
||||
function processDirectory(dirPath, relativePath = '') {
|
||||
const items = fs.readdirSync(dirPath);
|
||||
|
||||
for (const item of items) {
|
||||
const itemPath = path.join(dirPath, item);
|
||||
const relativeItemPath = relativePath
|
||||
? path.join(relativePath, item)
|
||||
: item;
|
||||
const stat = fs.statSync(itemPath);
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
// Recursively process subdirectory
|
||||
processDirectory(itemPath, relativeItemPath);
|
||||
|
||||
// Check if directory is empty after processing and remove if so
|
||||
try {
|
||||
const remainingItems = fs.readdirSync(itemPath);
|
||||
if (remainingItems.length === 0) {
|
||||
fs.rmSync(itemPath, { recursive: true, force: true });
|
||||
log(
|
||||
'debug',
|
||||
`[Rule Transformer] Removed empty directory: ${relativeItemPath}`
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
// Directory might have been removed already, ignore
|
||||
}
|
||||
} else if (stat.isFile()) {
|
||||
if (taskmasterFiles.includes(relativeItemPath)) {
|
||||
// This is a Task Master file, remove it
|
||||
fs.rmSync(itemPath, { force: true });
|
||||
removedFiles.push(relativeItemPath);
|
||||
log(
|
||||
'debug',
|
||||
`[Rule Transformer] Removed Task Master file: ${relativeItemPath}`
|
||||
);
|
||||
} else {
|
||||
// This is not a Task Master file, leave it
|
||||
hasOtherRulesFiles = true;
|
||||
log(
|
||||
'debug',
|
||||
`[Rule Transformer] Preserved existing file: ${relativeItemPath}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process the rules directory recursively
|
||||
processDirectory(targetDir);
|
||||
|
||||
result.filesRemoved = removedFiles;
|
||||
|
||||
// Only remove the rules directory if it's empty after removing Task Master files
|
||||
const remainingFiles = fs.readdirSync(targetDir);
|
||||
if (remainingFiles.length === 0) {
|
||||
fs.rmSync(targetDir, { recursive: true, force: true });
|
||||
log(
|
||||
'debug',
|
||||
`[Rule Transformer] Removed empty rules directory: ${targetDir}`
|
||||
);
|
||||
} else if (hasOtherRulesFiles) {
|
||||
result.notice = `Preserved ${remainingFiles.length} existing rule files in ${profile.rulesDir}`;
|
||||
log('info', `[Rule Transformer] ${result.notice}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Handle MCP configuration - only remove Task Master, preserve other servers
|
||||
if (profile.mcpConfig !== false) {
|
||||
result.mcpResult = removeTaskMasterMCPConfiguration(
|
||||
projectDir,
|
||||
profile.mcpConfigPath
|
||||
);
|
||||
if (result.mcpResult.hasOtherServers) {
|
||||
if (!result.notice) {
|
||||
result.notice = 'Preserved other MCP server configurations';
|
||||
} else {
|
||||
result.notice += '; preserved other MCP server configurations';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Call removal hook if defined (e.g., Roo's custom cleanup)
|
||||
if (typeof profile.onRemoveRulesProfile === 'function') {
|
||||
profile.onRemoveRulesProfile(projectDir);
|
||||
}
|
||||
|
||||
// 4. Only remove profile directory if:
|
||||
// - It's completely empty after all operations, AND
|
||||
// - All rules removed were Task Master rules (no existing rules preserved), AND
|
||||
// - MCP config was completely deleted (not just Task Master removed), AND
|
||||
// - No other files or folders exist in the profile directory
|
||||
if (fs.existsSync(profileDir)) {
|
||||
const remaining = fs.readdirSync(profileDir);
|
||||
const allRulesWereTaskMaster = !hasOtherRulesFiles;
|
||||
const mcpConfigCompletelyDeleted = result.mcpResult?.deleted === true;
|
||||
|
||||
// Check if there are any other files or folders beyond what we expect
|
||||
const hasOtherFilesOrFolders = remaining.length > 0;
|
||||
|
||||
if (
|
||||
remaining.length === 0 &&
|
||||
allRulesWereTaskMaster &&
|
||||
(profile.mcpConfig === false || mcpConfigCompletelyDeleted) &&
|
||||
!hasOtherFilesOrFolders
|
||||
) {
|
||||
fs.rmSync(profileDir, { recursive: true, force: true });
|
||||
result.profileDirRemoved = true;
|
||||
log(
|
||||
'debug',
|
||||
`[Rule Transformer] Removed profile directory: ${profileDir} (completely empty, all rules were Task Master rules, and MCP config was completely removed)`
|
||||
);
|
||||
} else {
|
||||
// Determine what was preserved and why
|
||||
const preservationReasons = [];
|
||||
if (hasOtherFilesOrFolders) {
|
||||
preservationReasons.push(
|
||||
`${remaining.length} existing files/folders`
|
||||
);
|
||||
}
|
||||
if (hasOtherRulesFiles) {
|
||||
preservationReasons.push('existing rule files');
|
||||
}
|
||||
if (result.mcpResult?.hasOtherServers) {
|
||||
preservationReasons.push('other MCP server configurations');
|
||||
}
|
||||
|
||||
const preservationMessage = `Preserved ${preservationReasons.join(', ')} in ${profile.profileDir}`;
|
||||
|
||||
if (!result.notice) {
|
||||
result.notice = preservationMessage;
|
||||
} else if (!result.notice.includes('Preserved')) {
|
||||
result.notice += `; ${preservationMessage.toLowerCase()}`;
|
||||
}
|
||||
|
||||
log('info', `[Rule Transformer] ${preservationMessage}`);
|
||||
}
|
||||
}
|
||||
|
||||
result.success = true;
|
||||
log(
|
||||
'debug',
|
||||
`[Rule Transformer] Successfully removed ${profile.profileName} Task Master files from ${projectDir}`
|
||||
);
|
||||
} catch (error) {
|
||||
result.error = error.message;
|
||||
log(
|
||||
'error',
|
||||
`[Rule Transformer] Failed to remove ${profile.profileName} rules: ${error.message}`
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
describe('Claude Profile Initialization Functionality', () => {
|
||||
let claudeProfileContent;
|
||||
|
||||
beforeAll(() => {
|
||||
const claudeJsPath = path.join(
|
||||
process.cwd(),
|
||||
'src',
|
||||
'profiles',
|
||||
'claude.js'
|
||||
);
|
||||
claudeProfileContent = fs.readFileSync(claudeJsPath, 'utf8');
|
||||
});
|
||||
|
||||
test('claude.js is a simple profile with correct configuration', () => {
|
||||
expect(claudeProfileContent).toContain("profileName: 'claude'");
|
||||
expect(claudeProfileContent).toContain("displayName: 'Claude Code'");
|
||||
expect(claudeProfileContent).toContain("profileDir: '.'");
|
||||
expect(claudeProfileContent).toContain("rulesDir: '.'");
|
||||
});
|
||||
|
||||
test('claude.js has no MCP configuration', () => {
|
||||
expect(claudeProfileContent).toContain('mcpConfig: false');
|
||||
expect(claudeProfileContent).toContain('mcpConfigName: null');
|
||||
expect(claudeProfileContent).toContain('mcpConfigPath: null');
|
||||
});
|
||||
|
||||
test('claude.js has empty file map (simple profile)', () => {
|
||||
expect(claudeProfileContent).toContain('fileMap: {}');
|
||||
expect(claudeProfileContent).toContain('conversionConfig: {}');
|
||||
expect(claudeProfileContent).toContain('globalReplacements: []');
|
||||
});
|
||||
|
||||
test('claude.js has lifecycle functions for file management', () => {
|
||||
expect(claudeProfileContent).toContain('function onAddRulesProfile');
|
||||
expect(claudeProfileContent).toContain('function onRemoveRulesProfile');
|
||||
expect(claudeProfileContent).toContain(
|
||||
'function onPostConvertRulesProfile'
|
||||
);
|
||||
});
|
||||
|
||||
test('claude.js copies AGENTS.md to CLAUDE.md', () => {
|
||||
expect(claudeProfileContent).toContain("'AGENTS.md'");
|
||||
expect(claudeProfileContent).toContain("'CLAUDE.md'");
|
||||
expect(claudeProfileContent).toContain('copyFileSync');
|
||||
});
|
||||
|
||||
test('claude.js has proper error handling', () => {
|
||||
expect(claudeProfileContent).toContain('try {');
|
||||
expect(claudeProfileContent).toContain('} catch (err) {');
|
||||
expect(claudeProfileContent).toContain("log('error'");
|
||||
});
|
||||
});
|
||||
@@ -1,53 +0,0 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
describe('Cline Profile Initialization Functionality', () => {
|
||||
let clineProfileContent;
|
||||
|
||||
beforeAll(() => {
|
||||
const clineJsPath = path.join(process.cwd(), 'src', 'profiles', 'cline.js');
|
||||
clineProfileContent = fs.readFileSync(clineJsPath, 'utf8');
|
||||
});
|
||||
|
||||
test('cline.js uses factory pattern with correct configuration', () => {
|
||||
expect(clineProfileContent).toContain("name: 'cline'");
|
||||
expect(clineProfileContent).toContain("displayName: 'Cline'");
|
||||
expect(clineProfileContent).toContain("rulesDir: '.clinerules'");
|
||||
expect(clineProfileContent).toContain("profileDir: '.clinerules'");
|
||||
});
|
||||
|
||||
test('cline.js configures .mdc to .md extension mapping', () => {
|
||||
expect(clineProfileContent).toContain("fileExtension: '.mdc'");
|
||||
expect(clineProfileContent).toContain("targetExtension: '.md'");
|
||||
});
|
||||
|
||||
test('cline.js uses standard tool mappings', () => {
|
||||
expect(clineProfileContent).toContain('COMMON_TOOL_MAPPINGS.STANDARD');
|
||||
// Should contain comment about standard tool names
|
||||
expect(clineProfileContent).toContain('standard tool names');
|
||||
});
|
||||
|
||||
test('cline.js contains correct URL configuration', () => {
|
||||
expect(clineProfileContent).toContain("url: 'cline.bot'");
|
||||
expect(clineProfileContent).toContain("docsUrl: 'docs.cline.bot'");
|
||||
});
|
||||
|
||||
test('cline.js has MCP configuration disabled', () => {
|
||||
expect(clineProfileContent).toContain('mcpConfig: false');
|
||||
expect(clineProfileContent).toContain(
|
||||
"mcpConfigName: 'cline_mcp_settings.json'"
|
||||
);
|
||||
});
|
||||
|
||||
test('cline.js has custom file mapping for cursor_rules.mdc', () => {
|
||||
expect(clineProfileContent).toContain('customFileMap:');
|
||||
expect(clineProfileContent).toContain(
|
||||
"'cursor_rules.mdc': 'cline_rules.md'"
|
||||
);
|
||||
});
|
||||
|
||||
test('cline.js uses createProfile factory function', () => {
|
||||
expect(clineProfileContent).toContain('createProfile');
|
||||
expect(clineProfileContent).toContain('export const clineProfile');
|
||||
});
|
||||
});
|
||||
@@ -1,54 +0,0 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
describe('Codex Profile Initialization Functionality', () => {
|
||||
let codexProfileContent;
|
||||
|
||||
beforeAll(() => {
|
||||
const codexJsPath = path.join(process.cwd(), 'src', 'profiles', 'codex.js');
|
||||
codexProfileContent = fs.readFileSync(codexJsPath, 'utf8');
|
||||
});
|
||||
|
||||
test('codex.js is a simple profile with correct configuration', () => {
|
||||
expect(codexProfileContent).toContain("profileName: 'codex'");
|
||||
expect(codexProfileContent).toContain("displayName: 'Codex'");
|
||||
expect(codexProfileContent).toContain("profileDir: '.'");
|
||||
expect(codexProfileContent).toContain("rulesDir: '.'");
|
||||
});
|
||||
|
||||
test('codex.js has no MCP configuration', () => {
|
||||
expect(codexProfileContent).toContain('mcpConfig: false');
|
||||
expect(codexProfileContent).toContain('mcpConfigName: null');
|
||||
expect(codexProfileContent).toContain('mcpConfigPath: null');
|
||||
});
|
||||
|
||||
test('codex.js has empty file map (simple profile)', () => {
|
||||
expect(codexProfileContent).toContain('fileMap: {}');
|
||||
expect(codexProfileContent).toContain('conversionConfig: {}');
|
||||
expect(codexProfileContent).toContain('globalReplacements: []');
|
||||
});
|
||||
|
||||
test('codex.js has lifecycle functions for file management', () => {
|
||||
expect(codexProfileContent).toContain('function onAddRulesProfile');
|
||||
expect(codexProfileContent).toContain('function onRemoveRulesProfile');
|
||||
expect(codexProfileContent).toContain('function onPostConvertRulesProfile');
|
||||
});
|
||||
|
||||
test('codex.js copies AGENTS.md to AGENTS.md (same filename)', () => {
|
||||
expect(codexProfileContent).toContain("'AGENTS.md'");
|
||||
expect(codexProfileContent).toContain('copyFileSync');
|
||||
// Should copy to the same filename (AGENTS.md)
|
||||
expect(codexProfileContent).toMatch(/destFile.*AGENTS\.md/);
|
||||
});
|
||||
|
||||
test('codex.js has proper error handling', () => {
|
||||
expect(codexProfileContent).toContain('try {');
|
||||
expect(codexProfileContent).toContain('} catch (err) {');
|
||||
expect(codexProfileContent).toContain("log('error'");
|
||||
});
|
||||
|
||||
test('codex.js removes AGENTS.md on profile removal', () => {
|
||||
expect(codexProfileContent).toContain('rmSync');
|
||||
expect(codexProfileContent).toContain('force: true');
|
||||
});
|
||||
});
|
||||
@@ -1,44 +0,0 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
describe('Cursor Profile Initialization Functionality', () => {
|
||||
let cursorProfileContent;
|
||||
|
||||
beforeAll(() => {
|
||||
const cursorJsPath = path.join(
|
||||
process.cwd(),
|
||||
'src',
|
||||
'profiles',
|
||||
'cursor.js'
|
||||
);
|
||||
cursorProfileContent = fs.readFileSync(cursorJsPath, 'utf8');
|
||||
});
|
||||
|
||||
test('cursor.js uses factory pattern with correct configuration', () => {
|
||||
expect(cursorProfileContent).toContain("name: 'cursor'");
|
||||
expect(cursorProfileContent).toContain("displayName: 'Cursor'");
|
||||
expect(cursorProfileContent).toContain("rulesDir: '.cursor/rules'");
|
||||
expect(cursorProfileContent).toContain("profileDir: '.cursor'");
|
||||
});
|
||||
|
||||
test('cursor.js preserves .mdc extension in both input and output', () => {
|
||||
expect(cursorProfileContent).toContain("fileExtension: '.mdc'");
|
||||
expect(cursorProfileContent).toContain("targetExtension: '.mdc'");
|
||||
// Should preserve cursor_rules.mdc filename
|
||||
expect(cursorProfileContent).toContain(
|
||||
"'cursor_rules.mdc': 'cursor_rules.mdc'"
|
||||
);
|
||||
});
|
||||
|
||||
test('cursor.js uses standard tool mappings (no tool renaming)', () => {
|
||||
expect(cursorProfileContent).toContain('COMMON_TOOL_MAPPINGS.STANDARD');
|
||||
// Should not contain custom tool mappings since cursor keeps original names
|
||||
expect(cursorProfileContent).not.toContain('edit_file');
|
||||
expect(cursorProfileContent).not.toContain('apply_diff');
|
||||
});
|
||||
|
||||
test('cursor.js contains correct URL configuration', () => {
|
||||
expect(cursorProfileContent).toContain("url: 'cursor.so'");
|
||||
expect(cursorProfileContent).toContain("docsUrl: 'docs.cursor.com'");
|
||||
});
|
||||
});
|
||||
@@ -1,112 +0,0 @@
|
||||
import { jest } from '@jest/globals';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
describe('Roo Files Inclusion in Package', () => {
|
||||
// This test verifies that the required Roo files are included in the final package
|
||||
|
||||
test('package.json includes assets/** in the "files" array for Roo source files', () => {
|
||||
// Read the package.json file
|
||||
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
||||
|
||||
// Check if assets/** is included in the files array (which contains Roo files)
|
||||
expect(packageJson.files).toContain('assets/**');
|
||||
});
|
||||
|
||||
test('roo.js profile contains logic for Roo directory creation and file copying', () => {
|
||||
// Read the roo.js profile file
|
||||
const rooJsPath = path.join(process.cwd(), 'src', 'profiles', 'roo.js');
|
||||
const rooJsContent = fs.readFileSync(rooJsPath, 'utf8');
|
||||
|
||||
// Check for the main handler function
|
||||
expect(
|
||||
rooJsContent.includes('onAddRulesProfile(targetDir, assetsDir)')
|
||||
).toBe(true);
|
||||
|
||||
// Check for general recursive copy of assets/roocode
|
||||
expect(
|
||||
rooJsContent.includes('copyRecursiveSync(sourceDir, targetDir)')
|
||||
).toBe(true);
|
||||
|
||||
// Check for updated path handling
|
||||
expect(rooJsContent.includes("path.join(assetsDir, 'roocode')")).toBe(true);
|
||||
|
||||
// Check for .roomodes file copying logic (source and destination paths)
|
||||
expect(rooJsContent.includes("path.join(sourceDir, '.roomodes')")).toBe(
|
||||
true
|
||||
);
|
||||
expect(rooJsContent.includes("path.join(targetDir, '.roomodes')")).toBe(
|
||||
true
|
||||
);
|
||||
|
||||
// Check for mode-specific rule file copying logic
|
||||
expect(rooJsContent.includes('for (const mode of ROO_MODES)')).toBe(true);
|
||||
expect(
|
||||
rooJsContent.includes(
|
||||
'path.join(rooModesDir, `rules-${mode}`, `${mode}-rules`)'
|
||||
)
|
||||
).toBe(true);
|
||||
expect(
|
||||
rooJsContent.includes(
|
||||
"path.join(targetDir, '.roo', `rules-${mode}`, `${mode}-rules`)"
|
||||
)
|
||||
).toBe(true);
|
||||
|
||||
// Check for import of ROO_MODES from profiles.js instead of local definition
|
||||
expect(
|
||||
rooJsContent.includes(
|
||||
"import { ROO_MODES } from '../constants/profiles.js'"
|
||||
)
|
||||
).toBe(true);
|
||||
|
||||
// Verify ROO_MODES is used in the for loop
|
||||
expect(rooJsContent.includes('for (const mode of ROO_MODES)')).toBe(true);
|
||||
|
||||
// Verify mode variable is used in the template strings (this confirms modes are being processed)
|
||||
expect(rooJsContent.includes('rules-${mode}')).toBe(true);
|
||||
expect(rooJsContent.includes('${mode}-rules')).toBe(true);
|
||||
|
||||
// Verify that the ROO_MODES constant is properly imported and used
|
||||
// We should be able to find the template literals that use the mode variable
|
||||
expect(rooJsContent.includes('`rules-${mode}`')).toBe(true);
|
||||
expect(rooJsContent.includes('`${mode}-rules`')).toBe(true);
|
||||
expect(rooJsContent.includes('Copied ${mode}-rules to ${dest}')).toBe(true);
|
||||
|
||||
// Also verify that the expected mode names are defined in the imported constant
|
||||
// by checking that the import is from the correct file that contains all 6 modes
|
||||
const profilesConstantsPath = path.join(
|
||||
process.cwd(),
|
||||
'src',
|
||||
'constants',
|
||||
'profiles.js'
|
||||
);
|
||||
const profilesContent = fs.readFileSync(profilesConstantsPath, 'utf8');
|
||||
|
||||
// Check that ROO_MODES is exported and contains all expected modes
|
||||
expect(profilesContent.includes('export const ROO_MODES')).toBe(true);
|
||||
const expectedModes = [
|
||||
'architect',
|
||||
'ask',
|
||||
'orchestrator',
|
||||
'code',
|
||||
'debug',
|
||||
'test'
|
||||
];
|
||||
expectedModes.forEach((mode) => {
|
||||
expect(profilesContent.includes(`'${mode}'`)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
test('source Roo files exist in assets directory', () => {
|
||||
// Verify that the source files for Roo integration exist
|
||||
expect(
|
||||
fs.existsSync(path.join(process.cwd(), 'assets', 'roocode', '.roo'))
|
||||
).toBe(true);
|
||||
expect(
|
||||
fs.existsSync(path.join(process.cwd(), 'assets', 'roocode', '.roomodes'))
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -1,70 +0,0 @@
|
||||
import { jest } from '@jest/globals';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
describe('Roo Profile Initialization Functionality', () => {
|
||||
let rooProfileContent;
|
||||
|
||||
beforeAll(() => {
|
||||
// Read the roo.js profile file content once for all tests
|
||||
const rooJsPath = path.join(process.cwd(), 'src', 'profiles', 'roo.js');
|
||||
rooProfileContent = fs.readFileSync(rooJsPath, 'utf8');
|
||||
});
|
||||
|
||||
test('roo.js profile ensures Roo directory structure via onAddRulesProfile', () => {
|
||||
// Check if onAddRulesProfile function exists
|
||||
expect(rooProfileContent).toContain(
|
||||
'onAddRulesProfile(targetDir, assetsDir)'
|
||||
);
|
||||
|
||||
// Check for the general copy of assets/roocode which includes .roo base structure
|
||||
expect(rooProfileContent).toContain(
|
||||
"const sourceDir = path.join(assetsDir, 'roocode');"
|
||||
);
|
||||
expect(rooProfileContent).toContain(
|
||||
'copyRecursiveSync(sourceDir, targetDir);'
|
||||
);
|
||||
|
||||
// Check for the specific .roo modes directory handling
|
||||
expect(rooProfileContent).toContain(
|
||||
"const rooModesDir = path.join(sourceDir, '.roo');"
|
||||
);
|
||||
|
||||
// Check for import of ROO_MODES from profiles.js instead of local definition
|
||||
expect(rooProfileContent).toContain(
|
||||
"import { ROO_MODES } from '../constants/profiles.js';"
|
||||
);
|
||||
});
|
||||
|
||||
test('roo.js profile copies .roomodes file via onAddRulesProfile', () => {
|
||||
expect(rooProfileContent).toContain(
|
||||
'onAddRulesProfile(targetDir, assetsDir)'
|
||||
);
|
||||
|
||||
// Check for the specific .roomodes copy logic
|
||||
expect(rooProfileContent).toContain(
|
||||
"const roomodesSrc = path.join(sourceDir, '.roomodes');"
|
||||
);
|
||||
expect(rooProfileContent).toContain(
|
||||
"const roomodesDest = path.join(targetDir, '.roomodes');"
|
||||
);
|
||||
expect(rooProfileContent).toContain(
|
||||
'fs.copyFileSync(roomodesSrc, roomodesDest);'
|
||||
);
|
||||
});
|
||||
|
||||
test('roo.js profile copies mode-specific rule files via onAddRulesProfile', () => {
|
||||
expect(rooProfileContent).toContain(
|
||||
'onAddRulesProfile(targetDir, assetsDir)'
|
||||
);
|
||||
expect(rooProfileContent).toContain('for (const mode of ROO_MODES)');
|
||||
|
||||
// Check for the specific mode rule file copy logic
|
||||
expect(rooProfileContent).toContain(
|
||||
'const src = path.join(rooModesDir, `rules-${mode}`, `${mode}-rules`);'
|
||||
);
|
||||
expect(rooProfileContent).toContain(
|
||||
"const dest = path.join(targetDir, '.roo', `rules-${mode}`, `${mode}-rules`);"
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,98 +0,0 @@
|
||||
import { jest } from '@jest/globals';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
describe('Rules Files Inclusion in Package', () => {
|
||||
// This test verifies that the required rules files are included in the final package
|
||||
|
||||
test('package.json includes assets/** in the "files" array for rules source files', () => {
|
||||
// Read the package.json file
|
||||
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
||||
|
||||
// Check if assets/** is included in the files array (which contains rules files)
|
||||
expect(packageJson.files).toContain('assets/**');
|
||||
});
|
||||
|
||||
test('source rules files exist in assets/rules directory', () => {
|
||||
// Verify that the actual rules files exist
|
||||
const rulesDir = path.join(process.cwd(), 'assets', 'rules');
|
||||
expect(fs.existsSync(rulesDir)).toBe(true);
|
||||
|
||||
// Check for the 4 files that currently exist
|
||||
const expectedFiles = [
|
||||
'dev_workflow.mdc',
|
||||
'taskmaster.mdc',
|
||||
'self_improve.mdc',
|
||||
'cursor_rules.mdc'
|
||||
];
|
||||
|
||||
expectedFiles.forEach((file) => {
|
||||
const filePath = path.join(rulesDir, file);
|
||||
expect(fs.existsSync(filePath)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
test('roo.js profile contains logic for Roo directory creation and file copying', () => {
|
||||
// Read the roo.js profile file
|
||||
const rooJsPath = path.join(process.cwd(), 'src', 'profiles', 'roo.js');
|
||||
const rooJsContent = fs.readFileSync(rooJsPath, 'utf8');
|
||||
|
||||
// Check for the main handler function
|
||||
expect(
|
||||
rooJsContent.includes('onAddRulesProfile(targetDir, assetsDir)')
|
||||
).toBe(true);
|
||||
|
||||
// Check for general recursive copy of assets/roocode
|
||||
expect(
|
||||
rooJsContent.includes('copyRecursiveSync(sourceDir, targetDir)')
|
||||
).toBe(true);
|
||||
|
||||
// Check for updated path handling
|
||||
expect(rooJsContent.includes("path.join(assetsDir, 'roocode')")).toBe(true);
|
||||
|
||||
// Check for .roomodes file copying logic (source and destination paths)
|
||||
expect(rooJsContent.includes("path.join(sourceDir, '.roomodes')")).toBe(
|
||||
true
|
||||
);
|
||||
expect(rooJsContent.includes("path.join(targetDir, '.roomodes')")).toBe(
|
||||
true
|
||||
);
|
||||
|
||||
// Check for mode-specific rule file copying logic
|
||||
expect(rooJsContent.includes('for (const mode of ROO_MODES)')).toBe(true);
|
||||
expect(
|
||||
rooJsContent.includes(
|
||||
'path.join(rooModesDir, `rules-${mode}`, `${mode}-rules`)'
|
||||
)
|
||||
).toBe(true);
|
||||
expect(
|
||||
rooJsContent.includes(
|
||||
"path.join(targetDir, '.roo', `rules-${mode}`, `${mode}-rules`)"
|
||||
)
|
||||
).toBe(true);
|
||||
|
||||
// Check for import of ROO_MODES from profiles.js
|
||||
expect(
|
||||
rooJsContent.includes(
|
||||
"import { ROO_MODES } from '../constants/profiles.js'"
|
||||
)
|
||||
).toBe(true);
|
||||
|
||||
// Verify mode variable is used in the template strings (this confirms modes are being processed)
|
||||
expect(rooJsContent.includes('rules-${mode}')).toBe(true);
|
||||
expect(rooJsContent.includes('${mode}-rules')).toBe(true);
|
||||
});
|
||||
|
||||
test('source Roo files exist in assets directory', () => {
|
||||
// Verify that the source files for Roo integration exist
|
||||
expect(
|
||||
fs.existsSync(path.join(process.cwd(), 'assets', 'roocode', '.roo'))
|
||||
).toBe(true);
|
||||
expect(
|
||||
fs.existsSync(path.join(process.cwd(), 'assets', 'roocode', '.roomodes'))
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -1,41 +0,0 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
describe('Trae Profile Initialization Functionality', () => {
|
||||
let traeProfileContent;
|
||||
|
||||
beforeAll(() => {
|
||||
const traeJsPath = path.join(process.cwd(), 'src', 'profiles', 'trae.js');
|
||||
traeProfileContent = fs.readFileSync(traeJsPath, 'utf8');
|
||||
});
|
||||
|
||||
test('trae.js uses factory pattern with correct configuration', () => {
|
||||
expect(traeProfileContent).toContain("name: 'trae'");
|
||||
expect(traeProfileContent).toContain("displayName: 'Trae'");
|
||||
expect(traeProfileContent).toContain("rulesDir: '.trae/rules'");
|
||||
expect(traeProfileContent).toContain("profileDir: '.trae'");
|
||||
});
|
||||
|
||||
test('trae.js configures .mdc to .md extension mapping', () => {
|
||||
expect(traeProfileContent).toContain("fileExtension: '.mdc'");
|
||||
expect(traeProfileContent).toContain("targetExtension: '.md'");
|
||||
});
|
||||
|
||||
test('trae.js uses standard tool mappings', () => {
|
||||
expect(traeProfileContent).toContain('COMMON_TOOL_MAPPINGS.STANDARD');
|
||||
// Should contain comment about standard tool names
|
||||
expect(traeProfileContent).toContain('standard tool names');
|
||||
});
|
||||
|
||||
test('trae.js contains correct URL configuration', () => {
|
||||
expect(traeProfileContent).toContain("url: 'trae.ai'");
|
||||
expect(traeProfileContent).toContain("docsUrl: 'docs.trae.ai'");
|
||||
});
|
||||
|
||||
test('trae.js has MCP configuration disabled', () => {
|
||||
expect(traeProfileContent).toContain('mcpConfig: false');
|
||||
expect(traeProfileContent).toContain(
|
||||
"mcpConfigName: 'trae_mcp_settings.json'"
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,39 +0,0 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
describe('Windsurf Profile Initialization Functionality', () => {
|
||||
let windsurfProfileContent;
|
||||
|
||||
beforeAll(() => {
|
||||
const windsurfJsPath = path.join(
|
||||
process.cwd(),
|
||||
'src',
|
||||
'profiles',
|
||||
'windsurf.js'
|
||||
);
|
||||
windsurfProfileContent = fs.readFileSync(windsurfJsPath, 'utf8');
|
||||
});
|
||||
|
||||
test('windsurf.js uses factory pattern with correct configuration', () => {
|
||||
expect(windsurfProfileContent).toContain("name: 'windsurf'");
|
||||
expect(windsurfProfileContent).toContain("displayName: 'Windsurf'");
|
||||
expect(windsurfProfileContent).toContain("rulesDir: '.windsurf/rules'");
|
||||
expect(windsurfProfileContent).toContain("profileDir: '.windsurf'");
|
||||
});
|
||||
|
||||
test('windsurf.js configures .mdc to .md extension mapping', () => {
|
||||
expect(windsurfProfileContent).toContain("fileExtension: '.mdc'");
|
||||
expect(windsurfProfileContent).toContain("targetExtension: '.md'");
|
||||
});
|
||||
|
||||
test('windsurf.js uses standard tool mappings', () => {
|
||||
expect(windsurfProfileContent).toContain('COMMON_TOOL_MAPPINGS.STANDARD');
|
||||
// Should contain comment about standard tool names
|
||||
expect(windsurfProfileContent).toContain('standard tool names');
|
||||
});
|
||||
|
||||
test('windsurf.js contains correct URL configuration', () => {
|
||||
expect(windsurfProfileContent).toContain("url: 'windsurf.com'");
|
||||
expect(windsurfProfileContent).toContain("docsUrl: 'docs.windsurf.com'");
|
||||
});
|
||||
});
|
||||
71
tests/integration/roo-files-inclusion.test.js
Normal file
71
tests/integration/roo-files-inclusion.test.js
Normal file
@@ -0,0 +1,71 @@
|
||||
import { jest } from '@jest/globals';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
describe('Roo Files Inclusion in Package', () => {
|
||||
// This test verifies that the required Roo files are included in the final package
|
||||
|
||||
test('package.json includes assets/** in the "files" array for Roo source files', () => {
|
||||
// Read the package.json file
|
||||
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
||||
|
||||
// Check if assets/** is included in the files array (which contains Roo files)
|
||||
expect(packageJson.files).toContain('assets/**');
|
||||
});
|
||||
|
||||
test('init.js creates Roo directories and copies files', () => {
|
||||
// Read the init.js file
|
||||
const initJsPath = path.join(process.cwd(), 'scripts', 'init.js');
|
||||
const initJsContent = fs.readFileSync(initJsPath, 'utf8');
|
||||
|
||||
// Check for Roo directory creation (using more flexible pattern matching)
|
||||
const hasRooDir = initJsContent.includes(
|
||||
"ensureDirectoryExists(path.join(targetDir, '.roo'))"
|
||||
);
|
||||
expect(hasRooDir).toBe(true);
|
||||
|
||||
// Check for .roomodes file copying using hardcoded path
|
||||
const hasRoomodes = initJsContent.includes(
|
||||
"path.join(targetDir, '.roomodes')"
|
||||
);
|
||||
expect(hasRoomodes).toBe(true);
|
||||
|
||||
// Check for local ROO_MODES definition and usage
|
||||
const hasRooModes = initJsContent.includes('ROO_MODES');
|
||||
expect(hasRooModes).toBe(true);
|
||||
|
||||
// Check for local ROO_MODES array definition
|
||||
const hasLocalRooModes = initJsContent.includes(
|
||||
"const ROO_MODES = ['architect', 'ask', 'boomerang', 'code', 'debug', 'test']"
|
||||
);
|
||||
expect(hasLocalRooModes).toBe(true);
|
||||
|
||||
// Check for mode-specific patterns (these will still be present in the local array)
|
||||
const hasArchitect = initJsContent.includes('architect');
|
||||
const hasAsk = initJsContent.includes('ask');
|
||||
const hasBoomerang = initJsContent.includes('boomerang');
|
||||
const hasCode = initJsContent.includes('code');
|
||||
const hasDebug = initJsContent.includes('debug');
|
||||
const hasTest = initJsContent.includes('test');
|
||||
|
||||
expect(hasArchitect).toBe(true);
|
||||
expect(hasAsk).toBe(true);
|
||||
expect(hasBoomerang).toBe(true);
|
||||
expect(hasCode).toBe(true);
|
||||
expect(hasDebug).toBe(true);
|
||||
expect(hasTest).toBe(true);
|
||||
});
|
||||
|
||||
test('source Roo files exist in assets directory', () => {
|
||||
// Verify that the source files for Roo integration exist
|
||||
expect(
|
||||
fs.existsSync(path.join(process.cwd(), 'assets', 'roocode', '.roo'))
|
||||
).toBe(true);
|
||||
expect(
|
||||
fs.existsSync(path.join(process.cwd(), 'assets', 'roocode', '.roomodes'))
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user