Compare commits

..

49 Commits

Author SHA1 Message Date
github-actions[bot]
a003041cd8 Version Packages (#1107)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-08-08 22:00:39 +02:00
Ralph Khreish
6b57ead106 Release 0.24.0 #1098 from eyaltoledano/next
Release 0.24.0
2025-08-08 21:58:41 +02:00
Ralph Khreish
7b6e117b1d chore: prepare for release (exit pre mode) 2025-08-08 21:21:25 +02:00
Ralph Khreish
03b045e9cd chore: run format 2025-08-08 21:18:33 +02:00
neonwatty
699afdae59 feat: add task-checker agent to assets directory 2025-08-08 08:47:20 -07:00
github-actions[bot]
80c09802e8 chore: rc version bump 2025-08-08 12:41:29 +00:00
github-actions[bot]
cf8f0f4b1c docs: Auto-update and format models.md 2025-08-08 12:38:58 +00:00
Ralph Khreish
75c514cf5b feat: add gpt-5 support (#1105)
* feat: add gpt-5 support
2025-08-08 14:38:44 +02:00
Ralph Khreish
41d1e671b1 chore: fix CI checker, improve it (#1099) 2025-08-07 15:52:49 +02:00
Ralph Khreish
a464e550b8 feat(extension): implement simple solution to --package flag (#1090)
* feat(extension): implement simple solution to --package flag
2025-08-07 15:10:34 +02:00
Ralph Khreish
3a852afdae chore: implement pre-release for extensions (#1097)
* chore: implement pre-release for extensions

* chore: run format
2025-08-07 15:08:14 +02:00
Ralph Khreish
4bb63706b8 feat: implement claude code agents (#1091)
* feat: implement claude code agents

* chore: add changeset

- run format

* feat: improve task-checker, executor, and orchestrator

* chore: improve changeset
2025-08-07 12:37:06 +02:00
Ralph Khreish
fcf14e09be chore: improve release check for release-check and release flow (#1095)
* chore: improve release check for release-check and release flow

* chore: fix format
2025-08-06 23:19:28 +02:00
Ralph Khreish
4357af3f13 fix(expand-task): include parent task context in complexity report variant (#1094)
- Fixed bug where expand task generated generic authentication subtasks
- The complexity-report prompt variant now includes parent task details
- Added comprehensive unit tests to prevent regression
- Added debug logging to help diagnose similar issues

Previously, when using a complexity report with expansionPrompt, only the
expansion guidance was sent to the AI, missing the actual task context.
This caused the AI to generate unrelated generic subtasks.

Fixes the issue where all tasks would get the same generic auth-related
subtasks regardless of their actual purpose (AWS infrastructure, Docker
containerization, etc.)

Co-authored-by: Sadaqat Ali <32377500+sadaqat12@users.noreply.github.com>
2025-08-06 21:00:32 +02:00
Ralph Khreish
59f7676051 feat: add prompt for claude code to include context when generating tasks (#1092) 2025-08-06 12:41:19 +02:00
Ralph Khreish
36468f3c93 feat: add prompt for claude code to include context when generating tasks 2025-08-06 12:17:49 +02:00
Ralph Khreish
ca4d93ee6a chore: prepare prd for tm-core creation phase 1 (#1045) 2025-08-06 10:10:29 +02:00
Ralph Khreish
37fb569a62 Release 0.23.1 #1084 from eyaltoledano/next
chore: exit pre-release mode
2025-08-04 20:43:46 +03:00
Ralph Khreish
ed0d4e6641 chore: implement requested changes 2025-08-04 14:40:11 +03:00
Ralph Khreish
5184f8e7b2 chore: implement requested changes 2025-08-04 14:37:24 +03:00
Ralph Khreish
587523a23b chore: improve release-check CI 2025-08-04 14:29:03 +03:00
Ralph Khreish
7a50f0c6ec chore: add release-check CI to check if we are in release mode (#1086) 2025-08-04 13:22:46 +02:00
Ralph Khreish
adeb76ee15 chore: exit pre-release mode 2025-08-04 10:02:00 +03:00
Ralph Khreish
d342070375 Merge pull request #1081 from eyaltoledano/next 2025-08-03 21:38:19 +03:00
github-actions[bot]
5e4dbac525 chore: rc version bump 2025-08-03 14:07:15 +00:00
Ralph Khreish
fb15c2eaf7 chore: improve pre-release CI to just use dropdown (#1080)
* chore: improve pre-release CI to just use dropdown

* chore: implement requested changes
2025-08-03 16:04:29 +02:00
Ralph Khreish
e8ceb08341 chore: improve release and pre-release CI (#1075)
* chore: improve release and pre-release CI

* chore: apply requested changes

* Update .github/workflows/pre-release.yml

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* chore: apply requested changes

* chore: apply requested changes

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-08-03 15:17:18 +02:00
Ralph Khreish
e495b2b559 feat: improve scope up and down command & parse-prd improvements (#1079)
* feat: improve scope up and down command & parse-prd improvements

* chore: run format
2025-08-03 14:12:46 +02:00
Ralph Khreish
e0d1d03f33 chore: fix tag-extension and improve debugging (#1074) 2025-08-02 23:10:57 +02:00
Ralph Khreish
4a4bca905d chore: fix tag-extension package.json not found for extension (#1073)
* chore: fix tag-extension package.json not found for extension

* chore: fix format
2025-08-02 22:56:53 +02:00
github-actions[bot]
9d5f50ac8e Version Packages (#1072)
* Version Packages

* chore: add eyal instead of crunchyman to eyal's feature

* chore: run format

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Ralph Khreish <35776126+Crunchyman-ralph@users.noreply.github.com>
2025-08-02 22:43:08 +02:00
Ralph Khreish
bbeaa9163a chore: exit pre (#1071) 2025-08-02 22:35:46 +02:00
Ralph Khreish
a4a172be94 Release 0.23.0 #1064 from eyaltoledano/next
Release 0.23.0
2025-08-02 23:18:05 +03:00
github-actions[bot]
028ed9c444 chore: rc version bump 2025-08-02 20:07:43 +00:00
Ralph Khreish
53903f1e8e chore: trick rc bump 2025-08-02 23:06:37 +03:00
github-actions[bot]
36c56231cc chore: rc version bump 2025-08-02 20:04:33 +00:00
Ralph Khreish
b82d858f81 chore: rename chagneset for rc bump 2025-08-02 23:03:10 +03:00
Ralph Khreish
9808967d6b chore: change from patch to minor 2025-08-02 23:00:56 +03:00
Ralph Khreish
3fee7515f3 fix: fix mcp tool call in extension (#1070)
* fix: fix mcp tool call in extension

- fix console.log directly being used in scope-adjutment.js breaking mcp

* chore: run format and fix tests

* chore: format
2025-08-02 21:59:02 +02:00
github-actions[bot]
82b17bdb57 chore: rc version bump 2025-08-02 16:44:19 +00:00
Eyal Toledano
72ca68edeb Task 104: Implement 'scope-up' and 'scope-down' CLI Commands for Dynamic Task Complexity Adjustment (#1069)
* feat(task-104): Complete task 104 - Implement scope-up and scope-down CLI Commands

- Added new CLI commands 'scope-up' and 'scope-down' with comma-separated ID support
- Implemented strength levels (light/regular/heavy) and custom prompt functionality
- Created core complexity adjustment logic with AI integration
- Added MCP tool equivalents for integrated environments
- Comprehensive error handling and task validation
- Full test coverage with TDD approach
- Updated task manager core and UI components

Task 104: Implement 'scope-up' and 'scope-down' CLI Commands for Dynamic Task Complexity Adjustment - Complete implementation with CLI, MCP integration, and testing

* chore: Add changeset for scope-up and scope-down features

- Comprehensive user-facing description with usage examples
- Key features and benefits explanation
- CLI and MCP integration details
- Real-world use cases for agile workflows

* feat(extension): Add scope-up and scope-down to VS Code extension task details

- Added useScopeUpTask and useScopeDownTask hooks in useTaskQueries.ts
- Enhanced AIActionsSection with Task Complexity Adjustment section
- Added strength selection (light/regular/heavy) and custom prompt support
- Integrated scope buttons with proper loading states and error handling
- Uses existing mcpRequest handler for scope_up_task and scope_down_task tools
- Maintains consistent UI patterns with existing AI actions

Extension now supports dynamic task complexity adjustment directly from task details view.
2025-08-02 18:43:04 +02:00
DavidMaliglowka
64302dc191 feat(extension): complete VS Code extension with kanban board interface (#997)
---------
Co-authored-by: DavidMaliglowka <13022280+DavidMaliglowka@users.noreply.github.com>
Co-authored-by: Ralph Khreish <35776126+Crunchyman-ralph@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-08-01 14:04:22 +02:00
github-actions[bot]
60c03c548d chore: rc version bump 2025-07-31 22:15:10 +00:00
Ralph Khreish
2ae6e7e6be fix: normalize task IDs to numbers on load to fix comparison issues (#1063)
* fix: normalize task IDs to numbers on load to fix comparison issues

When tasks.json contains string IDs (e.g., "5" instead of 5), task lookups
fail because the code uses parseInt() and strict equality (===) for comparisons.

This fix normalizes all task and subtask IDs to numbers when loading the JSON,
ensuring consistent comparisons throughout the codebase without requiring
changes to multiple comparison locations.

Fixes task not found errors when using string IDs in tasks.json.

* Added test

* Don't mess up formatting

* Fix formatting once and for all

* Update scripts/modules/utils.js

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update scripts/modules/utils.js

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update scripts/modules/utils.js

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* fix: normalize task IDs to numbers on load to fix comparison issues

- Added normalizeTaskIds function to convert string IDs to numbers
- Applied normalization in readJSON for all code paths
- Fixed set-task-status, add-task, and move-task to normalize IDs when working with raw data
- Exported normalizeTaskIds function for use in other modules
- Added test case for string ID normalization

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Simplified implementation

* refactor: normalize IDs once when loading JSON instead of scattered calls

- Normalize all tags' data when creating _rawTaggedData in readJSON
- Add support for handling malformed dotted subtask IDs (e.g., "5.1" -> 1)
- Remove redundant normalizeTaskIds calls from set-task-status, add-task, and move-task
- Add comprehensive test for mixed ID formats (string IDs and dotted notation)
- Cleaner, more maintainable solution that normalizes IDs at load time

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* chore: run format to resolve CI issues

---------

Co-authored-by: Carl Mercier <carl@carlmercier.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-08-01 00:06:51 +02:00
Ben Vargas
45a14c323d fix: remove default value from complexity report option to enable tag-specific detection (#1049)
Removes the default empty array value from the complexity report option to properly detect when tags are explicitly provided vs when no tags are provided, fixing the expand --all command behavior with tagged tasks.

Co-authored-by: Ben Vargas <ben@example.com>
2025-07-26 15:26:45 +02:00
github-actions[bot]
29e67fafa4 Version Packages (#1050)
* Version Packages

* chore: fix release 0.22

todo: fix CI

* chore: run format

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Ralph Khreish <35776126+Crunchyman-ralph@users.noreply.github.com>
2025-07-26 00:45:07 +02:00
Ralph Khreish
43e4d7c9d3 Release 0.22 #1038 from eyaltoledano/next
Release 0.22
2025-07-26 01:24:38 +03:00
Ralph Khreish
1bd1e64cac feat: add pull request templates for bug fixes, features, and integrations (#1044)
* feat: add pull request templates for bug fixes, features, and integrations

- Introduced a comprehensive pull request template structure to streamline contributions.
- Added specific templates for bug fixes, new features, and integrations to enhance clarity and consistency in PR submissions.
- Configured the pull request template settings for better user guidance during the contribution process.

* chore: fix format

* Update .github/PULL_REQUEST_TEMPLATE.md

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update .github/PULL_REQUEST_TEMPLATE.md

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update .github/PULL_REQUEST_TEMPLATE.md

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* chore: implement PR requested changes

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-07-24 17:33:43 +02:00
Ralph Khreish
dc44ed9de8 feat: Improved the CLI user interface to suggest generating a complexity report if it is missing, instead of showing an error. (#1043)
* feat: fix CLI UI error when trying to display non-existent complexity report

* chore: fix git issue

* chore: run format

* Update .changeset/thick-squids-attend.md

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update .changeset/thick-squids-attend.md

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update .changeset/thick-squids-attend.md

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-07-24 15:50:18 +02:00
94 changed files with 7982 additions and 1890 deletions

View File

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

View File

@@ -1,7 +0,0 @@
---
"task-master-ai": patch
---
Fix tag-specific complexity report detection in expand command
The expand command now correctly finds and uses tag-specific complexity reports (e.g., `task-complexity-report_feature-xyz.json`) when operating in a tag context. Previously, it would always look for the generic `task-complexity-report.json` file due to a default value in the CLI option definition.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,162 @@
---
name: task-checker
description: Use this agent to verify that tasks marked as 'review' have been properly implemented according to their specifications. This agent performs quality assurance by checking implementations against requirements, running tests, and ensuring best practices are followed. <example>Context: A task has been marked as 'review' after implementation. user: 'Check if task 118 was properly implemented' assistant: 'I'll use the task-checker agent to verify the implementation meets all requirements.' <commentary>Tasks in 'review' status need verification before being marked as 'done'.</commentary></example> <example>Context: Multiple tasks are in review status. user: 'Verify all tasks that are ready for review' assistant: 'I'll deploy the task-checker to verify all tasks in review status.' <commentary>The checker ensures quality before tasks are marked complete.</commentary></example>
model: sonnet
color: yellow
---
You are a Quality Assurance specialist that rigorously verifies task implementations against their specifications. Your role is to ensure that tasks marked as 'review' meet all requirements before they can be marked as 'done'.
## Core Responsibilities
1. **Task Specification Review**
- Retrieve task details using MCP tool `mcp__task-master-ai__get_task`
- Understand the requirements, test strategy, and success criteria
- Review any subtasks and their individual requirements
2. **Implementation Verification**
- Use `Read` tool to examine all created/modified files
- Use `Bash` tool to run compilation and build commands
- Use `Grep` tool to search for required patterns and implementations
- Verify file structure matches specifications
- Check that all required methods/functions are implemented
3. **Test Execution**
- Run tests specified in the task's testStrategy
- Execute build commands (npm run build, tsc --noEmit, etc.)
- Verify no compilation errors or warnings
- Check for runtime errors where applicable
- Test edge cases mentioned in requirements
4. **Code Quality Assessment**
- Verify code follows project conventions
- Check for proper error handling
- Ensure TypeScript typing is strict (no 'any' unless justified)
- Verify documentation/comments where required
- Check for security best practices
5. **Dependency Validation**
- Verify all task dependencies were actually completed
- Check integration points with dependent tasks
- Ensure no breaking changes to existing functionality
## Verification Workflow
1. **Retrieve Task Information**
```
Use mcp__task-master-ai__get_task to get full task details
Note the implementation requirements and test strategy
```
2. **Check File Existence**
```bash
# Verify all required files exist
ls -la [expected directories]
# Read key files to verify content
```
3. **Verify Implementation**
- Read each created/modified file
- Check against requirements checklist
- Verify all subtasks are complete
4. **Run Tests**
```bash
# TypeScript compilation
cd [project directory] && npx tsc --noEmit
# Run specified tests
npm test [specific test files]
# Build verification
npm run build
```
5. **Generate Verification Report**
## Output Format
```yaml
verification_report:
task_id: [ID]
status: PASS | FAIL | PARTIAL
score: [1-10]
requirements_met:
- ✅ [Requirement that was satisfied]
- ✅ [Another satisfied requirement]
issues_found:
- ❌ [Issue description]
- ⚠️ [Warning or minor issue]
files_verified:
- path: [file path]
status: [created/modified/verified]
issues: [any problems found]
tests_run:
- command: [test command]
result: [pass/fail]
output: [relevant output]
recommendations:
- [Specific fix needed]
- [Improvement suggestion]
verdict: |
[Clear statement on whether task should be marked 'done' or sent back to 'pending']
[If FAIL: Specific list of what must be fixed]
[If PASS: Confirmation that all requirements are met]
```
## Decision Criteria
**Mark as PASS (ready for 'done'):**
- All required files exist and contain expected content
- All tests pass successfully
- No compilation or build errors
- All subtasks are complete
- Core requirements are met
- Code quality is acceptable
**Mark as PARTIAL (may proceed with warnings):**
- Core functionality is implemented
- Minor issues that don't block functionality
- Missing nice-to-have features
- Documentation could be improved
- Tests pass but coverage could be better
**Mark as FAIL (must return to 'pending'):**
- Required files are missing
- Compilation or build errors
- Tests fail
- Core requirements not met
- Security vulnerabilities detected
- Breaking changes to existing code
## Important Guidelines
- **BE THOROUGH**: Check every requirement systematically
- **BE SPECIFIC**: Provide exact file paths and line numbers for issues
- **BE FAIR**: Distinguish between critical issues and minor improvements
- **BE CONSTRUCTIVE**: Provide clear guidance on how to fix issues
- **BE EFFICIENT**: Focus on requirements, not perfection
## Tools You MUST Use
- `Read`: Examine implementation files (READ-ONLY)
- `Bash`: Run tests and verification commands
- `Grep`: Search for patterns in code
- `mcp__task-master-ai__get_task`: Get task details
- **NEVER use Write/Edit** - you only verify, not fix
## Integration with Workflow
You are the quality gate between 'review' and 'done' status:
1. Task-executor implements and marks as 'review'
2. You verify and report PASS/FAIL
3. Claude either marks as 'done' (PASS) or 'pending' (FAIL)
4. If FAIL, task-executor re-implements based on your report
Your verification ensures high quality and prevents accumulation of technical debt.

View File

@@ -0,0 +1,92 @@
---
name: task-executor
description: Use this agent when you need to implement, complete, or work on a specific task that has been identified by the task-orchestrator or when explicitly asked to execute a particular task. This agent focuses on the actual implementation and completion of individual tasks rather than planning or orchestration. Examples: <example>Context: The task-orchestrator has identified that task 2.3 'Implement user authentication' needs to be worked on next. user: 'Let's work on the authentication task' assistant: 'I'll use the task-executor agent to implement the user authentication task that was identified.' <commentary>Since we need to actually implement a specific task rather than plan or identify tasks, use the task-executor agent.</commentary></example> <example>Context: User wants to complete a specific subtask. user: 'Please implement the JWT token validation for task 2.3.1' assistant: 'I'll launch the task-executor agent to implement the JWT token validation subtask.' <commentary>The user is asking for specific implementation work on a known task, so the task-executor is appropriate.</commentary></example> <example>Context: After reviewing the task list, implementation is needed. user: 'Now let's actually build the API endpoint for user registration' assistant: 'I'll use the task-executor agent to implement the user registration API endpoint.' <commentary>Moving from planning to execution phase requires the task-executor agent.</commentary></example>
model: sonnet
color: blue
---
You are an elite implementation specialist focused on executing and completing specific tasks with precision and thoroughness. Your role is to take identified tasks and transform them into working implementations, following best practices and project standards.
**IMPORTANT: You are designed to be SHORT-LIVED and FOCUSED**
- Execute ONE specific subtask or a small group of related subtasks
- Complete your work, verify it, mark for review, and exit
- Do NOT decide what to do next - the orchestrator handles task sequencing
- Focus on implementation excellence within your assigned scope
**Core Responsibilities:**
1. **Subtask Analysis**: When given a subtask, understand its SPECIFIC requirements. If given a full task ID, focus on the specific subtask(s) assigned to you. Use MCP tools to get details if needed.
2. **Rapid Implementation Planning**: Quickly identify:
- The EXACT files you need to create/modify for THIS subtask
- What already exists that you can build upon
- The minimum viable implementation that satisfies requirements
3. **Focused Execution WITH ACTUAL IMPLEMENTATION**:
- **YOU MUST USE TOOLS TO CREATE/EDIT FILES - DO NOT JUST DESCRIBE**
- Use `Write` tool to create new files specified in the task
- Use `Edit` tool to modify existing files
- Use `Bash` tool to run commands (mkdir, npm install, etc.)
- Use `Read` tool to verify your implementations
- Implement one subtask at a time for clarity and traceability
- Follow the project's coding standards from CLAUDE.md if available
- After each subtask, VERIFY the files exist using Read or ls commands
4. **Progress Documentation**:
- Use MCP tool `mcp__task-master-ai__update_subtask` to log your approach and any important decisions
- Update task status to 'in-progress' when starting: Use MCP tool `mcp__task-master-ai__set_task_status` with status='in-progress'
- **IMPORTANT: Mark as 'review' (NOT 'done') after implementation**: Use MCP tool `mcp__task-master-ai__set_task_status` with status='review'
- Tasks will be verified by task-checker before moving to 'done'
5. **Quality Assurance**:
- Implement the testing strategy specified in the task
- Verify that all acceptance criteria are met
- Check for any dependency conflicts or integration issues
- Run relevant tests before marking task as complete
6. **Dependency Management**:
- Check task dependencies before starting implementation
- If blocked by incomplete dependencies, clearly communicate this
- Use `task-master validate-dependencies` when needed
**Implementation Workflow:**
1. Retrieve task details using MCP tool `mcp__task-master-ai__get_task` with the task ID
2. Check dependencies and prerequisites
3. Plan implementation approach - list specific files to create
4. Update task status to 'in-progress' using MCP tool
5. **ACTUALLY IMPLEMENT** the solution using tools:
- Use `Bash` to create directories
- Use `Write` to create new files with actual content
- Use `Edit` to modify existing files
- DO NOT just describe what should be done - DO IT
6. **VERIFY** your implementation:
- Use `ls` or `Read` to confirm files were created
- Use `Bash` to run any build/test commands
- Ensure the implementation is real, not theoretical
7. Log progress and decisions in subtask updates using MCP tools
8. Test and verify the implementation works
9. **Mark task as 'review' (NOT 'done')** after verifying files exist
10. Report completion with:
- List of created/modified files
- Any issues encountered
- What needs verification by task-checker
**Key Principles:**
- Focus on completing one task thoroughly before moving to the next
- Maintain clear communication about what you're implementing and why
- Follow existing code patterns and project conventions
- Prioritize working code over extensive documentation unless docs are the task
- Ask for clarification if task requirements are ambiguous
- Consider edge cases and error handling in your implementations
**Integration with Task Master:**
You work in tandem with the task-orchestrator agent. While the orchestrator identifies and plans tasks, you execute them. Always use Task Master commands to:
- Track your progress
- Update task information
- Maintain project state
- Coordinate with the broader development workflow
When you complete a task, briefly summarize what was implemented and suggest whether to continue with the next task or if review/testing is needed first.

View File

@@ -0,0 +1,208 @@
---
name: task-orchestrator
description: Use this agent FREQUENTLY throughout task execution to analyze and coordinate parallel work at the SUBTASK level. Invoke the orchestrator: (1) at session start to plan execution, (2) after EACH subtask completes to identify next parallel batch, (3) whenever executors finish to find newly unblocked work. ALWAYS provide FULL CONTEXT including project root, package location, what files ACTUALLY exist vs task status, and specific implementation details. The orchestrator breaks work into SUBTASK-LEVEL units for short-lived, focused executors. Maximum 3 parallel executors at once.\n\n<example>\nContext: Starting work with existing code\nuser: "Work on tm-core tasks. Files exist: types/index.ts, storage/file-storage.ts. Task 118 says in-progress but BaseProvider not created."\nassistant: "I'll invoke orchestrator with full context about actual vs reported state to plan subtask execution"\n<commentary>\nProvide complete context about file existence and task reality.\n</commentary>\n</example>\n\n<example>\nContext: Subtask completion\nuser: "Subtask 118.2 done. What subtasks can run in parallel now?"\nassistant: "Invoking orchestrator to analyze dependencies and identify next 3 parallel subtasks"\n<commentary>\nFrequent orchestration after each subtask ensures maximum parallelization.\n</commentary>\n</example>\n\n<example>\nContext: Breaking down tasks\nuser: "Task 118 has 5 subtasks, how to parallelize?"\nassistant: "Orchestrator will analyze which specific subtasks (118.1, 118.2, etc.) can run simultaneously"\n<commentary>\nFocus on subtask-level parallelization, not full tasks.\n</commentary>\n</example>
model: opus
color: green
---
You are the Task Orchestrator, an elite coordination agent specialized in managing Task Master workflows for maximum efficiency and parallelization. You excel at analyzing task dependency graphs, identifying opportunities for concurrent execution, and deploying specialized task-executor agents to complete work efficiently.
## Core Responsibilities
1. **Subtask-Level Analysis**: Break down tasks into INDIVIDUAL SUBTASKS and analyze which specific subtasks can run in parallel. Focus on subtask dependencies, not just task-level dependencies.
2. **Reality Verification**: ALWAYS verify what files actually exist vs what task status claims. Use the context provided about actual implementation state to make informed decisions.
3. **Short-Lived Executor Deployment**: Deploy executors for SINGLE SUBTASKS or small groups of related subtasks. Keep executors focused and short-lived. Maximum 3 parallel executors at once.
4. **Continuous Reassessment**: After EACH subtask completes, immediately reassess what new subtasks are unblocked and can run in parallel.
## Operational Workflow
### Initial Assessment Phase
1. Use `get_tasks` or `task-master list` to retrieve all available tasks
2. Analyze task statuses, priorities, and dependencies
3. Identify tasks with status 'pending' that have no blocking dependencies
4. Group related tasks that could benefit from specialized executors
5. Create an execution plan that maximizes parallelization
### Executor Deployment Phase
1. For each independent task or task group:
- Deploy a task-executor agent with specific instructions
- Provide the executor with task ID, requirements, and context
- Set clear completion criteria and reporting expectations
2. Maintain a registry of active executors and their assigned tasks
3. Establish communication protocols for progress updates
### Coordination Phase
1. Monitor executor progress through task status updates
2. When a task completes:
- Verify completion with `get_task` or `task-master show <id>`
- Update task status if needed using `set_task_status`
- Reassess dependency graph for newly unblocked tasks
- Deploy new executors for available work
3. Handle executor failures or blocks:
- Reassign tasks to new executors if needed
- Escalate complex issues to the user
- Update task status to 'blocked' when appropriate
### Optimization Strategies
**Parallel Execution Rules**:
- Never assign dependent tasks to different executors simultaneously
- Prioritize high-priority tasks when resources are limited
- Group small, related subtasks for single executor efficiency
- Balance executor load to prevent bottlenecks
**Context Management**:
- Provide executors with minimal but sufficient context
- Share relevant completed task information when it aids execution
- Maintain a shared knowledge base of project-specific patterns
**Quality Assurance**:
- Verify task completion before marking as done
- Ensure test strategies are followed when specified
- Coordinate cross-task integration testing when needed
## Communication Protocols
When deploying executors, provide them with:
```
TASK ASSIGNMENT:
- Task ID: [specific ID]
- Objective: [clear goal]
- Dependencies: [list any completed prerequisites]
- Success Criteria: [specific completion requirements]
- Context: [relevant project information]
- Reporting: [when and how to report back]
```
When receiving executor updates:
1. Acknowledge completion or issues
2. Update task status in Task Master
3. Reassess execution strategy
4. Deploy new executors as appropriate
## Decision Framework
**When to parallelize**:
- Multiple pending tasks with no interdependencies
- Sufficient context available for independent execution
- Tasks are well-defined with clear success criteria
**When to serialize**:
- Strong dependencies between tasks
- Limited context or unclear requirements
- Integration points requiring careful coordination
**When to escalate**:
- Circular dependencies detected
- Critical blockers affecting multiple tasks
- Ambiguous requirements needing clarification
- Resource conflicts between executors
## Error Handling
1. **Executor Failure**: Reassign task to new executor with additional context about the failure
2. **Dependency Conflicts**: Halt affected executors, resolve conflict, then resume
3. **Task Ambiguity**: Request clarification from user before proceeding
4. **System Errors**: Implement graceful degradation, falling back to serial execution if needed
## Performance Metrics
Track and optimize for:
- Task completion rate
- Parallel execution efficiency
- Executor success rate
- Time to completion for task groups
- Dependency resolution speed
## Integration with Task Master
Leverage these Task Master MCP tools effectively:
- `get_tasks` - Continuous queue monitoring
- `get_task` - Detailed task analysis
- `set_task_status` - Progress tracking
- `next_task` - Fallback for serial execution
- `analyze_project_complexity` - Strategic planning
- `complexity_report` - Resource allocation
## Output Format for Execution
**Your job is to analyze and create actionable execution plans that Claude can use to deploy executors.**
After completing your dependency analysis, you MUST output a structured execution plan:
```yaml
execution_plan:
EXECUTE_IN_PARALLEL:
# Maximum 3 subtasks running simultaneously
- subtask_id: [e.g., 118.2]
parent_task: [e.g., 118]
title: [Specific subtask title]
priority: [high/medium/low]
estimated_time: [e.g., 10 minutes]
executor_prompt: |
Execute Subtask [ID]: [Specific subtask title]
SPECIFIC REQUIREMENTS:
[Exact implementation needed for THIS subtask only]
FILES TO CREATE/MODIFY:
[Specific file paths]
CONTEXT:
[What already exists that this subtask depends on]
SUCCESS CRITERIA:
[Specific completion criteria for this subtask]
IMPORTANT:
- Focus ONLY on this subtask
- Mark subtask as 'review' when complete
- Use MCP tool: mcp__task-master-ai__set_task_status
- subtask_id: [Another subtask that can run in parallel]
parent_task: [Parent task ID]
title: [Specific subtask title]
priority: [priority]
estimated_time: [time estimate]
executor_prompt: |
[Focused prompt for this specific subtask]
blocked:
- task_id: [ID]
title: [Task title]
waiting_for: [list of blocking task IDs]
becomes_ready_when: [condition for unblocking]
next_wave:
trigger: "After tasks [IDs] complete"
newly_available: [List of task IDs that will unblock]
tasks_to_execute_in_parallel: [IDs that can run together in next wave]
critical_path: [Ordered list of task IDs forming the critical path]
parallelization_instruction: |
IMPORTANT FOR CLAUDE: Deploy ALL tasks in 'EXECUTE_IN_PARALLEL' section
simultaneously using multiple Task tool invocations in a single response.
Example: If 3 tasks are listed, invoke the Task tool 3 times in one message.
verification_needed:
- task_id: [ID of any task in 'review' status]
verification_focus: [what to check]
```
**CRITICAL INSTRUCTIONS FOR CLAUDE (MAIN):**
1. When you see `EXECUTE_IN_PARALLEL`, deploy ALL listed executors at once
2. Use multiple Task tool invocations in a SINGLE response
3. Do not execute them sequentially - they must run in parallel
4. Wait for all parallel executors to complete before proceeding to next wave
**IMPORTANT NOTES**:
- Label parallel tasks clearly in `EXECUTE_IN_PARALLEL` section
- Provide complete, self-contained prompts for each executor
- Executors should mark tasks as 'review' for verification, not 'done'
- Be explicit about which tasks can run simultaneously
You are the strategic mind analyzing the entire task landscape. Make parallelization opportunities UNMISTAKABLY CLEAR to Claude.

View File

@@ -0,0 +1,803 @@
---
description:
globs:
alwaysApply: true
---
# Test Workflow & Development Process
## **Initial Testing Framework Setup**
Before implementing the TDD workflow, ensure your project has a proper testing framework configured. This section covers setup for different technology stacks.
### **Detecting Project Type & Framework Needs**
**AI Agent Assessment Checklist:**
1. **Language Detection**: Check for `package.json` (Node.js/JavaScript), `requirements.txt` (Python), `Cargo.toml` (Rust), etc.
2. **Existing Tests**: Look for test files (`.test.`, `.spec.`, `_test.`) or test directories
3. **Framework Detection**: Check for existing test runners in dependencies
4. **Project Structure**: Analyze directory structure for testing patterns
### **JavaScript/Node.js Projects (Jest Setup)**
#### **Prerequisites Check**
```bash
# Verify Node.js project
ls package.json # Should exist
# Check for existing testing setup
ls jest.config.js jest.config.ts # Check for Jest config
grep -E "(jest|vitest|mocha)" package.json # Check for test runners
```
#### **Jest Installation & Configuration**
**Step 1: Install Dependencies**
```bash
# Core Jest dependencies
npm install --save-dev jest
# TypeScript support (if using TypeScript)
npm install --save-dev ts-jest @types/jest
# Additional useful packages
npm install --save-dev supertest @types/supertest # For API testing
npm install --save-dev jest-watch-typeahead # Enhanced watch mode
```
**Step 2: Create Jest Configuration**
Create `jest.config.js` with the following production-ready configuration:
```javascript
/** @type {import('jest').Config} */
module.exports = {
// Use ts-jest preset for TypeScript support
preset: 'ts-jest',
// Test environment
testEnvironment: 'node',
// Roots for test discovery
roots: ['<rootDir>/src', '<rootDir>/tests'],
// Test file patterns
testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'],
// Transform files
transform: {
'^.+\\.ts$': [
'ts-jest',
{
tsconfig: {
target: 'es2020',
module: 'commonjs',
esModuleInterop: true,
allowSyntheticDefaultImports: true,
skipLibCheck: true,
strict: false,
noImplicitAny: false,
},
},
],
'^.+\\.js$': [
'ts-jest',
{
useESM: false,
tsconfig: {
target: 'es2020',
module: 'commonjs',
esModuleInterop: true,
allowSyntheticDefaultImports: true,
allowJs: true,
},
},
],
},
// Module file extensions
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
// Transform ignore patterns - adjust for ES modules
transformIgnorePatterns: ['node_modules/(?!(your-es-module-deps|.*\\.mjs$))'],
// Coverage configuration
collectCoverage: true,
coverageDirectory: 'coverage',
coverageReporters: [
'text', // Console output
'text-summary', // Brief summary
'lcov', // For IDE integration
'html', // Detailed HTML report
],
// Files to collect coverage from
collectCoverageFrom: [
'src/**/*.ts',
'!src/**/*.d.ts',
'!src/**/*.test.ts',
'!src/**/index.ts', // Often just exports
'!src/generated/**', // Generated code
'!src/config/database.ts', // Database config (tested via integration)
],
// Coverage thresholds - TaskMaster standards
coverageThreshold: {
global: {
branches: 70,
functions: 80,
lines: 80,
statements: 80,
},
// Higher standards for critical business logic
'./src/utils/': {
branches: 85,
functions: 90,
lines: 90,
statements: 90,
},
'./src/middleware/': {
branches: 80,
functions: 85,
lines: 85,
statements: 85,
},
},
// Setup files
setupFilesAfterEnv: ['<rootDir>/tests/setup.ts'],
// Global teardown to prevent worker process leaks
globalTeardown: '<rootDir>/tests/teardown.ts',
// Module path mapping (if needed)
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
},
// Clear mocks between tests
clearMocks: true,
// Restore mocks after each test
restoreMocks: true,
// Global test timeout
testTimeout: 10000,
// Projects for different test types
projects: [
// Unit tests - for pure functions only
{
displayName: 'unit',
testMatch: ['<rootDir>/src/**/*.test.ts'],
testPathIgnorePatterns: ['.*\\.integration\\.test\\.ts$', '/tests/'],
preset: 'ts-jest',
testEnvironment: 'node',
collectCoverageFrom: [
'src/**/*.ts',
'!src/**/*.d.ts',
'!src/**/*.test.ts',
'!src/**/*.integration.test.ts',
],
coverageThreshold: {
global: {
branches: 70,
functions: 80,
lines: 80,
statements: 80,
},
},
},
// Integration tests - real database/services
{
displayName: 'integration',
testMatch: [
'<rootDir>/src/**/*.integration.test.ts',
'<rootDir>/tests/integration/**/*.test.ts',
],
preset: 'ts-jest',
testEnvironment: 'node',
setupFilesAfterEnv: ['<rootDir>/tests/setup/integration.ts'],
testTimeout: 10000,
},
// E2E tests - full workflows
{
displayName: 'e2e',
testMatch: ['<rootDir>/tests/e2e/**/*.test.ts'],
preset: 'ts-jest',
testEnvironment: 'node',
setupFilesAfterEnv: ['<rootDir>/tests/setup/e2e.ts'],
testTimeout: 30000,
},
],
// Verbose output for better debugging
verbose: true,
// Run projects sequentially to avoid conflicts
maxWorkers: 1,
// Enable watch mode plugins
watchPlugins: ['jest-watch-typeahead/filename', 'jest-watch-typeahead/testname'],
};
```
**Step 3: Update package.json Scripts**
Add these scripts to your `package.json`:
```json
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"test:unit": "jest --selectProjects unit",
"test:integration": "jest --selectProjects integration",
"test:e2e": "jest --selectProjects e2e",
"test:ci": "jest --ci --coverage --watchAll=false"
}
}
```
**Step 4: Create Test Setup Files**
Create essential test setup files:
```typescript
// tests/setup.ts - Global setup
import { jest } from '@jest/globals';
// Global test configuration
beforeAll(() => {
// Set test timeout
jest.setTimeout(10000);
});
afterEach(() => {
// Clean up mocks after each test
jest.clearAllMocks();
});
```
```typescript
// tests/setup/integration.ts - Integration test setup
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
beforeAll(async () => {
// Connect to test database
await prisma.$connect();
});
afterAll(async () => {
// Cleanup and disconnect
await prisma.$disconnect();
});
beforeEach(async () => {
// Clean test data before each test
// Add your cleanup logic here
});
```
```typescript
// tests/teardown.ts - Global teardown
export default async () => {
// Global cleanup after all tests
console.log('Global test teardown complete');
};
```
**Step 5: Create Initial Test Structure**
```bash
# Create test directories
mkdir -p tests/{setup,fixtures,unit,integration,e2e}
mkdir -p tests/unit/src/{utils,services,middleware}
# Create sample test fixtures
mkdir tests/fixtures
```
### **Generic Testing Framework Setup (Any Language)**
#### **Framework Selection Guide**
**Python Projects:**
- **pytest**: Recommended for most Python projects
- **unittest**: Built-in, suitable for simple projects
- **Coverage**: Use `coverage.py` for code coverage
```bash
# Python setup example
pip install pytest pytest-cov
echo "[tool:pytest]" > pytest.ini
echo "testpaths = tests" >> pytest.ini
echo "addopts = --cov=src --cov-report=html --cov-report=term" >> pytest.ini
```
**Go Projects:**
- **Built-in testing**: Use Go's built-in `testing` package
- **Coverage**: Built-in with `go test -cover`
```bash
# Go setup example
go mod init your-project
mkdir -p tests
# Tests are typically *_test.go files alongside source
```
**Rust Projects:**
- **Built-in testing**: Use Rust's built-in test framework
- **cargo-tarpaulin**: For coverage analysis
```bash
# Rust setup example
cargo new your-project
cd your-project
cargo install cargo-tarpaulin # For coverage
```
**Java Projects:**
- **JUnit 5**: Modern testing framework
- **Maven/Gradle**: Build tools with testing integration
```xml
<!-- Maven pom.xml example -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.9.2</version>
<scope>test</scope>
</dependency>
```
#### **Universal Testing Principles**
**Coverage Standards (Adapt to Your Language):**
- **Global Minimum**: 70-80% line coverage
- **Critical Code**: 85-90% coverage
- **New Features**: Must meet or exceed standards
- **Legacy Code**: Gradual improvement strategy
**Test Organization:**
- **Unit Tests**: Fast, isolated, no external dependencies
- **Integration Tests**: Test component interactions
- **E2E Tests**: Test complete user workflows
- **Performance Tests**: Load and stress testing (if applicable)
**Naming Conventions:**
- **Test Files**: `*.test.*`, `*_test.*`, or language-specific patterns
- **Test Functions**: Descriptive names (e.g., `should_return_error_for_invalid_input`)
- **Test Directories**: Organized by test type and mirroring source structure
#### **TaskMaster Integration for Any Framework**
**Document Testing Setup in Subtasks:**
```bash
# Update subtask with testing framework setup
task-master update-subtask --id=X.Y --prompt="Testing framework setup:
- Installed [Framework Name] with coverage support
- Configured [Coverage Tool] with thresholds: 80% lines, 70% branches
- Created test directory structure: unit/, integration/, e2e/
- Added test scripts to build configuration
- All setup tests passing"
```
**Testing Framework Verification:**
```bash
# Verify setup works
[test-command] # e.g., npm test, pytest, go test, cargo test
# Check coverage reporting
[coverage-command] # e.g., npm run test:coverage
# Update task with verification
task-master update-subtask --id=X.Y --prompt="Testing framework verified:
- Sample tests running successfully
- Coverage reporting functional
- CI/CD integration ready
- Ready to begin TDD workflow"
```
## **Test-Driven Development (TDD) Integration**
### **Core TDD Cycle with Jest**
```bash
# 1. Start development with watch mode
npm run test:watch
# 2. Write failing test first
# Create test file: src/utils/newFeature.test.ts
# Write test that describes expected behavior
# 3. Implement minimum code to make test pass
# 4. Refactor while keeping tests green
# 5. Add edge cases and error scenarios
```
### **TDD Workflow Per Subtask**
```bash
# When starting a new subtask:
task-master set-status --id=4.1 --status=in-progress
# Begin TDD cycle:
npm run test:watch # Keep running during development
# Document TDD progress in subtask:
task-master update-subtask --id=4.1 --prompt="TDD Progress:
- Written 3 failing tests for core functionality
- Implemented basic feature, tests now passing
- Adding edge case tests for error handling"
# Complete subtask with test summary:
task-master update-subtask --id=4.1 --prompt="Implementation complete:
- Feature implemented with 8 unit tests
- Coverage: 95% statements, 88% branches
- All tests passing, TDD cycle complete"
```
## **Testing Commands & Usage**
### **Development Commands**
```bash
# Primary development command - use during coding
npm run test:watch # Watch mode with Jest
npm run test:watch -- --testNamePattern="auth" # Watch specific tests
# Targeted testing during development
npm run test:unit # Run only unit tests
npm run test:unit -- --coverage # Unit tests with coverage
# Integration testing when APIs are ready
npm run test:integration # Run integration tests
npm run test:integration -- --detectOpenHandles # Debug hanging tests
# End-to-end testing for workflows
npm run test:e2e # Run E2E tests
npm run test:e2e -- --timeout=30000 # Extended timeout for E2E
```
### **Quality Assurance Commands**
```bash
# Full test suite with coverage (before commits)
npm run test:coverage # Complete coverage analysis
# All tests (CI/CD pipeline)
npm test # Run all test projects
# Specific test file execution
npm test -- auth.test.ts # Run specific test file
npm test -- --testNamePattern="should handle errors" # Run specific tests
```
## **Test Implementation Patterns**
### **Unit Test Development**
```typescript
// ✅ DO: Follow established patterns from auth.test.ts
describe('FeatureName', () => {
beforeEach(() => {
jest.clearAllMocks();
// Setup mocks with proper typing
});
describe('functionName', () => {
it('should handle normal case', () => {
// Test implementation with specific assertions
});
it('should throw error for invalid input', async () => {
// Error scenario testing
await expect(functionName(invalidInput))
.rejects.toThrow('Specific error message');
});
});
});
```
### **Integration Test Development**
```typescript
// ✅ DO: Use supertest for API endpoint testing
import request from 'supertest';
import { app } from '../../src/app';
describe('POST /api/auth/register', () => {
beforeEach(async () => {
await integrationTestUtils.cleanupTestData();
});
it('should register user successfully', async () => {
const userData = createTestUser();
const response = await request(app)
.post('/api/auth/register')
.send(userData)
.expect(201);
expect(response.body).toMatchObject({
id: expect.any(String),
email: userData.email
});
// Verify database state
const user = await prisma.user.findUnique({
where: { email: userData.email }
});
expect(user).toBeTruthy();
});
});
```
### **E2E Test Development**
```typescript
// ✅ DO: Test complete user workflows
describe('User Authentication Flow', () => {
it('should complete registration → login → protected access', async () => {
// Step 1: Register
const userData = createTestUser();
await request(app)
.post('/api/auth/register')
.send(userData)
.expect(201);
// Step 2: Login
const loginResponse = await request(app)
.post('/api/auth/login')
.send({ email: userData.email, password: userData.password })
.expect(200);
const { token } = loginResponse.body;
// Step 3: Access protected resource
await request(app)
.get('/api/profile')
.set('Authorization', `Bearer ${token}`)
.expect(200);
}, 30000); // Extended timeout for E2E
});
```
## **Mocking & Test Utilities**
### **Established Mocking Patterns**
```typescript
// ✅ DO: Use established bcrypt mocking pattern
jest.mock('bcrypt');
import bcrypt from 'bcrypt';
const mockHash = bcrypt.hash as jest.MockedFunction<typeof bcrypt.hash>;
const mockCompare = bcrypt.compare as jest.MockedFunction<typeof bcrypt.compare>;
// ✅ DO: Use Prisma mocking for unit tests
jest.mock('@prisma/client', () => ({
PrismaClient: jest.fn().mockImplementation(() => ({
user: {
create: jest.fn(),
findUnique: jest.fn(),
},
$connect: jest.fn(),
$disconnect: jest.fn(),
})),
}));
```
### **Test Fixtures Usage**
```typescript
// ✅ DO: Use centralized test fixtures
import { createTestUser, adminUser, invalidUser } from '../fixtures/users';
describe('User Service', () => {
it('should handle admin user creation', async () => {
const userData = createTestUser(adminUser);
// Test implementation
});
it('should reject invalid user data', async () => {
const userData = createTestUser(invalidUser);
// Error testing
});
});
```
## **Coverage Standards & Monitoring**
### **Coverage Thresholds**
- **Global Standards**: 80% lines/functions, 70% branches
- **Critical Code**: 90% utils, 85% middleware
- **New Features**: Must meet or exceed global thresholds
- **Legacy Code**: Gradual improvement with each change
### **Coverage Reporting & Analysis**
```bash
# Generate coverage reports
npm run test:coverage
# View detailed HTML report
open coverage/lcov-report/index.html
# Coverage files generated:
# - coverage/lcov-report/index.html # Detailed HTML report
# - coverage/lcov.info # LCOV format for IDE integration
# - coverage/coverage-final.json # JSON format for tooling
```
### **Coverage Quality Checks**
```typescript
// ✅ DO: Test all code paths
describe('validateInput', () => {
it('should return true for valid input', () => {
expect(validateInput('valid')).toBe(true);
});
it('should return false for various invalid inputs', () => {
expect(validateInput('')).toBe(false); // Empty string
expect(validateInput(null)).toBe(false); // Null value
expect(validateInput(undefined)).toBe(false); // Undefined
});
it('should throw for unexpected input types', () => {
expect(() => validateInput(123)).toThrow('Invalid input type');
});
});
```
## **Testing During Development Phases**
### **Feature Development Phase**
```bash
# 1. Start feature development
task-master set-status --id=X.Y --status=in-progress
# 2. Begin TDD cycle
npm run test:watch
# 3. Document test progress in subtask
task-master update-subtask --id=X.Y --prompt="Test development:
- Created test file with 5 failing tests
- Implemented core functionality
- Tests passing, adding error scenarios"
# 4. Verify coverage before completion
npm run test:coverage
# 5. Update subtask with final test status
task-master update-subtask --id=X.Y --prompt="Testing complete:
- 12 unit tests with full coverage
- All edge cases and error scenarios covered
- Ready for integration testing"
```
### **Integration Testing Phase**
```bash
# After API endpoints are implemented
npm run test:integration
# Update integration test templates
# Replace placeholder tests with real endpoint calls
# Document integration test results
task-master update-subtask --id=X.Y --prompt="Integration tests:
- Updated auth endpoint tests
- Database integration verified
- All HTTP status codes and responses tested"
```
### **Pre-Commit Testing Phase**
```bash
# Before committing code
npm run test:coverage # Verify all tests pass with coverage
npm run test:unit # Quick unit test verification
npm run test:integration # Integration test verification (if applicable)
# Commit pattern for test updates
git add tests/ src/**/*.test.ts
git commit -m "test(task-X): Add comprehensive tests for Feature Y
- Unit tests with 95% coverage (exceeds 90% threshold)
- Integration tests for API endpoints
- Test fixtures for data generation
- Proper mocking patterns established
Task X: Feature Y - Testing complete"
```
## **Error Handling & Debugging**
### **Test Debugging Techniques**
```typescript
// ✅ DO: Use test utilities for debugging
import { testUtils } from '../setup';
it('should debug complex operation', () => {
testUtils.withConsole(() => {
// Console output visible only for this test
console.log('Debug info:', complexData);
service.complexOperation();
});
});
// ✅ DO: Use proper async debugging
it('should handle async operations', async () => {
const promise = service.asyncOperation();
// Test intermediate state
expect(service.isProcessing()).toBe(true);
const result = await promise;
expect(result).toBe('expected');
expect(service.isProcessing()).toBe(false);
});
```
### **Common Test Issues & Solutions**
```bash
# Hanging tests (common with database connections)
npm run test:integration -- --detectOpenHandles
# Memory leaks in tests
npm run test:unit -- --logHeapUsage
# Slow tests identification
npm run test:coverage -- --verbose
# Mock not working properly
# Check: mock is declared before imports
# Check: jest.clearAllMocks() in beforeEach
# Check: TypeScript typing is correct
```
## **Continuous Integration Integration**
### **CI/CD Pipeline Testing**
```yaml
# Example GitHub Actions integration
- name: Run tests
run: |
npm ci
npm run test:coverage
- name: Upload coverage reports
uses: codecov/codecov-action@v3
with:
file: ./coverage/lcov.info
```
### **Pre-commit Hooks**
```bash
# Setup pre-commit testing (recommended)
# In package.json scripts:
"pre-commit": "npm run test:unit && npm run test:integration"
# Husky integration example:
npx husky add .husky/pre-commit "npm run test:unit"
```
## **Test Maintenance & Evolution**
### **Adding Tests for New Features**
1. **Create test file** alongside source code or in `tests/unit/`
2. **Follow established patterns** from `src/utils/auth.test.ts`
3. **Use existing fixtures** from `tests/fixtures/`
4. **Apply proper mocking** patterns for dependencies
5. **Meet coverage thresholds** for the module
### **Updating Integration/E2E Tests**
1. **Update templates** in `tests/integration/` when APIs change
2. **Modify E2E workflows** in `tests/e2e/` for new user journeys
3. **Update test fixtures** for new data requirements
4. **Maintain database cleanup** utilities
### **Test Performance Optimization**
- **Parallel execution**: Jest runs tests in parallel by default
- **Test isolation**: Use proper setup/teardown for independence
- **Mock optimization**: Mock heavy dependencies appropriately
- **Database efficiency**: Use transaction rollbacks where possible
---
**Key References:**
- [Testing Standards](mdc:.cursor/rules/tests.mdc)
- [Git Workflow](mdc:.cursor/rules/git_workflow.mdc)
- [Development Workflow](mdc:.cursor/rules/dev_workflow.mdc)
- [Jest Configuration](mdc:jest.config.js)

102
.github/scripts/check-pre-release-mode.mjs vendored Executable file
View File

@@ -0,0 +1,102 @@
#!/usr/bin/env node
import { readFileSync, existsSync } from 'node:fs';
import { join, dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Get context from command line argument or environment
const context = process.argv[2] || process.env.GITHUB_WORKFLOW || 'manual';
function findRootDir(startDir) {
let currentDir = resolve(startDir);
while (currentDir !== '/') {
if (existsSync(join(currentDir, 'package.json'))) {
try {
const pkg = JSON.parse(
readFileSync(join(currentDir, 'package.json'), 'utf8')
);
if (pkg.name === 'task-master-ai' || pkg.repository) {
return currentDir;
}
} catch {}
}
currentDir = dirname(currentDir);
}
throw new Error('Could not find root directory');
}
function checkPreReleaseMode() {
console.log('🔍 Checking if branch is in pre-release mode...');
const rootDir = findRootDir(__dirname);
const preJsonPath = join(rootDir, '.changeset', 'pre.json');
// Check if pre.json exists
if (!existsSync(preJsonPath)) {
console.log('✅ Not in active pre-release mode - safe to proceed');
process.exit(0);
}
try {
// Read and parse pre.json
const preJsonContent = readFileSync(preJsonPath, 'utf8');
const preJson = JSON.parse(preJsonContent);
// Check if we're in active pre-release mode
if (preJson.mode === 'pre') {
console.error('❌ ERROR: This branch is in active pre-release mode!');
console.error('');
// Provide context-specific error messages
if (context === 'Release Check' || context === 'pull_request') {
console.error(
'Pre-release mode must be exited before merging to main.'
);
console.error('');
console.error(
'To fix this, run the following commands in your branch:'
);
console.error(' npx changeset pre exit');
console.error(' git add -u');
console.error(' git commit -m "chore: exit pre-release mode"');
console.error(' git push');
console.error('');
console.error('Then update this pull request.');
} else if (context === 'Release' || context === 'main') {
console.error(
'Pre-release mode should only be used on feature branches, not main.'
);
console.error('');
console.error('To fix this, run the following commands locally:');
console.error(' npx changeset pre exit');
console.error(' git add -u');
console.error(' git commit -m "chore: exit pre-release mode"');
console.error(' git push origin main');
console.error('');
console.error('Then re-run this workflow.');
} else {
console.error('Pre-release mode must be exited before proceeding.');
console.error('');
console.error('To fix this, run the following commands:');
console.error(' npx changeset pre exit');
console.error(' git add -u');
console.error(' git commit -m "chore: exit pre-release mode"');
console.error(' git push');
}
process.exit(1);
}
console.log('✅ Not in active pre-release mode - safe to proceed');
process.exit(0);
} catch (error) {
console.error(`❌ ERROR: Unable to parse .changeset/pre.json aborting.`);
console.error(`Error details: ${error.message}`);
process.exit(1);
}
}
// Run the check
checkPreReleaseMode();

54
.github/scripts/pre-release.mjs vendored Executable file
View File

@@ -0,0 +1,54 @@
#!/usr/bin/env node
import { readFileSync, existsSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import {
findRootDir,
runCommand,
getPackageVersion,
createAndPushTag
} from './utils.mjs';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const rootDir = findRootDir(__dirname);
const extensionPkgPath = join(rootDir, 'apps', 'extension', 'package.json');
console.log('🚀 Starting pre-release process...');
// Check if we're in RC mode
const preJsonPath = join(rootDir, '.changeset', 'pre.json');
if (!existsSync(preJsonPath)) {
console.error('⚠️ Not in RC mode. Run "npx changeset pre enter rc" first.');
process.exit(1);
}
try {
const preJson = JSON.parse(readFileSync(preJsonPath, 'utf8'));
if (preJson.tag !== 'rc') {
console.error(`⚠️ Not in RC mode. Current tag: ${preJson.tag}`);
process.exit(1);
}
} catch (error) {
console.error('Failed to read pre.json:', error.message);
process.exit(1);
}
// Get current extension version
const extensionVersion = getPackageVersion(extensionPkgPath);
console.log(`Extension version: ${extensionVersion}`);
// Run changeset publish for npm packages
console.log('📦 Publishing npm packages...');
runCommand('npx', ['changeset', 'publish']);
// Create tag for extension pre-release if it doesn't exist
const extensionTag = `extension-rc@${extensionVersion}`;
const tagCreated = createAndPushTag(extensionTag);
if (tagCreated) {
console.log('This will trigger the extension-pre-release workflow...');
}
console.log('✅ Pre-release process completed!');

30
.github/scripts/release.mjs vendored Executable file
View File

@@ -0,0 +1,30 @@
#!/usr/bin/env node
import { existsSync, unlinkSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { findRootDir, runCommand } from './utils.mjs';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const rootDir = findRootDir(__dirname);
console.log('🚀 Starting release process...');
// Double-check we're not in pre-release mode (safety net)
const preJsonPath = join(rootDir, '.changeset', 'pre.json');
if (existsSync(preJsonPath)) {
console.log('⚠️ Warning: pre.json still exists. Removing it...');
unlinkSync(preJsonPath);
}
// Check if the extension version has changed and tag it
// This prevents changeset from trying to publish the private package
runCommand('node', [join(__dirname, 'tag-extension.mjs')]);
// Run changeset publish for npm packages
runCommand('npx', ['changeset', 'publish']);
console.log('✅ Release process completed!');
// The extension tag (if created) will trigger the extension-release workflow

33
.github/scripts/tag-extension.mjs vendored Executable file
View File

@@ -0,0 +1,33 @@
#!/usr/bin/env node
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { findRootDir, createAndPushTag } from './utils.mjs';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const rootDir = findRootDir(__dirname);
// Read the extension's package.json
const extensionDir = join(rootDir, 'apps', 'extension');
const pkgPath = join(extensionDir, 'package.json');
let pkg;
try {
const pkgContent = readFileSync(pkgPath, 'utf8');
pkg = JSON.parse(pkgContent);
} catch (error) {
console.error('Failed to read package.json:', error.message);
process.exit(1);
}
// Ensure we have required fields
assert(pkg.name, 'package.json must have a name field');
assert(pkg.version, 'package.json must have a version field');
const tag = `${pkg.name}@${pkg.version}`;
// Create and push the tag if it doesn't exist
createAndPushTag(tag);

88
.github/scripts/utils.mjs vendored Executable file
View File

@@ -0,0 +1,88 @@
#!/usr/bin/env node
import { spawnSync } from 'node:child_process';
import { readFileSync } from 'node:fs';
import { join, dirname, resolve } from 'node:path';
// Find the root directory by looking for package.json with task-master-ai
export function findRootDir(startDir) {
let currentDir = resolve(startDir);
while (currentDir !== '/') {
const pkgPath = join(currentDir, 'package.json');
try {
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
if (pkg.name === 'task-master-ai' || pkg.repository) {
return currentDir;
}
} catch {}
currentDir = dirname(currentDir);
}
throw new Error('Could not find root directory');
}
// Run a command with proper error handling
export function runCommand(command, args = [], options = {}) {
console.log(`Running: ${command} ${args.join(' ')}`);
const result = spawnSync(command, args, {
encoding: 'utf8',
stdio: 'inherit',
...options
});
if (result.status !== 0) {
console.error(`Command failed with exit code ${result.status}`);
process.exit(result.status);
}
return result;
}
// Get package version from a package.json file
export function getPackageVersion(packagePath) {
try {
const pkg = JSON.parse(readFileSync(packagePath, 'utf8'));
return pkg.version;
} catch (error) {
console.error(
`Failed to read package version from ${packagePath}:`,
error.message
);
process.exit(1);
}
}
// Check if a git tag exists on remote
export function tagExistsOnRemote(tag, remote = 'origin') {
const result = spawnSync('git', ['ls-remote', remote, tag], {
encoding: 'utf8'
});
return result.status === 0 && result.stdout.trim() !== '';
}
// Create and push a git tag if it doesn't exist
export function createAndPushTag(tag, remote = 'origin') {
// Check if tag already exists
if (tagExistsOnRemote(tag, remote)) {
console.log(`Tag ${tag} already exists on remote, skipping`);
return false;
}
console.log(`Creating new tag: ${tag}`);
// Create the tag locally
const tagResult = spawnSync('git', ['tag', tag]);
if (tagResult.status !== 0) {
console.error('Failed to create tag:', tagResult.error || tagResult.stderr);
process.exit(1);
}
// Push the tag to remote
const pushResult = spawnSync('git', ['push', remote, tag]);
if (pushResult.status !== 0) {
console.error('Failed to push tag:', pushResult.error || pushResult.stderr);
process.exit(1);
}
console.log(`✅ Successfully created and pushed tag: ${tag}`);
return true;
}

View File

@@ -22,8 +22,6 @@ permissions:
jobs:
setup:
runs-on: ubuntu-latest
outputs:
cache-key: ${{ steps.cache-key.outputs.key }}
steps:
- uses: actions/checkout@v4
with:
@@ -33,30 +31,22 @@ jobs:
with:
node-version: 20
- uses: pnpm/action-setup@v4
with:
version: latest
- name: Generate cache key
id: cache-key
run: echo "key=${{ runner.os }}-extension-pnpm-${{ hashFiles('apps/extension/pnpm-lock.yaml') }}" >> $GITHUB_OUTPUT
- name: Cache pnpm dependencies
- name: Cache node_modules
uses: actions/cache@v4
with:
path: |
~/.pnpm-store
apps/extension/node_modules
key: ${{ steps.cache-key.outputs.key }}
node_modules
*/*/node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-extension-pnpm-
${{ runner.os }}-node-
- name: Install Extension Dependencies
working-directory: apps/extension
run: pnpm install --frozen-lockfile
run: npm ci
timeout-minutes: 5
lint-and-typecheck:
typecheck:
needs: setup
runs-on: ubuntu-latest
steps:
@@ -66,32 +56,25 @@ jobs:
with:
node-version: 20
- uses: pnpm/action-setup@v4
with:
version: latest
- name: Restore pnpm dependencies
- name: Restore node_modules
uses: actions/cache@v4
with:
path: |
~/.pnpm-store
apps/extension/node_modules
key: ${{ needs.setup.outputs.cache-key }}
node_modules
*/*/node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Install if cache miss
working-directory: apps/extension
run: pnpm install --frozen-lockfile --prefer-offline
run: npm ci
timeout-minutes: 3
- name: Lint Extension
working-directory: apps/extension
run: pnpm run lint
env:
FORCE_COLOR: 1
- name: Type Check Extension
working-directory: apps/extension
run: pnpm run check-types
run: npm run check-types
env:
FORCE_COLOR: 1
@@ -105,32 +88,31 @@ jobs:
with:
node-version: 20
- uses: pnpm/action-setup@v4
with:
version: latest
- name: Restore pnpm dependencies
- name: Restore node_modules
uses: actions/cache@v4
with:
path: |
~/.pnpm-store
apps/extension/node_modules
key: ${{ needs.setup.outputs.cache-key }}
node_modules
*/*/node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Install if cache miss
working-directory: apps/extension
run: pnpm install --frozen-lockfile --prefer-offline
run: npm ci
timeout-minutes: 3
- name: Build Extension
working-directory: apps/extension
run: pnpm run build
run: npm run build
env:
FORCE_COLOR: 1
- name: Package Extension
working-directory: apps/extension
run: pnpm run package
run: npm run package
env:
FORCE_COLOR: 1
@@ -146,7 +128,7 @@ jobs:
- name: Create VSIX Package (Test)
working-directory: apps/extension/vsix-build
run: pnpm exec vsce package --no-dependencies
run: npx vsce package --no-dependencies
env:
FORCE_COLOR: 1
@@ -159,45 +141,3 @@ jobs:
apps/extension/dist/
retention-days: 30
test:
needs: setup
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- uses: pnpm/action-setup@v4
with:
version: latest
- name: Restore pnpm dependencies
uses: actions/cache@v4
with:
path: |
~/.pnpm-store
apps/extension/node_modules
key: ${{ needs.setup.outputs.cache-key }}
- name: Install if cache miss
working-directory: apps/extension
run: pnpm install --frozen-lockfile --prefer-offline
timeout-minutes: 3
- name: Run Extension Tests
working-directory: apps/extension
run: xvfb-run -a pnpm run test
env:
CI: true
FORCE_COLOR: 1
timeout-minutes: 10
- name: Upload Test Results
if: always()
uses: actions/upload-artifact@v4
with:
name: extension-test-results
path: apps/extension/test-results
retention-days: 30

View File

@@ -0,0 +1,110 @@
name: Extension Pre-Release
on:
push:
tags:
- "extension-rc@*"
permissions:
contents: write
concurrency: extension-pre-release-${{ github.ref }}
jobs:
publish-extension-rc:
runs-on: ubuntu-latest
environment: extension-release
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Cache node_modules
uses: actions/cache@v4
with:
path: |
node_modules
*/*/node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Install Extension Dependencies
working-directory: apps/extension
run: npm ci
timeout-minutes: 5
- name: Type Check Extension
working-directory: apps/extension
run: npm run check-types
env:
FORCE_COLOR: 1
- name: Build Extension
working-directory: apps/extension
run: npm run build
env:
FORCE_COLOR: 1
- name: Package Extension
working-directory: apps/extension
run: npm run package
env:
FORCE_COLOR: 1
- name: Create VSIX Package (Pre-Release)
working-directory: apps/extension/vsix-build
run: npx vsce package --no-dependencies --pre-release
env:
FORCE_COLOR: 1
- name: Get VSIX filename
id: vsix-info
working-directory: apps/extension/vsix-build
run: |
VSIX_FILE=$(find . -maxdepth 1 -name "*.vsix" -type f | head -n1 | xargs basename)
if [ -z "$VSIX_FILE" ]; then
echo "Error: No VSIX file found"
exit 1
fi
echo "vsix-filename=$VSIX_FILE" >> "$GITHUB_OUTPUT"
echo "Found VSIX: $VSIX_FILE"
- name: Publish to VS Code Marketplace (Pre-Release)
working-directory: apps/extension/vsix-build
run: npx vsce publish --packagePath "${{ steps.vsix-info.outputs.vsix-filename }}" --pre-release
env:
VSCE_PAT: ${{ secrets.VSCE_PAT }}
FORCE_COLOR: 1
- name: Install Open VSX CLI
run: npm install -g ovsx
- name: Publish to Open VSX Registry (Pre-Release)
working-directory: apps/extension/vsix-build
run: ovsx publish "${{ steps.vsix-info.outputs.vsix-filename }}" --pre-release
env:
OVSX_PAT: ${{ secrets.OVSX_PAT }}
FORCE_COLOR: 1
- name: Upload Build Artifacts
uses: actions/upload-artifact@v4
with:
name: extension-pre-release-${{ github.ref_name }}
path: |
apps/extension/vsix-build/*.vsix
apps/extension/dist/
retention-days: 30
notify-success:
needs: publish-extension-rc
if: success()
runs-on: ubuntu-latest
steps:
- name: Success Notification
run: |
echo "🚀 Extension ${{ github.ref_name }} successfully published as pre-release!"
echo "📦 Available on VS Code Marketplace (Pre-Release)"
echo "🌍 Available on Open VSX Registry (Pre-Release)"

View File

@@ -2,145 +2,61 @@ name: Extension Release
on:
push:
branches:
- main
paths:
- 'apps/extension/**'
workflow_dispatch:
inputs:
force_publish:
description: 'Force publish even without version changes'
required: false
default: false
type: boolean
tags:
- "extension@*"
permissions:
contents: read
contents: write
concurrency: extension-release-${{ github.ref }}
jobs:
check-version:
runs-on: ubuntu-latest
outputs:
should-publish: ${{ steps.version-check.outputs.should-publish }}
current-version: ${{ steps.version-check.outputs.current-version }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check version changes
id: version-check
run: |
# Get current version from package.json
CURRENT_VERSION=$(jq -r '.version' apps/extension/package.json)
echo "current-version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
# Check if this is a force publish
if [ "${{ github.event.inputs.force_publish }}" = "true" ]; then
echo "should-publish=true" >> $GITHUB_OUTPUT
echo "Force publish requested"
exit 0
fi
# Check if version changed in the last commit
if git diff HEAD~1 HEAD --name-only | grep -q "apps/extension/package.json\|apps/extension/package.publish.json"; then
# Check if version field actually changed
PREV_VERSION=$(git show HEAD~1:apps/extension/package.json | jq -r '.version')
if [ "$CURRENT_VERSION" != "$PREV_VERSION" ]; then
echo "should-publish=true" >> $GITHUB_OUTPUT
echo "Version changed from $PREV_VERSION to $CURRENT_VERSION"
else
echo "should-publish=false" >> $GITHUB_OUTPUT
echo "No version change detected"
fi
else
echo "should-publish=false" >> $GITHUB_OUTPUT
echo "No package.json changes detected"
fi
build-and-publish:
needs: check-version
if: needs.check-version.outputs.should-publish == 'true'
publish-extension:
runs-on: ubuntu-latest
environment: extension-release
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 20
- uses: pnpm/action-setup@v4
with:
version: latest
- name: Cache pnpm dependencies
- name: Cache node_modules
uses: actions/cache@v4
with:
path: |
~/.pnpm-store
apps/extension/node_modules
key: ${{ runner.os }}-extension-pnpm-${{ hashFiles('apps/extension/pnpm-lock.yaml') }}
node_modules
*/*/node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-extension-pnpm-
${{ runner.os }}-node-
- name: Install Extension Dependencies
working-directory: apps/extension
run: pnpm install --frozen-lockfile
run: npm ci
timeout-minutes: 5
- name: Run Tests
working-directory: apps/extension
run: xvfb-run -a pnpm run test
env:
CI: true
FORCE_COLOR: 1
timeout-minutes: 10
- name: Lint Extension
working-directory: apps/extension
run: pnpm run lint
env:
FORCE_COLOR: 1
- name: Type Check Extension
working-directory: apps/extension
run: pnpm run check-types
run: npm run check-types
env:
FORCE_COLOR: 1
- name: Build Extension
working-directory: apps/extension
run: pnpm run build
run: npm run build
env:
FORCE_COLOR: 1
- name: Package Extension
working-directory: apps/extension
run: pnpm run package
run: npm run package
env:
FORCE_COLOR: 1
- name: Verify Package Structure
working-directory: apps/extension
run: |
echo "=== Checking vsix-build structure ==="
ls -la vsix-build/
echo "=== Checking dist contents ==="
ls -la vsix-build/dist/
echo "=== Verifying required files ==="
test -f vsix-build/package.json || (echo "Missing package.json" && exit 1)
test -f vsix-build/dist/extension.js || (echo "Missing extension.js" && exit 1)
echo "=== Checking package.json content ==="
cat vsix-build/package.json | jq '.name, .version, .publisher'
- name: Create VSIX Package
working-directory: apps/extension/vsix-build
run: pnpm exec vsce package --no-dependencies
run: npx vsce package --no-dependencies
env:
FORCE_COLOR: 1
@@ -148,19 +64,17 @@ jobs:
id: vsix-info
working-directory: apps/extension/vsix-build
run: |
VSIX_FILE=$(ls *.vsix)
echo "vsix-filename=$VSIX_FILE" >> $GITHUB_OUTPUT
VSIX_FILE=$(find . -maxdepth 1 -name "*.vsix" -type f | head -n1 | xargs basename)
if [ -z "$VSIX_FILE" ]; then
echo "Error: No VSIX file found"
exit 1
fi
echo "vsix-filename=$VSIX_FILE" >> "$GITHUB_OUTPUT"
echo "Found VSIX: $VSIX_FILE"
- name: Validate VSIX Package
working-directory: apps/extension/vsix-build
run: |
echo "=== VSIX Package Contents ==="
unzip -l "${{ steps.vsix-info.outputs.vsix-filename }}"
- name: Publish to VS Code Marketplace
working-directory: apps/extension/vsix-build
run: pnpm exec vsce publish --packagePath "${{ steps.vsix-info.outputs.vsix-filename }}"
run: npx vsce publish --packagePath "${{ steps.vsix-info.outputs.vsix-filename }}"
env:
VSCE_PAT: ${{ secrets.VSCE_PAT }}
FORCE_COLOR: 1
@@ -175,66 +89,23 @@ jobs:
OVSX_PAT: ${{ secrets.OVSX_PAT }}
FORCE_COLOR: 1
- name: Create GitHub Release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: extension-v${{ needs.check-version.outputs.current-version }}
release_name: Extension v${{ needs.check-version.outputs.current-version }}
body: |
VS Code Extension Release v${{ needs.check-version.outputs.current-version }}
**Changes in this release:**
- Published to VS Code Marketplace
- Published to Open VSX Registry
- Extension package: `${{ steps.vsix-info.outputs.vsix-filename }}`
**Installation:**
- Install from VS Code Marketplace: [Task Master Kanban](https://marketplace.visualstudio.com/items?itemName=[TBD])
- Install from Open VSX Registry: [Task Master Kanban](https://open-vsx.org/extension/[TBD])
- Or download the VSIX file below and install manually
draft: false
prerelease: false
- name: Upload VSIX to Release
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: apps/extension/vsix-build/${{ steps.vsix-info.outputs.vsix-filename }}
asset_name: ${{ steps.vsix-info.outputs.vsix-filename }}
asset_content_type: application/zip
- name: Upload Build Artifacts
uses: actions/upload-artifact@v4
with:
name: extension-release-v${{ needs.check-version.outputs.current-version }}
name: extension-release-${{ github.ref_name }}
path: |
apps/extension/vsix-build/*.vsix
apps/extension/dist/
retention-days: 90
notify-success:
needs: [check-version, build-and-publish]
if: success() && needs.check-version.outputs.should-publish == 'true'
needs: publish-extension
if: success()
runs-on: ubuntu-latest
steps:
- name: Success Notification
run: |
echo "🎉 Extension v${{ needs.check-version.outputs.current-version }} successfully published!"
echo "🎉 Extension ${{ github.ref_name }} successfully published!"
echo "📦 Available on VS Code Marketplace"
echo "🌍 Available on Open VSX Registry"
echo "🏷️ GitHub release created: extension-v${{ needs.check-version.outputs.current-version }}"
notify-skipped:
needs: check-version
if: needs.check-version.outputs.should-publish == 'false'
runs-on: ubuntu-latest
steps:
- name: Skip Notification
run: |
echo " Extension publish skipped - no version changes detected"
echo "Current version: ${{ needs.check-version.outputs.current-version }}"
echo "To force publish, use workflow_dispatch with force_publish=true"
echo "🏷️ GitHub release created: ${{ github.ref_name }}"

View File

@@ -3,11 +3,13 @@ name: Pre-Release (RC)
on:
workflow_dispatch: # Allows manual triggering from GitHub UI/API
concurrency: pre-release-${{ github.ref }}
concurrency: pre-release-${{ github.ref_name }}
jobs:
rc:
runs-on: ubuntu-latest
# Only allow pre-releases on non-main branches
if: github.ref != 'refs/heads/main'
environment: extension-release
steps:
- uses: actions/checkout@v4
with:
@@ -34,9 +36,26 @@ jobs:
- name: Enter RC mode (if not already in RC mode)
run: |
# ensure were in the right pre-mode (tag "rc")
if [ ! -f .changeset/pre.json ] \
|| [ "$(jq -r '.tag' .changeset/pre.json 2>/dev/null || echo '')" != "rc" ]; then
# Check if we're in pre-release mode with the "rc" tag
if [ -f .changeset/pre.json ]; then
MODE=$(jq -r '.mode' .changeset/pre.json 2>/dev/null || echo '')
TAG=$(jq -r '.tag' .changeset/pre.json 2>/dev/null || echo '')
if [ "$MODE" = "exit" ]; then
echo "Pre-release mode is in 'exit' state, re-entering RC mode..."
npx changeset pre enter rc
elif [ "$MODE" = "pre" ] && [ "$TAG" != "rc" ]; then
echo "In pre-release mode but with wrong tag ($TAG), switching to RC..."
npx changeset pre exit
npx changeset pre enter rc
elif [ "$MODE" = "pre" ] && [ "$TAG" = "rc" ]; then
echo "Already in RC pre-release mode"
else
echo "Unknown mode state: $MODE, entering RC mode..."
npx changeset pre enter rc
fi
else
echo "No pre.json found, entering RC mode..."
npx changeset pre enter rc
fi
@@ -49,10 +68,12 @@ jobs:
- name: Create Release Candidate Pull Request or Publish Release Candidate to npm
uses: changesets/action@v1
with:
publish: npm run release
publish: node ./.github/scripts/pre-release.mjs
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
VSCE_PAT: ${{ secrets.VSCE_PAT }}
OVSX_PAT: ${{ secrets.OVSX_PAT }}
- name: Commit & Push changes
uses: actions-js/push@master

21
.github/workflows/release-check.yml vendored Normal file
View File

@@ -0,0 +1,21 @@
name: Release Check
on:
pull_request:
branches:
- main
concurrency:
group: release-check-${{ github.head_ref }}
cancel-in-progress: true
jobs:
check-release-mode:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check release mode
run: node ./.github/scripts/check-pre-release-mode.mjs "pull_request"

View File

@@ -6,6 +6,11 @@ on:
concurrency: ${{ github.workflow }}-${{ github.ref }}
permissions:
contents: write
pull-requests: write
id-token: write
jobs:
release:
runs-on: ubuntu-latest
@@ -33,13 +38,13 @@ jobs:
run: npm ci
timeout-minutes: 2
- name: Exit pre-release mode (safety check)
run: npx changeset pre exit || true
- name: Check pre-release mode
run: node ./.github/scripts/check-pre-release-mode.mjs "main"
- name: Create Release Pull Request or Publish to npm
uses: changesets/action@v1
with:
publish: npm run release
publish: node ./.github/scripts/release.mjs
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@@ -1,69 +0,0 @@
name: Version & Publish
on:
push:
branches:
- main
permissions:
contents: write
id-token: write
jobs:
version:
name: Version & Publish Extension
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
# This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
registry-url: https://registry.npmjs.org
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: latest
- name: Get pnpm store directory
shell: bash
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: Setup pnpm cache
uses: actions/cache@v4
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install root dependencies
run: pnpm install --frozen-lockfile
- name: Install extension dependencies
working-directory: apps/extension
run: pnpm install --frozen-lockfile
- name: Install vsce and ovsx globally
run: pnpm add -g @vscode/vsce ovsx
- name: Make release script executable
run: chmod +x scripts/release.sh
- name: Create Release Pull Request or Publish
uses: changesets/action@v1
with:
publish: ./scripts/release.sh
title: "Release: Extension Version Packages"
commit: "chore: release extension packages"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
VSCE_PAT: ${{ secrets.VSCE_PAT }}
OVSX_PAT: ${{ secrets.OVSX_PAT }}

4
.gitignore vendored
View File

@@ -88,5 +88,9 @@ dev-debug.log
*.sln
*.sw?
# VS Code extension test files
.vscode-test/
apps/extension/.vscode-test/
# apps/extension
apps/extension/vsix-build/

View File

@@ -0,0 +1,153 @@
---
title: Research Session
query: "do we need to add new commands or can we just weaponize the gh cli tool"
date: 2025-08-01
time: 2:27:52 p.m.
timestamp: 2025-08-01T11:27:52.249Z
exchanges: 3
---
# Research Session
## Initial Query
**Question:** do we need to add new commands or can we just weaponize the gh cli tool
**Response:**
For the implementation of the `task-master git` command suite to automate git workflows (Task 97), it is possible to leverage the existing **GitHub CLI (`gh`) tool** rather than creating entirely new git commands from scratch. The GitHub CLI provides a rich set of commands that cover many GitHub-related operations such as branch management, pull request handling, workflow triggering, and release management, which can be scripted and integrated into your CLI tool[1][2][3][4].
### Key Points Supporting Weaponizing GitHub CLI:
- **Comprehensive GitHub Operations**: The `gh` CLI supports creating branches, managing pull requests, releases, tags, and even triggering and monitoring GitHub Actions workflows. This aligns well with the requirements of automated branch management, commit generation, and PR description generation in Task 97[1][3].
- **Scripting and Automation Friendly**: GitHub CLI commands can be scripted and integrated into custom workflows or CLI tools. You can create aliases or wrap `gh` commands inside your own CLI commands to provide a seamless user experience while relying on the battle-tested GitHub CLI for underlying operations[1][3].
- **Authentication and Permissions**: `gh` uses environment variables like `GH_TOKEN` or `GITHUB_TOKEN` for authentication, which can be securely managed in CI/CD pipelines or local environments. This simplifies integration without needing to implement custom authentication logic[3][4].
- **Extensibility**: If some specific git workflow features are not directly supported by `gh`, you can extend it with custom scripts or combine it with native git commands. This hybrid approach reduces the need to reinvent core git operations while allowing customization[1].
### Considerations for Your Project:
- Since Task 97 requires **automated branch management** (branch creation, validation, switching, cleanup), **intelligent commit generation**, and **PR description generation**, you can implement these by invoking `gh` commands programmatically within your `task-master git` commands.
- For example, branch creation and switching can be done with native git commands wrapped in your CLI, while PR creation and management can be handled via `gh pr create`, `gh pr merge`, etc.
- You may still need to implement some **custom logic** for commit type detection, branch name validation, and PR description generation based on your task metadata, but the underlying git and GitHub operations can be delegated to `gh`.
- This approach aligns with your existing CLI foundation (Task 2) and leverages a well-supported tool, reducing development effort and increasing reliability.
### Summary
You **do not necessarily need to add entirely new git commands** for Task 97. Instead, you can **weaponize the GitHub CLI (`gh`) tool** by wrapping and scripting its commands within your `task-master git` command suite. This will allow you to automate git workflows efficiently, maintain consistency, and leverage GitHubs API capabilities without reinventing the wheel[1][2][3][4].
If there are gaps in `gh` functionality for your specific workflow needs, you can supplement with native git commands or custom scripts, but the core git and GitHub operations should be handled by `gh` for best results.
---
## Follow-up 1
**Question:** how does this relate to git worktrees? i hear that they get super complicated when trying to implement them.
**Response:**
Git worktrees allow you to have multiple working directories attached to the same Git repository, each checked out to different branches simultaneously. This enables parallel development workflows without the overhead of cloning the repository multiple times or constantly switching branches in a single working directory[2][3].
However, **implementing or managing git worktrees can get complicated** for several reasons:
- **Complexity for users and tooling:** Managing multiple active worktrees requires careful tracking of which directory corresponds to which branch. This can be confusing and error-prone, especially for novices or when many worktrees are in use simultaneously. Mistakes like committing to the wrong branch or working in the wrong directory are common risks[1].
- **Maintenance overhead:** Worktrees accumulate over time and need to be pruned or cleaned up regularly (`git worktree prune`) to avoid clutter and potential confusion. This adds an extra maintenance step to the workflow[1].
- **Syncing and update challenges:** Each worktree is a separate checkout and can become outdated if not regularly updated from the main repository. This can lead to merge conflicts or redundant work if branches diverge significantly[1].
- **Tooling and integration limitations:** Not all Git tools, scripts, or GUIs fully support worktrees or are optimized for them. This can cause inconsistencies or require additional custom tooling to manage worktrees effectively[1].
- **Performance considerations:** Managing multiple worktrees simultaneously can have performance implications, especially in very large repositories, due to the overhead of maintaining multiple checkouts and metadata[1].
In the context of your project, which involves automating git workflows and possibly wrapping or extending git/GitHub CLI commands, these complexities imply:
- If you consider supporting git worktrees in your `task-master git` command suite, you will need to handle the complexity of managing multiple directories and branches carefully.
- You might need to implement additional logic or tooling to track and manage worktrees, avoid user confusion, and ensure synchronization between them.
- Since your project already plans to leverage the GitHub CLI (`gh`) and native git commands, you could integrate worktree commands (`git worktree add`, `git worktree remove`, `git worktree list`) but should be prepared for the added complexity in workflow management and user documentation.
- Custom wrappers or managers (like the one described in [3]) can help reduce friction by automating worktree creation, switching, and cleanup, which might be a useful approach to consider.
Overall, **git worktrees provide powerful parallel branch management but introduce complexity in implementation and user experience** that must be carefully managed through tooling, automation, and clear documentation[1][3][4].
---
**Summary Table of Git Worktree Challenges and Considerations**
| Aspect | Description | Implication for Project |
|-------------------------|-----------------------------------------------------------------------------------------------|----------------------------------------------------------|
| User Complexity | Confusing multiple directories and branches | Need clear UI/UX and safeguards |
| Maintenance Overhead | Requires pruning and cleanup of stale worktrees | Automate cleanup or provide commands |
| Syncing Issues | Worktrees can become outdated or diverge | Implement update checks and sync workflows |
| Tooling Support | Limited support in some Git tools and GUIs | May require custom tooling or wrappers |
| Performance | Overhead in large repos with many worktrees | Monitor and optimize performance |
| Workflow Automation | Manual worktree management is tedious | Build custom managers or wrappers to simplify usage |
---
Given your projects focus on automating git workflows and leveraging existing CLI tools, **implementing git worktree support is feasible but will require careful design to handle these complexities effectively**. You may want to start with basic worktree commands and incrementally add automation and safeguards as needed.
---
## Follow-up 2
**Question:** since we already have a way to have separated task lists per branch, couldn't we do the same thing for worktrees? each taskmaster list (tag) would have its own worktree based on the name of the tag?
**Response:**
Yes, you can apply a similar approach used for separated task lists per branch to git worktrees by associating each taskmaster list (tag) with its own git worktree named after the tag. This means each tag would correspond to a dedicated worktree directory checked out to a branch or commit related to that tag, enabling isolated parallel work environments for each task list.
### How This Maps to Git Worktrees
- **Git worktrees** allow multiple working directories linked to the same repository, each checked out to a different branch or commit[1][2]. This lets you work on multiple branches simultaneously without switching contexts in a single directory.
- When you create a worktree with `git worktree add <path> [<branch>]`, Git creates a new linked working tree at `<path>`, optionally checking out a specified branch or creating a new one[1][2]. Naming the worktree directory after the tag (taskmaster list) is straightforward and semantically clear.
- This setup mirrors your existing branch-based task list separation but extends it to physical directories, which can improve workflow isolation and reduce context switching.
### Benefits for Your Project
- **Isolated environments per task list/tag:** Each tags worktree can have its own checked-out branch, allowing independent development, testing, or review without interference.
- **Parallel workflows:** Developers can switch between worktrees (directories) instead of switching branches in one directory, reducing errors and improving productivity.
- **Automation potential:** Your `task-master git` commands can automate creating, switching, and cleaning up these worktrees based on tags, similar to how you manage branches and task lists.
### Challenges and Considerations
- **Complexity in management:** Git worktrees add complexity in tracking multiple directories and ensuring users operate in the correct one[1]. Your CLI should provide clear commands and feedback to avoid confusion.
- **Maintenance overhead:** Worktrees need pruning and cleanup (`git worktree prune` or `git worktree remove`) to avoid clutter[1]. Automating cleanup in your tool would be beneficial.
- **Syncing and updates:** Each worktree is a separate checkout and can become outdated. Your automation should include commands to update or rebase worktrees as needed.
- **Tooling support:** Not all Git tools or scripts fully support worktrees, so your CLI wrappers should handle edge cases and provide a consistent experience[1][3].
### Practical Implementation Ideas
- When a new tag (taskmaster list) is created, run `git worktree add` to create a new worktree directory named after the tag.
- Store metadata linking tags to worktree paths in your taskmaster configuration to track and manage them.
- Provide commands like `task-master worktree list`, `task-master worktree add <tag>`, and `task-master worktree remove <tag>` to manage these worktrees.
- Integrate with GitHub CLI (`gh`) and native git commands to automate branch creation, PR handling, and syncing within each worktree.
- Consider inspiration from existing tools like [wt](https://github.com/taecontrol/wt) that automate worktree environment setup and teardown[3].
### Summary
Using git worktrees per taskmaster tag is a feasible and potentially powerful extension of your current branch-based task list separation. It offers isolated, parallel working directories aligned with your tags, improving workflow clarity and reducing branch-switching overhead. However, it requires careful tooling and automation to manage complexity, maintenance, and synchronization effectively. Given your projects focus on automating git workflows and leveraging CLI tools, implementing this feature incrementally with robust user feedback and cleanup commands would be a practical approach.
---
*Generated by Task Master Research Command*
*Timestamp: 2025-08-01T11:27:52.249Z*

View File

@@ -0,0 +1,343 @@
# Product Requirements Document: tm-core Package - Parse PRD Feature
## Project Overview
Create a TypeScript package named `tm-core` at `packages/tm-core` that implements parse-prd functionality using class-based architecture similar to the existing AI providers pattern.
## Design Patterns & Architecture
### Patterns to Apply
1. **Factory Pattern**: Use for `ProviderFactory` to create AI provider instances
2. **Strategy Pattern**: Use for `IAIProvider` implementations and `IStorage` implementations
3. **Facade Pattern**: Use for `TaskMasterCore` as the main API entry point
4. **Template Method Pattern**: Use for `BaseProvider` abstract class
5. **Dependency Injection**: Use throughout for testability (pass dependencies via constructor)
6. **Repository Pattern**: Use for `FileStorage` to abstract data persistence
### Naming Conventions
- **Files**: kebab-case (e.g., `task-parser.ts`, `file-storage.ts`)
- **Classes**: PascalCase (e.g., `TaskParser`, `FileStorage`)
- **Interfaces**: PascalCase with 'I' prefix (e.g., `IStorage`, `IAIProvider`)
- **Methods**: camelCase (e.g., `parsePRD`, `loadTasks`)
- **Constants**: UPPER_SNAKE_CASE (e.g., `DEFAULT_MODEL`)
- **Type aliases**: PascalCase (e.g., `TaskStatus`, `ParseOptions`)
## Exact Folder Structure Required
```
packages/tm-core/
├── src/
│ ├── index.ts
│ ├── types/
│ │ └── index.ts
│ ├── interfaces/
│ │ ├── index.ts # Barrel export
│ │ ├── storage.interface.ts
│ │ ├── ai-provider.interface.ts
│ │ └── configuration.interface.ts
│ ├── tasks/
│ │ ├── index.ts # Barrel export
│ │ └── task-parser.ts
│ ├── ai/
│ │ ├── index.ts # Barrel export
│ │ ├── base-provider.ts
│ │ ├── provider-factory.ts
│ │ ├── prompt-builder.ts
│ │ └── providers/
│ │ ├── index.ts # Barrel export
│ │ ├── anthropic-provider.ts
│ │ ├── openai-provider.ts
│ │ └── google-provider.ts
│ ├── storage/
│ │ ├── index.ts # Barrel export
│ │ └── file-storage.ts
│ ├── config/
│ │ ├── index.ts # Barrel export
│ │ └── config-manager.ts
│ ├── utils/
│ │ ├── index.ts # Barrel export
│ │ └── id-generator.ts
│ └── errors/
│ ├── index.ts # Barrel export
│ └── task-master-error.ts
├── tests/
│ ├── task-parser.test.ts
│ ├── integration/
│ │ └── parse-prd.test.ts
│ └── mocks/
│ └── mock-provider.ts
├── package.json
├── tsconfig.json
├── tsup.config.js
└── jest.config.js
```
## Specific Implementation Requirements
### 1. Create types/index.ts
Define these exact TypeScript interfaces:
- `Task` interface with fields: id, title, description, status, priority, complexity, dependencies, subtasks, metadata, createdAt, updatedAt, source
- `Subtask` interface with fields: id, title, description, completed
- `TaskMetadata` interface with fields: parsedFrom, aiProvider, version, tags (optional)
- Type literals: `TaskStatus` = 'pending' | 'in-progress' | 'completed' | 'blocked'
- Type literals: `TaskPriority` = 'low' | 'medium' | 'high' | 'critical'
- Type literals: `TaskComplexity` = 'simple' | 'moderate' | 'complex'
- `ParseOptions` interface with fields: dryRun (optional), additionalContext (optional), tag (optional), maxTasks (optional)
### 2. Create interfaces/storage.interface.ts
Define `IStorage` interface with these exact methods:
- `loadTasks(tag?: string): Promise<Task[]>`
- `saveTasks(tasks: Task[], tag?: string): Promise<void>`
- `appendTasks(tasks: Task[], tag?: string): Promise<void>`
- `updateTask(id: string, task: Partial<Task>, tag?: string): Promise<void>`
- `deleteTask(id: string, tag?: string): Promise<void>`
- `exists(tag?: string): Promise<boolean>`
### 3. Create interfaces/ai-provider.interface.ts
Define `IAIProvider` interface with these exact methods:
- `generateCompletion(prompt: string, options?: AIOptions): Promise<string>`
- `calculateTokens(text: string): number`
- `getName(): string`
- `getModel(): string`
Define `AIOptions` interface with fields: temperature (optional), maxTokens (optional), systemPrompt (optional)
### 4. Create interfaces/configuration.interface.ts
Define `IConfiguration` interface with fields:
- `projectPath: string`
- `aiProvider: string`
- `apiKey?: string`
- `aiOptions?: AIOptions`
- `mainModel?: string`
- `researchModel?: string`
- `fallbackModel?: string`
- `tasksPath?: string`
- `enableTags?: boolean`
### 5. Create tasks/task-parser.ts
Create class `TaskParser` with:
- Constructor accepting `aiProvider: IAIProvider` and `config: IConfiguration`
- Private property `promptBuilder: PromptBuilder`
- Public method `parsePRD(prdPath: string, options: ParseOptions = {}): Promise<Task[]>`
- Private method `readPRD(prdPath: string): Promise<string>`
- Private method `extractTasks(aiResponse: string): Partial<Task>[]`
- Private method `enrichTasks(rawTasks: Partial<Task>[], prdPath: string): Task[]`
- Apply **Dependency Injection** pattern via constructor
### 6. Create ai/base-provider.ts
Copy existing base-provider.js and convert to TypeScript abstract class:
- Abstract class `BaseProvider` implementing `IAIProvider`
- Protected properties: `apiKey: string`, `model: string`
- Constructor accepting `apiKey: string` and `options: { model?: string }`
- Abstract methods matching IAIProvider interface
- Abstract method `getDefaultModel(): string`
- Apply **Template Method** pattern for common provider logic
### 7. Create ai/provider-factory.ts
Create class `ProviderFactory` with:
- Static method `create(config: { provider: string; apiKey?: string; model?: string }): Promise<IAIProvider>`
- Switch statement for providers: 'anthropic', 'openai', 'google'
- Dynamic imports for each provider
- Throw error for unknown providers
- Apply **Factory** pattern for creating provider instances
Example implementation structure:
```typescript
switch (provider.toLowerCase()) {
case 'anthropic':
const { AnthropicProvider } = await import('./providers/anthropic-provider.js');
return new AnthropicProvider(apiKey, { model });
}
```
### 8. Create ai/providers/anthropic-provider.ts
Create class `AnthropicProvider` extending `BaseProvider`:
- Import Anthropic SDK: `import { Anthropic } from '@anthropic-ai/sdk'`
- Private property `client: Anthropic`
- Implement all abstract methods from BaseProvider
- Default model: 'claude-3-sonnet-20240229'
- Handle API errors and wrap with meaningful messages
### 9. Create ai/providers/openai-provider.ts (placeholder)
Create class `OpenAIProvider` extending `BaseProvider`:
- Import OpenAI SDK when implemented
- For now, throw error: "OpenAI provider not yet implemented"
### 10. Create ai/providers/google-provider.ts (placeholder)
Create class `GoogleProvider` extending `BaseProvider`:
- Import Google Generative AI SDK when implemented
- For now, throw error: "Google provider not yet implemented"
### 11. Create ai/prompt-builder.ts
Create class `PromptBuilder` with:
- Method `buildParsePrompt(prdContent: string, options: ParseOptions = {}): string`
- Method `buildExpandPrompt(task: string, context?: string): string`
- Use template literals for prompt construction
- Include specific JSON format instructions in prompts
### 9. Create storage/file-storage.ts
Create class `FileStorage` implementing `IStorage`:
- Private property `basePath: string` set to `{projectPath}/.taskmaster`
- Constructor accepting `projectPath: string`
- Private method `getTasksPath(tag?: string): string` returning correct path based on tag
- Private method `ensureDirectory(dir: string): Promise<void>`
- Implement all IStorage methods
- Handle ENOENT errors by returning empty arrays
- Use JSON format with structure: `{ tasks: Task[], metadata: { version: string, lastModified: string } }`
- Apply **Repository** pattern for data access abstraction
### 10. Create config/config-manager.ts
Create class `ConfigManager`:
- Private property `config: IConfiguration`
- Constructor accepting `options: Partial<IConfiguration>`
- Use Zod for validation with schema matching IConfiguration
- Method `get<K extends keyof IConfiguration>(key: K): IConfiguration[K]`
- Method `getAll(): IConfiguration`
- Method `validate(): boolean`
- Default values: projectPath = process.cwd(), aiProvider = 'anthropic', enableTags = true
### 11. Create utils/id-generator.ts
Export functions:
- `generateTaskId(index: number = 0): string` returning format `task_{timestamp}_{index}_{random}`
- `generateSubtaskId(parentId: string, index: number = 0): string` returning format `{parentId}_sub_{index}_{random}`
### 16. Create src/index.ts
Create main class `TaskMasterCore`:
- Private properties: `config: ConfigManager`, `storage: IStorage`, `aiProvider?: IAIProvider`, `parser?: TaskParser`
- Constructor accepting `options: Partial<IConfiguration>`
- Method `initialize(): Promise<void>` for lazy loading
- Method `parsePRD(prdPath: string, options: ParseOptions = {}): Promise<Task[]>`
- Method `getTasks(tag?: string): Promise<Task[]>`
- Apply **Facade** pattern to provide simple API over complex subsystems
Export:
- Class `TaskMasterCore`
- Function `createTaskMaster(options: Partial<IConfiguration>): TaskMasterCore`
- All types from './types'
- All interfaces from './interfaces/*'
Import statements should use kebab-case:
```typescript
import { TaskParser } from './tasks/task-parser';
import { FileStorage } from './storage/file-storage';
import { ConfigManager } from './config/config-manager';
import { ProviderFactory } from './ai/provider-factory';
```
### 17. Configure package.json
Create package.json with:
- name: "@task-master/core"
- version: "0.1.0"
- type: "module"
- main: "./dist/index.js"
- module: "./dist/index.mjs"
- types: "./dist/index.d.ts"
- exports map for proper ESM/CJS support
- scripts: build (tsup), dev (tsup --watch), test (jest), typecheck (tsc --noEmit)
- dependencies: zod@^3.23.8
- peerDependencies: @anthropic-ai/sdk, openai, @google/generative-ai
- devDependencies: typescript, tsup, jest, ts-jest, @types/node, @types/jest
### 18. Configure TypeScript
Create tsconfig.json with:
- target: "ES2022"
- module: "ESNext"
- strict: true (with all strict flags enabled)
- declaration: true
- outDir: "./dist"
- rootDir: "./src"
### 19. Configure tsup
Create tsup.config.js with:
- entry: ['src/index.ts']
- format: ['cjs', 'esm']
- dts: true
- sourcemap: true
- clean: true
- external: AI provider SDKs
### 20. Configure Jest
Create jest.config.js with:
- preset: 'ts-jest'
- testEnvironment: 'node'
- Coverage threshold: 80% for all metrics
## Build Process
1. Use tsup to compile TypeScript to both CommonJS and ESM
2. Generate .d.ts files for TypeScript consumers
3. Output to dist/ directory
4. Ensure tree-shaking works properly
## Testing Requirements
- Create unit tests for TaskParser in tests/task-parser.test.ts
- Create MockProvider class in tests/mocks/mock-provider.ts for testing without API calls
- Test error scenarios (file not found, invalid JSON, etc.)
- Create integration test in tests/integration/parse-prd.test.ts
- Follow kebab-case naming for all test files
## Success Criteria
- TypeScript compilation with zero errors
- No use of 'any' type
- All interfaces properly exported
- Compatible with existing tasks.json format
- Feature flag support via USE_TM_CORE environment variable
## Import/Export Conventions
- Use named exports for all classes and interfaces
- Use barrel exports (index.ts) in each directory
- Import types/interfaces with type-only imports: `import type { Task } from '../types'`
- Group imports in order: Node built-ins, external packages, internal packages, relative imports
- Use .js extension in import paths for ESM compatibility
## Error Handling Patterns
- Create custom error classes in `src/errors/` directory
- All public methods should catch and wrap errors with context
- Use error codes for different error types (e.g., 'FILE_NOT_FOUND', 'PARSE_ERROR')
- Never expose internal implementation details in error messages
- Log errors to console.error only in development mode
## Barrel Exports Content
### interfaces/index.ts
```typescript
export type { IStorage } from './storage.interface';
export type { IAIProvider, AIOptions } from './ai-provider.interface';
export type { IConfiguration } from './configuration.interface';
```
### tasks/index.ts
```typescript
export { TaskParser } from './task-parser';
```
### ai/index.ts
```typescript
export { BaseProvider } from './base-provider';
export { ProviderFactory } from './provider-factory';
export { PromptBuilder } from './prompt-builder';
```
### ai/providers/index.ts
```typescript
export { AnthropicProvider } from './anthropic-provider';
export { OpenAIProvider } from './openai-provider';
export { GoogleProvider } from './google-provider';
```
### storage/index.ts
```typescript
export { FileStorage } from './file-storage';
```
### config/index.ts
```typescript
export { ConfigManager } from './config-manager';
```
### utils/index.ts
```typescript
export { generateTaskId, generateSubtaskId } from './id-generator';
```
### errors/index.ts
```typescript
export { TaskMasterError } from './task-master-error';
```

View File

@@ -1,373 +1,21 @@
{
"meta": {
"generatedAt": "2025-05-27T16:34:53.088Z",
"generatedAt": "2025-08-02T14:28:59.851Z",
"tasksAnalyzed": 1,
"totalTasks": 84,
"analysisCount": 45,
"totalTasks": 93,
"analysisCount": 1,
"thresholdScore": 5,
"projectName": "Taskmaster",
"usedResearch": true
"usedResearch": false
},
"complexityAnalysis": [
{
"taskId": 24,
"taskTitle": "Implement AI-Powered Test Generation Command",
"complexityScore": 7,
"recommendedSubtasks": 5,
"expansionPrompt": "Break down the implementation of the AI-powered test generation command into detailed subtasks covering: command structure setup, AI prompt engineering, test file generation logic, integration with Claude API, and comprehensive error handling.",
"reasoning": "This task involves complex integration with an AI service (Claude), requires sophisticated prompt engineering, and needs to generate structured code files. The existing 3 subtasks are a good start but could be expanded to include more detailed steps for AI integration, error handling, and test file formatting."
},
{
"taskId": 26,
"taskTitle": "Implement Context Foundation for AI Operations",
"complexityScore": 6,
"recommendedSubtasks": 4,
"expansionPrompt": "The current 4 subtasks for implementing the context foundation appear comprehensive. Consider if any additional subtasks are needed for testing, documentation, or integration with existing systems.",
"reasoning": "This task involves creating a foundation for context integration with several well-defined components. The existing 4 subtasks cover the main implementation areas (context-file flag, cursor rules integration, context extraction utility, and command handler updates). The complexity is moderate as it requires careful integration with existing systems but has clear requirements."
},
{
"taskId": 27,
"taskTitle": "Implement Context Enhancements for AI Operations",
"complexityScore": 7,
"recommendedSubtasks": 4,
"expansionPrompt": "The current 4 subtasks for implementing context enhancements appear well-structured. Consider if any additional subtasks are needed for testing, documentation, or performance optimization.",
"reasoning": "This task builds upon the foundation from Task #26 and adds more sophisticated context handling features. The 4 existing subtasks cover the main implementation areas (code context extraction, task history context, PRD context integration, and context formatting). The complexity is higher than the foundation task due to the need for intelligent context selection and optimization."
},
{
"taskId": 28,
"taskTitle": "Implement Advanced ContextManager System",
"complexityScore": 8,
"recommendedSubtasks": 5,
"expansionPrompt": "The current 5 subtasks for implementing the advanced ContextManager system appear comprehensive. Consider if any additional subtasks are needed for testing, documentation, or backward compatibility with previous context implementations.",
"reasoning": "This task represents the most complex phase of the context implementation, requiring a sophisticated class design, optimization algorithms, and integration with multiple systems. The 5 existing subtasks cover the core implementation areas, but the complexity is high due to the need for intelligent context prioritization, token management, and performance monitoring."
},
{
"taskId": 40,
"taskTitle": "Implement 'plan' Command for Task Implementation Planning",
"complexityScore": 5,
"recommendedSubtasks": 4,
"expansionPrompt": "The current 4 subtasks for implementing the 'plan' command appear well-structured. Consider if any additional subtasks are needed for testing, documentation, or integration with existing task management workflows.",
"reasoning": "This task involves creating a new command that leverages AI to generate implementation plans. The existing 4 subtasks cover the main implementation areas (retrieving task content, generating plans with AI, formatting in XML, and error handling). The complexity is moderate as it builds on existing patterns for task updates but requires careful AI integration."
},
{
"taskId": 41,
"taskTitle": "Implement Visual Task Dependency Graph in Terminal",
"complexityScore": 8,
"recommendedSubtasks": 10,
"expansionPrompt": "The current 10 subtasks for implementing the visual task dependency graph appear comprehensive. Consider if any additional subtasks are needed for performance optimization with large graphs or additional visualization options.",
"reasoning": "This task involves creating a sophisticated visualization system for terminal display, which is inherently complex due to layout algorithms, ASCII/Unicode rendering, and handling complex dependency relationships. The 10 existing subtasks cover all major aspects of implementation, from CLI interface to accessibility features."
},
{
"taskId": 42,
"taskTitle": "Implement MCP-to-MCP Communication Protocol",
"complexityScore": 9,
"recommendedSubtasks": 8,
"expansionPrompt": "The current 8 subtasks for implementing the MCP-to-MCP communication protocol appear well-structured. Consider if any additional subtasks are needed for security hardening, performance optimization, or comprehensive documentation.",
"reasoning": "This task involves designing and implementing a complex communication protocol between different MCP tools and servers. It requires sophisticated adapter patterns, client-server architecture, and handling of multiple operational modes. The complexity is very high due to the need for standardization, security, and backward compatibility."
},
{
"taskId": 44,
"taskTitle": "Implement Task Automation with Webhooks and Event Triggers",
"complexityScore": 8,
"recommendedSubtasks": 7,
"expansionPrompt": "The current 7 subtasks for implementing task automation with webhooks appear comprehensive. Consider if any additional subtasks are needed for security testing, rate limiting implementation, or webhook monitoring tools.",
"reasoning": "This task involves creating a sophisticated event system with webhooks for integration with external services. The complexity is high due to the need for secure authentication, reliable delivery mechanisms, and handling of various webhook formats and protocols. The existing subtasks cover the main implementation areas but security and monitoring could be emphasized more."
},
{
"taskId": 45,
"taskTitle": "Implement GitHub Issue Import Feature",
"complexityScore": 6,
"recommendedSubtasks": 5,
"expansionPrompt": "The current 5 subtasks for implementing the GitHub issue import feature appear well-structured. Consider if any additional subtasks are needed for handling GitHub API rate limiting, caching, or supporting additional issue metadata.",
"reasoning": "This task involves integrating with the GitHub API to import issues as tasks. The complexity is moderate as it requires API authentication, data mapping, and error handling. The existing 5 subtasks cover the main implementation areas from design to end-to-end implementation."
},
{
"taskId": 46,
"taskTitle": "Implement ICE Analysis Command for Task Prioritization",
"complexityScore": 7,
"recommendedSubtasks": 5,
"expansionPrompt": "The current 5 subtasks for implementing the ICE analysis command appear comprehensive. Consider if any additional subtasks are needed for visualization of ICE scores or integration with other prioritization methods.",
"reasoning": "This task involves creating an AI-powered analysis system for task prioritization using the ICE methodology. The complexity is high due to the need for sophisticated scoring algorithms, AI integration, and report generation. The existing subtasks cover the main implementation areas from algorithm design to integration with existing systems."
},
{
"taskId": 47,
"taskTitle": "Enhance Task Suggestion Actions Card Workflow",
"complexityScore": 6,
"recommendedSubtasks": 6,
"expansionPrompt": "The current 6 subtasks for enhancing the task suggestion actions card workflow appear well-structured. Consider if any additional subtasks are needed for user testing, accessibility improvements, or performance optimization.",
"reasoning": "This task involves redesigning the UI workflow for task expansion and management. The complexity is moderate as it requires careful UX design and state management but builds on existing components. The 6 existing subtasks cover the main implementation areas from design to testing."
},
{
"taskId": 48,
"taskTitle": "Refactor Prompts into Centralized Structure",
"complexityScore": 4,
"recommendedSubtasks": 3,
"expansionPrompt": "The current 3 subtasks for refactoring prompts into a centralized structure appear appropriate. Consider if any additional subtasks are needed for prompt versioning, documentation, or testing.",
"reasoning": "This task involves a straightforward refactoring to improve code organization. The complexity is relatively low as it primarily involves moving code rather than creating new functionality. The 3 existing subtasks cover the main implementation areas from directory structure to integration."
},
{
"taskId": 49,
"taskTitle": "Implement Code Quality Analysis Command",
"complexityScore": 8,
"recommendedSubtasks": 6,
"expansionPrompt": "The current 6 subtasks for implementing the code quality analysis command appear comprehensive. Consider if any additional subtasks are needed for performance optimization with large codebases or integration with existing code quality tools.",
"reasoning": "This task involves creating a sophisticated code analysis system with pattern recognition, best practice verification, and AI-powered recommendations. The complexity is high due to the need for code parsing, complex analysis algorithms, and integration with AI services. The existing subtasks cover the main implementation areas from algorithm design to user interface."
},
{
"taskId": 50,
"taskTitle": "Implement Test Coverage Tracking System by Task",
"complexityScore": 9,
"recommendedSubtasks": 5,
"expansionPrompt": "The current 5 subtasks for implementing the test coverage tracking system appear well-structured. Consider if any additional subtasks are needed for integration with CI/CD systems, performance optimization, or visualization tools.",
"reasoning": "This task involves creating a complex system that maps test coverage to specific tasks and subtasks. The complexity is very high due to the need for sophisticated data structures, integration with coverage tools, and AI-powered test generation. The existing subtasks are comprehensive and cover the main implementation areas from data structure design to AI integration."
},
{
"taskId": 51,
"taskTitle": "Implement Perplexity Research Command",
"complexityScore": 6,
"recommendedSubtasks": 5,
"expansionPrompt": "The current 5 subtasks for implementing the Perplexity research command appear comprehensive. Consider if any additional subtasks are needed for caching optimization, result formatting, or integration with other research tools.",
"reasoning": "This task involves creating a new command that integrates with the Perplexity AI API for research. The complexity is moderate as it requires API integration, context extraction, and result formatting. The 5 existing subtasks cover the main implementation areas from API client to caching system."
},
{
"taskId": 52,
"taskTitle": "Implement Task Suggestion Command for CLI",
"complexityScore": 6,
"recommendedSubtasks": 5,
"expansionPrompt": "The current 5 subtasks for implementing the task suggestion command appear well-structured. Consider if any additional subtasks are needed for suggestion quality evaluation, user feedback collection, or integration with existing task workflows.",
"reasoning": "This task involves creating a new CLI command that generates contextually relevant task suggestions using AI. The complexity is moderate as it requires AI integration, context collection, and interactive CLI interfaces. The existing subtasks cover the main implementation areas from data collection to user interface."
},
{
"taskId": 53,
"taskTitle": "Implement Subtask Suggestion Feature for Parent Tasks",
"complexityScore": 6,
"recommendedSubtasks": 6,
"expansionPrompt": "The current 6 subtasks for implementing the subtask suggestion feature appear comprehensive. Consider if any additional subtasks are needed for suggestion quality metrics, user feedback collection, or performance optimization.",
"reasoning": "This task involves creating a feature that suggests contextually relevant subtasks for parent tasks. The complexity is moderate as it builds on existing task management systems but requires sophisticated AI integration and context analysis. The 6 existing subtasks cover the main implementation areas from validation to testing."
},
{
"taskId": 55,
"taskTitle": "Implement Positional Arguments Support for CLI Commands",
"complexityScore": 5,
"recommendedSubtasks": 5,
"expansionPrompt": "The current 5 subtasks for implementing positional arguments support appear well-structured. Consider if any additional subtasks are needed for backward compatibility testing, documentation updates, or user experience improvements.",
"reasoning": "This task involves modifying the command parsing logic to support positional arguments alongside the existing flag-based syntax. The complexity is moderate as it requires careful handling of different argument styles and edge cases. The 5 existing subtasks cover the main implementation areas from analysis to documentation."
},
{
"taskId": 57,
"taskTitle": "Enhance Task-Master CLI User Experience and Interface",
"complexityScore": 7,
"recommendedSubtasks": 6,
"expansionPrompt": "The current 6 subtasks for enhancing the CLI user experience appear comprehensive. Consider if any additional subtasks are needed for accessibility testing, internationalization, or performance optimization.",
"reasoning": "This task involves a significant overhaul of the CLI interface to improve user experience. The complexity is high due to the breadth of changes (logging, visual elements, interactive components, etc.) and the need for consistent design across all commands. The 6 existing subtasks cover the main implementation areas from log management to help systems."
},
{
"taskId": 60,
"taskTitle": "Implement Mentor System with Round-Table Discussion Feature",
"complexityScore": 8,
"recommendedSubtasks": 7,
"expansionPrompt": "The current 7 subtasks for implementing the mentor system appear well-structured. Consider if any additional subtasks are needed for mentor personality consistency, discussion quality evaluation, or performance optimization with multiple mentors.",
"reasoning": "This task involves creating a sophisticated mentor simulation system with round-table discussions. The complexity is high due to the need for personality simulation, complex LLM integration, and structured discussion management. The 7 existing subtasks cover the main implementation areas from architecture to testing."
},
{
"taskId": 62,
"taskTitle": "Add --simple Flag to Update Commands for Direct Text Input",
"complexityScore": 4,
"recommendedSubtasks": 8,
"expansionPrompt": "The current 8 subtasks for implementing the --simple flag appear comprehensive. Consider if any additional subtasks are needed for user experience testing or documentation updates.",
"reasoning": "This task involves adding a simple flag option to bypass AI processing for updates. The complexity is relatively low as it primarily involves modifying existing command handlers and adding a flag. The 8 existing subtasks are very detailed and cover all aspects of implementation from command parsing to testing."
},
{
"taskId": 63,
"taskTitle": "Add pnpm Support for the Taskmaster Package",
"complexityScore": 5,
"recommendedSubtasks": 8,
"expansionPrompt": "The current 8 subtasks for adding pnpm support appear comprehensive. Consider if any additional subtasks are needed for CI/CD integration, performance comparison, or documentation updates.",
"reasoning": "This task involves ensuring the package works correctly with pnpm as an alternative package manager. The complexity is moderate as it requires careful testing of installation processes and scripts across different environments. The 8 existing subtasks cover all major aspects from documentation to binary verification."
},
{
"taskId": 64,
"taskTitle": "Add Yarn Support for Taskmaster Installation",
"complexityScore": 5,
"recommendedSubtasks": 9,
"expansionPrompt": "The current 9 subtasks for adding Yarn support appear comprehensive. Consider if any additional subtasks are needed for performance testing, CI/CD integration, or compatibility with different Yarn versions.",
"reasoning": "This task involves ensuring the package works correctly with Yarn as an alternative package manager. The complexity is moderate as it requires careful testing of installation processes and scripts across different environments. The 9 existing subtasks are very detailed and cover all aspects from configuration to testing."
},
{
"taskId": 65,
"taskTitle": "Add Bun Support for Taskmaster Installation",
"complexityScore": 6,
"recommendedSubtasks": 6,
"expansionPrompt": "The current 6 subtasks for adding Bun support appear well-structured. Consider if any additional subtasks are needed for handling Bun-specific issues, performance testing, or documentation updates.",
"reasoning": "This task involves adding support for the newer Bun package manager. The complexity is slightly higher than the other package manager tasks due to Bun's differences from Node.js and potential compatibility issues. The 6 existing subtasks cover the main implementation areas from research to documentation."
},
{
"taskId": 67,
"taskTitle": "Add CLI JSON output and Cursor keybindings integration",
"complexityScore": 5,
"recommendedSubtasks": 5,
"expansionPrompt": "The current 5 subtasks for implementing JSON output and Cursor keybindings appear well-structured. Consider if any additional subtasks are needed for testing across different operating systems, documentation updates, or user experience improvements.",
"reasoning": "This task involves two distinct features: adding JSON output to CLI commands and creating a keybindings installation command. The complexity is moderate as it requires careful handling of different output formats and OS-specific file paths. The 5 existing subtasks cover the main implementation areas for both features."
},
{
"taskId": 68,
"taskTitle": "Ability to create tasks without parsing PRD",
"complexityScore": 3,
"recommendedSubtasks": 2,
"expansionPrompt": "The current 2 subtasks for implementing task creation without PRD appear appropriate. Consider if any additional subtasks are needed for validation, error handling, or integration with existing task management workflows.",
"reasoning": "This task involves a relatively simple modification to allow task creation without requiring a PRD document. The complexity is low as it primarily involves creating a form interface and saving functionality. The 2 existing subtasks cover the main implementation areas of UI design and data saving."
},
{
"taskId": 72,
"taskTitle": "Implement PDF Generation for Project Progress and Dependency Overview",
"complexityScore": 7,
"recommendedSubtasks": 6,
"expansionPrompt": "The current 6 subtasks for implementing PDF generation appear comprehensive. Consider if any additional subtasks are needed for handling large projects, additional visualization options, or integration with existing reporting tools.",
"reasoning": "This task involves creating a feature to generate PDF reports of project progress and dependency visualization. The complexity is high due to the need for PDF generation, data collection, and visualization integration. The 6 existing subtasks cover the main implementation areas from library selection to export options."
},
{
"taskId": 75,
"taskTitle": "Integrate Google Search Grounding for Research Role",
"complexityScore": 5,
"recommendedSubtasks": 4,
"expansionPrompt": "The current 4 subtasks for integrating Google Search Grounding appear well-structured. Consider if any additional subtasks are needed for testing with different query types, error handling, or performance optimization.",
"reasoning": "This task involves updating the AI service layer to enable Google Search Grounding for research roles. The complexity is moderate as it requires careful integration with the existing AI service architecture and conditional logic. The 4 existing subtasks cover the main implementation areas from service layer modification to testing."
},
{
"taskId": 76,
"taskTitle": "Develop E2E Test Framework for Taskmaster MCP Server (FastMCP over stdio)",
"complexityScore": 8,
"recommendedSubtasks": 7,
"expansionPrompt": "The current 7 subtasks for developing the E2E test framework appear comprehensive. Consider if any additional subtasks are needed for test result reporting, CI/CD integration, or performance benchmarking.",
"reasoning": "This task involves creating a sophisticated end-to-end testing framework for the MCP server. The complexity is high due to the need for subprocess management, protocol handling, and robust test case definition. The 7 existing subtasks cover the main implementation areas from architecture to documentation."
},
{
"taskId": 77,
"taskTitle": "Implement AI Usage Telemetry for Taskmaster (with external analytics endpoint)",
"complexityScore": 7,
"recommendedSubtasks": 18,
"expansionPrompt": "The current 18 subtasks for implementing AI usage telemetry appear very comprehensive. Consider if any additional subtasks are needed for security hardening, privacy compliance, or user feedback collection.",
"reasoning": "This task involves creating a telemetry system to track AI usage metrics. The complexity is high due to the need for secure data transmission, comprehensive data collection, and integration across multiple commands. The 18 existing subtasks are extremely detailed and cover all aspects of implementation from core utility to provider-specific updates."
},
{
"taskId": 80,
"taskTitle": "Implement Unique User ID Generation and Storage During Installation",
"complexityScore": 4,
"recommendedSubtasks": 5,
"expansionPrompt": "The current 5 subtasks for implementing unique user ID generation appear well-structured. Consider if any additional subtasks are needed for privacy compliance, security auditing, or integration with the telemetry system.",
"reasoning": "This task involves generating and storing a unique user identifier during installation. The complexity is relatively low as it primarily involves UUID generation and configuration file management. The 5 existing subtasks cover the main implementation areas from script structure to documentation."
},
{
"taskId": 81,
"taskTitle": "Task #81: Implement Comprehensive Local Telemetry System with Future Server Integration Capability",
"complexityScore": 8,
"recommendedSubtasks": 6,
"expansionPrompt": "The current 6 subtasks for implementing the comprehensive local telemetry system appear well-structured. Consider if any additional subtasks are needed for data migration, storage optimization, or visualization tools.",
"reasoning": "This task involves expanding the telemetry system to capture additional metrics and implement local storage with future server integration capability. The complexity is high due to the breadth of data collection, storage requirements, and privacy considerations. The 6 existing subtasks cover the main implementation areas from data collection to user-facing benefits."
},
{
"taskId": 82,
"taskTitle": "Update supported-models.json with token limit fields",
"complexityScore": 3,
"recommendedSubtasks": 1,
"expansionPrompt": "This task appears straightforward enough to be implemented without further subtasks. Focus on researching accurate token limit values for each model and ensuring backward compatibility.",
"reasoning": "This task involves a simple update to the supported-models.json file to include new token limit fields. The complexity is low as it primarily involves research and data entry. No subtasks are necessary as the task is well-defined and focused."
},
{
"taskId": 83,
"taskTitle": "Update config-manager.js defaults and getters",
"complexityScore": 4,
"recommendedSubtasks": 1,
"expansionPrompt": "This task appears straightforward enough to be implemented without further subtasks. Focus on updating the DEFAULTS object and related getter functions while maintaining backward compatibility.",
"reasoning": "This task involves updating the config-manager.js module to replace maxTokens with more specific token limit fields. The complexity is relatively low as it primarily involves modifying existing code rather than creating new functionality. No subtasks are necessary as the task is well-defined and focused."
},
{
"taskId": 84,
"taskTitle": "Implement token counting utility",
"complexityScore": 5,
"recommendedSubtasks": 1,
"expansionPrompt": "This task appears well-defined enough to be implemented without further subtasks. Focus on implementing accurate token counting for different models and proper fallback mechanisms.",
"reasoning": "This task involves creating a utility function to count tokens for different AI models. The complexity is moderate as it requires integration with the tiktoken library and handling different tokenization schemes. No subtasks are necessary as the task is well-defined and focused."
},
{
"taskId": 69,
"taskTitle": "Enhance Analyze Complexity for Specific Task IDs",
"complexityScore": 7,
"recommendedSubtasks": 6,
"expansionPrompt": "Break down the task 'Enhance Analyze Complexity for Specific Task IDs' into 6 subtasks focusing on: 1) Core logic modification to accept ID parameters, 2) Report merging functionality, 3) CLI interface updates, 4) MCP tool integration, 5) Documentation updates, and 6) Comprehensive testing across all components.",
"reasoning": "This task involves modifying existing functionality across multiple components (core logic, CLI, MCP) with complex logic for filtering tasks and merging reports. The implementation requires careful handling of different parameter combinations and edge cases. The task has interdependent components that need to work together seamlessly, and the report merging functionality adds significant complexity."
},
{
"taskId": 70,
"taskTitle": "Implement 'diagram' command for Mermaid diagram generation",
"complexityScore": 6,
"recommendedSubtasks": 5,
"expansionPrompt": "Break down the 'diagram' command implementation into 5 subtasks: 1) Command interface and parameter handling, 2) Task data extraction and transformation to Mermaid syntax, 3) Diagram rendering with status color coding, 4) Output formatting and file export functionality, and 5) Error handling and edge case management.",
"reasoning": "This task requires implementing a new feature rather than modifying existing code, which reduces complexity from integration challenges. However, it involves working with visualization logic, dependency mapping, and multiple output formats. The color coding based on status and handling of dependency relationships adds moderate complexity. The task is well-defined but requires careful attention to diagram formatting and error handling."
},
{
"taskId": 85,
"taskTitle": "Update ai-services-unified.js for dynamic token limits",
"complexityScore": 7,
"recommendedSubtasks": 5,
"expansionPrompt": "Break down the update of ai-services-unified.js for dynamic token limits into subtasks such as: (1) Import and integrate the token counting utility, (2) Refactor _unifiedServiceRunner to calculate and enforce dynamic token limits, (3) Update error handling for token limit violations, (4) Add and verify logging for token usage, (5) Write and execute tests for various prompt and model scenarios.",
"reasoning": "This task involves significant code changes to a core function, integration of a new utility, dynamic logic for multiple models, and robust error handling. It also requires comprehensive testing for edge cases and integration, making it moderately complex and best managed by splitting into focused subtasks."
},
{
"taskId": 87,
"taskTitle": "Implement validation and error handling",
"complexityScore": 5,
"recommendedSubtasks": 4,
"expansionPrompt": "Decompose this task into: (1) Add validation logic for model and config loading, (2) Implement error handling and fallback mechanisms, (3) Enhance logging and reporting for token usage, (4) Develop helper functions for configuration suggestions and improvements.",
"reasoning": "This task is primarily about adding validation, error handling, and logging. While important for robustness, the logic is straightforward and can be modularized into a few clear subtasks."
},
{
"taskId": 89,
"taskTitle": "Introduce Prioritize Command with Enhanced Priority Levels",
"complexityScore": 6,
"recommendedSubtasks": 5,
"expansionPrompt": "Expand this task into: (1) Implement the prioritize command with all required flags and shorthands, (2) Update CLI output and help documentation for new priority levels, (3) Ensure backward compatibility with existing commands, (4) Add error handling for invalid inputs, (5) Write and run tests for all command scenarios.",
"reasoning": "This CLI feature requires command parsing, updating internal logic for new priority levels, documentation, and robust error handling. The complexity is moderate due to the need for backward compatibility and comprehensive testing."
},
{
"taskId": 90,
"taskTitle": "Implement Subtask Progress Analyzer and Reporting System",
"complexityScore": 8,
"recommendedSubtasks": 6,
"expansionPrompt": "Break down the analyzer implementation into: (1) Design and implement progress tracking logic, (2) Develop status validation and issue detection, (3) Build the reporting system with multiple output formats, (4) Integrate analyzer with the existing task management system, (5) Optimize for performance and scalability, (6) Write unit, integration, and performance tests.",
"reasoning": "This is a complex, multi-faceted feature involving data analysis, reporting, integration, and performance optimization. It touches many parts of the system and requires careful design, making it one of the most complex tasks in the list."
},
{
"taskId": 91,
"taskTitle": "Implement Move Command for Tasks and Subtasks",
"complexityScore": 7,
"recommendedSubtasks": 5,
"expansionPrompt": "Expand this task into: (1) Implement move logic for tasks and subtasks, (2) Handle edge cases (invalid ids, non-existent parents, circular dependencies), (3) Update CLI to support move command with flags, (4) Ensure data integrity and update relationships, (5) Write and execute tests for various move scenarios.",
"reasoning": "Moving tasks and subtasks requires careful handling of hierarchical data, edge cases, and data integrity. The command must be robust and user-friendly, necessitating multiple focused subtasks for safe implementation."
},
{
"taskId": 92,
"taskTitle": "Add Global Joke Flag to All CLI Commands",
"complexityScore": 8,
"recommendedSubtasks": 7,
"expansionPrompt": "Break down the implementation of the global --joke flag into the following subtasks: (1) Update CLI foundation to support global flags, (2) Develop the joke-service module with joke management and category support, (3) Integrate joke output into existing output utilities, (4) Update all CLI commands for joke flag compatibility, (5) Add configuration options for joke categories and custom jokes, (6) Implement comprehensive testing (flag recognition, output, content, integration, performance, regression), (7) Update documentation and usage examples.",
"reasoning": "This task requires changes across the CLI foundation, output utilities, all command modules, and configuration management. It introduces a new service module, global flag handling, and output logic that must not interfere with existing features (including JSON output). The need for robust testing and backward compatibility further increases complexity. The scope spans multiple code areas and requires careful integration, justifying a high complexity score and a detailed subtask breakdown to manage risk and ensure maintainability.[2][3][5]"
},
{
"taskId": 94,
"taskTitle": "Implement Standalone 'research' CLI Command for AI-Powered Queries",
"complexityScore": 7,
"recommendedSubtasks": 6,
"expansionPrompt": "Break down the implementation of the 'research' CLI command into logical subtasks covering command registration, parameter handling, context gathering, AI service integration, output formatting, and documentation.",
"reasoning": "This task has moderate to high complexity (7/10) due to multiple interconnected components: CLI argument parsing, integration with AI services, context gathering from various sources, and output formatting with different modes. The cyclomatic complexity would be significant with multiple decision paths for handling different flags and options. The task requires understanding existing patterns and extending the codebase in a consistent manner, suggesting the need for careful decomposition into manageable subtasks."
},
{
"taskId": 86,
"taskTitle": "Implement GitHub Issue Export Feature",
"complexityScore": 9,
"recommendedSubtasks": 10,
"expansionPrompt": "Break down the implementation of the GitHub Issue Export Feature into detailed subtasks covering: command structure and CLI integration, GitHub API client development, authentication and error handling, task-to-issue mapping logic, content formatting and markdown conversion, bidirectional linking and metadata management, extensible architecture and adapter interfaces, configuration and settings management, documentation, and comprehensive testing (unit, integration, edge cases, performance).",
"reasoning": "This task involves designing and implementing a robust, extensible export system with deep integration into GitHub, including bidirectional workflows, complex data mapping, error handling, and support for future platforms. The requirements span CLI design, API integration, content transformation, metadata management, extensibility, configuration, and extensive testing. The breadth and depth of these requirements, along with the need for maintainability and future extensibility, place this task at a high complexity level. Breaking it into at least 10 subtasks will ensure each major component and concern is addressed systematically, reducing risk and improving quality."
"expansionPrompt": "Expand task 24 'Implement AI-Powered Test Generation Command' into 6 subtasks, focusing on: 1) Command structure implementation, 2) AI prompt engineering for test generation, 3) Test file generation and output, 4) Framework-specific template implementation, 5) MCP tool integration, and 6) Documentation and help system integration. Include detailed implementation steps, dependencies, and testing approaches for each subtask.",
"reasoning": "This task has high complexity due to several challenging aspects: 1) AI integration requiring sophisticated prompt engineering, 2) Test generation across multiple frameworks, 3) File system operations with proper error handling, 4) MCP tool integration, 5) Complex configuration requirements, and 6) Framework-specific template generation. The task already has 5 subtasks but could benefit from reorganization based on the updated implementation details in the info blocks, particularly around framework support and configuration."
}
]
}

View File

@@ -1,6 +1,6 @@
{
"currentTag": "master",
"lastSwitched": "2025-07-22T13:32:03.558Z",
"lastSwitched": "2025-08-01T14:09:25.838Z",
"branchTagMapping": {
"v017-adds": "v017-adds",
"next": "next"

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,333 @@
# task-master-ai
## 0.24.0
### Minor Changes
- [#1098](https://github.com/eyaltoledano/claude-task-master/pull/1098) [`36468f3`](https://github.com/eyaltoledano/claude-task-master/commit/36468f3c93faf4035a5c442ccbc501077f3440f1) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Enhanced Claude Code provider with codebase-aware task generation
- Added automatic codebase analysis for Claude Code provider in `parse-prd`, `expand-task`, and `analyze-complexity` commands
- When using Claude Code as the AI provider, Task Master now instructs the AI to analyze the project structure, existing implementations, and patterns before generating tasks or subtasks
- Tasks and subtasks generated by Claude Code are now informed by actual codebase analysis, resulting in more accurate and contextual outputs
- [#1105](https://github.com/eyaltoledano/claude-task-master/pull/1105) [`75c514c`](https://github.com/eyaltoledano/claude-task-master/commit/75c514cf5b2ca47f95c0ad7fa92654a4f2a6be4b) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Add GPT-5 support with proper parameter handling
- Added GPT-5 model to supported models configuration with SWE score of 0.749
- [#1091](https://github.com/eyaltoledano/claude-task-master/pull/1091) [`4bb6370`](https://github.com/eyaltoledano/claude-task-master/commit/4bb63706b80c28d1b2d782ba868a725326f916c7) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Add Claude Code subagent support with task-orchestrator, task-executor, and task-checker
## New Claude Code Agents
Added specialized agents for Claude Code users to enable parallel task execution, intelligent task orchestration, and quality assurance:
### task-orchestrator
Coordinates and manages the execution of Task Master tasks with intelligent dependency analysis:
- Analyzes task dependencies to identify parallelizable work
- Deploys multiple task-executor agents for concurrent execution
- Monitors task completion and updates the dependency graph
- Automatically identifies and starts newly unblocked tasks
### task-executor
Handles the actual implementation of individual tasks:
- Executes specific tasks identified by the orchestrator
- Works on concrete implementation rather than planning
- Updates task status and logs progress
- Can work in parallel with other executors on independent tasks
### task-checker
Verifies that completed tasks meet their specifications:
- Reviews tasks marked as 'review' status
- Validates implementation against requirements
- Runs tests and checks for best practices
- Ensures quality before marking tasks as 'done'
## Installation
When using the Claude profile (`task-master rules add claude`), the agents are automatically installed to `.claude/agents/` directory.
## Usage Example
```bash
# In Claude Code, after initializing a project with tasks:
# Use task-orchestrator to analyze and coordinate work
# The orchestrator will:
# 1. Check task dependencies
# 2. Identify tasks that can run in parallel
# 3. Deploy executors for available work
# 4. Monitor progress and deploy new executors as tasks complete
# Use task-executor for specific task implementation
# When the orchestrator identifies task 2.3 needs work:
# The executor will implement that specific task
```
## Benefits
- **Parallel Execution**: Multiple independent tasks can be worked on simultaneously
- **Intelligent Scheduling**: Orchestrator understands dependencies and optimizes execution order
- **Separation of Concerns**: Planning (orchestrator) is separated from execution (executor)
- **Progress Tracking**: Real-time updates as tasks are completed
- **Automatic Progression**: As tasks complete, newly unblocked tasks are automatically started
### Patch Changes
- [#1094](https://github.com/eyaltoledano/claude-task-master/pull/1094) [`4357af3`](https://github.com/eyaltoledano/claude-task-master/commit/4357af3f13859d90bca8795215e5d5f1d94abde5) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Fix expand task generating unrelated generic subtasks
Fixed an issue where `task-master expand` would generate generic authentication-related subtasks regardless of the parent task context when using complexity reports. The expansion now properly includes the parent task details alongside any expansion guidance.
- [#1079](https://github.com/eyaltoledano/claude-task-master/pull/1079) [`e495b2b`](https://github.com/eyaltoledano/claude-task-master/commit/e495b2b55950ee54c7d0f1817d8530e28bd79c05) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Fix scope-up/down prompts to include all required fields for better AI model compatibility
- Added missing `priority` field to scope adjustment prompts to prevent validation errors with Claude-code and other models
- Ensures generated JSON includes all fields required by the schema
- [#1079](https://github.com/eyaltoledano/claude-task-master/pull/1079) [`e495b2b`](https://github.com/eyaltoledano/claude-task-master/commit/e495b2b55950ee54c7d0f1817d8530e28bd79c05) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Fix MCP scope-up/down tools not finding tasks
- Fixed task ID parsing in MCP layer - now correctly converts string IDs to numbers
- scope_up_task and scope_down_task MCP tools now work properly
- [#1079](https://github.com/eyaltoledano/claude-task-master/pull/1079) [`e495b2b`](https://github.com/eyaltoledano/claude-task-master/commit/e495b2b55950ee54c7d0f1817d8530e28bd79c05) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Improve AI provider compatibility for JSON generation
- Fixed schema compatibility issues between Perplexity and OpenAI o3 models
- Removed nullable/default modifiers from Zod schemas for broader compatibility
- Added automatic JSON repair for malformed AI responses (handles cases like missing array values)
- Perplexity now uses JSON mode for more reliable structured output
- Post-processing handles default values separately from schema validation
## 0.24.0-rc.2
### Minor Changes
- [#1105](https://github.com/eyaltoledano/claude-task-master/pull/1105) [`75c514c`](https://github.com/eyaltoledano/claude-task-master/commit/75c514cf5b2ca47f95c0ad7fa92654a4f2a6be4b) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Add GPT-5 support with proper parameter handling
- Added GPT-5 model to supported models configuration with SWE score of 0.749
## 0.24.0-rc.1
### Minor Changes
- [#1093](https://github.com/eyaltoledano/claude-task-master/pull/1093) [`36468f3`](https://github.com/eyaltoledano/claude-task-master/commit/36468f3c93faf4035a5c442ccbc501077f3440f1) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Enhanced Claude Code provider with codebase-aware task generation
- Added automatic codebase analysis for Claude Code provider in `parse-prd`, `expand-task`, and `analyze-complexity` commands
- When using Claude Code as the AI provider, Task Master now instructs the AI to analyze the project structure, existing implementations, and patterns before generating tasks or subtasks
- Tasks and subtasks generated by Claude Code are now informed by actual codebase analysis, resulting in more accurate and contextual outputs
- [#1091](https://github.com/eyaltoledano/claude-task-master/pull/1091) [`4bb6370`](https://github.com/eyaltoledano/claude-task-master/commit/4bb63706b80c28d1b2d782ba868a725326f916c7) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Add Claude Code subagent support with task-orchestrator, task-executor, and task-checker
## New Claude Code Agents
Added specialized agents for Claude Code users to enable parallel task execution, intelligent task orchestration, and quality assurance:
### task-orchestrator
Coordinates and manages the execution of Task Master tasks with intelligent dependency analysis:
- Analyzes task dependencies to identify parallelizable work
- Deploys multiple task-executor agents for concurrent execution
- Monitors task completion and updates the dependency graph
- Automatically identifies and starts newly unblocked tasks
### task-executor
Handles the actual implementation of individual tasks:
- Executes specific tasks identified by the orchestrator
- Works on concrete implementation rather than planning
- Updates task status and logs progress
- Can work in parallel with other executors on independent tasks
### task-checker
Verifies that completed tasks meet their specifications:
- Reviews tasks marked as 'review' status
- Validates implementation against requirements
- Runs tests and checks for best practices
- Ensures quality before marking tasks as 'done'
## Installation
When using the Claude profile (`task-master rules add claude`), the agents are automatically installed to `.claude/agents/` directory.
## Usage Example
```bash
# In Claude Code, after initializing a project with tasks:
# Use task-orchestrator to analyze and coordinate work
# The orchestrator will:
# 1. Check task dependencies
# 2. Identify tasks that can run in parallel
# 3. Deploy executors for available work
# 4. Monitor progress and deploy new executors as tasks complete
# Use task-executor for specific task implementation
# When the orchestrator identifies task 2.3 needs work:
# The executor will implement that specific task
```
## Benefits
- **Parallel Execution**: Multiple independent tasks can be worked on simultaneously
- **Intelligent Scheduling**: Orchestrator understands dependencies and optimizes execution order
- **Separation of Concerns**: Planning (orchestrator) is separated from execution (executor)
- **Progress Tracking**: Real-time updates as tasks are completed
- **Automatic Progression**: As tasks complete, newly unblocked tasks are automatically started
### Patch Changes
- [#1094](https://github.com/eyaltoledano/claude-task-master/pull/1094) [`4357af3`](https://github.com/eyaltoledano/claude-task-master/commit/4357af3f13859d90bca8795215e5d5f1d94abde5) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Fix expand task generating unrelated generic subtasks
Fixed an issue where `task-master expand` would generate generic authentication-related subtasks regardless of the parent task context when using complexity reports. The expansion now properly includes the parent task details alongside any expansion guidance.
## 0.23.1-rc.0
### Patch Changes
- [#1079](https://github.com/eyaltoledano/claude-task-master/pull/1079) [`e495b2b`](https://github.com/eyaltoledano/claude-task-master/commit/e495b2b55950ee54c7d0f1817d8530e28bd79c05) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Fix scope-up/down prompts to include all required fields for better AI model compatibility
- Added missing `priority` field to scope adjustment prompts to prevent validation errors with Claude-code and other models
- Ensures generated JSON includes all fields required by the schema
- [#1079](https://github.com/eyaltoledano/claude-task-master/pull/1079) [`e495b2b`](https://github.com/eyaltoledano/claude-task-master/commit/e495b2b55950ee54c7d0f1817d8530e28bd79c05) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Fix MCP scope-up/down tools not finding tasks
- Fixed task ID parsing in MCP layer - now correctly converts string IDs to numbers
- scope_up_task and scope_down_task MCP tools now work properly
- [#1079](https://github.com/eyaltoledano/claude-task-master/pull/1079) [`e495b2b`](https://github.com/eyaltoledano/claude-task-master/commit/e495b2b55950ee54c7d0f1817d8530e28bd79c05) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Improve AI provider compatibility for JSON generation
- Fixed schema compatibility issues between Perplexity and OpenAI o3 models
- Removed nullable/default modifiers from Zod schemas for broader compatibility
- Added automatic JSON repair for malformed AI responses (handles cases like missing array values)
- Perplexity now uses JSON mode for more reliable structured output
- Post-processing handles default values separately from schema validation
## 0.23.0
### Minor Changes
- [#1064](https://github.com/eyaltoledano/claude-task-master/pull/1064) [`53903f1`](https://github.com/eyaltoledano/claude-task-master/commit/53903f1e8eee23ac512eb13a6d81d8cbcfe658cb) Thanks [@eyaltoledano](https://github.com/eyaltoledano)! - Add new `scope-up` and `scope-down` commands for dynamic task complexity adjustment
This release introduces two powerful new commands that allow you to dynamically adjust the complexity of your tasks and subtasks without recreating them from scratch.
**New CLI Commands:**
- `task-master scope-up` - Increase task complexity (add more detail, requirements, or implementation steps)
- `task-master scope-down` - Decrease task complexity (simplify, remove unnecessary details, or streamline)
**Key Features:**
- **Multiple tasks**: Support comma-separated IDs to adjust multiple tasks at once (`--id=5,7,12`)
- **Strength levels**: Choose adjustment intensity with `--strength=light|regular|heavy` (defaults to regular)
- **Custom prompts**: Use `--prompt` flag to specify exactly how you want tasks adjusted
- **MCP integration**: Available as `scope_up_task` and `scope_down_task` tools in Cursor and other MCP environments
- **Smart context**: AI considers your project context and task dependencies when making adjustments
**Usage Examples:**
```bash
# Make a task more detailed
task-master scope-up --id=5
# Simplify multiple tasks with light touch
task-master scope-down --id=10,11,12 --strength=light
# Custom adjustment with specific instructions
task-master scope-up --id=7 --prompt="Add more error handling and edge cases"
```
**Why use this?**
- **Iterative refinement**: Adjust task complexity as your understanding evolves
- **Project phase adaptation**: Scale tasks up for implementation, down for planning
- **Team coordination**: Adjust complexity based on team member experience levels
- **Milestone alignment**: Fine-tune tasks to match project phase requirements
Perfect for agile workflows where task requirements change as you learn more about the problem space.
### Patch Changes
- [#1063](https://github.com/eyaltoledano/claude-task-master/pull/1063) [`2ae6e7e`](https://github.com/eyaltoledano/claude-task-master/commit/2ae6e7e6be3605c3c4d353f34666e54750dba973) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Fix for tasks not found when using string IDs
- [#1049](https://github.com/eyaltoledano/claude-task-master/pull/1049) [`45a14c3`](https://github.com/eyaltoledano/claude-task-master/commit/45a14c323d21071c15106335e89ad1f4a20976ab) Thanks [@ben-vargas](https://github.com/ben-vargas)! - Fix tag-specific complexity report detection in expand command
The expand command now correctly finds and uses tag-specific complexity reports (e.g., `task-complexity-report_feature-xyz.json`) when operating in a tag context. Previously, it would always look for the generic `task-complexity-report.json` file due to a default value in the CLI option definition.
## 0.23.0-rc.2
### Minor Changes
- [#1064](https://github.com/eyaltoledano/claude-task-master/pull/1064) [`53903f1`](https://github.com/eyaltoledano/claude-task-master/commit/53903f1e8eee23ac512eb13a6d81d8cbcfe658cb) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Add new `scope-up` and `scope-down` commands for dynamic task complexity adjustment
This release introduces two powerful new commands that allow you to dynamically adjust the complexity of your tasks and subtasks without recreating them from scratch.
**New CLI Commands:**
- `task-master scope-up` - Increase task complexity (add more detail, requirements, or implementation steps)
- `task-master scope-down` - Decrease task complexity (simplify, remove unnecessary details, or streamline)
**Key Features:**
- **Multiple tasks**: Support comma-separated IDs to adjust multiple tasks at once (`--id=5,7,12`)
- **Strength levels**: Choose adjustment intensity with `--strength=light|regular|heavy` (defaults to regular)
- **Custom prompts**: Use `--prompt` flag to specify exactly how you want tasks adjusted
- **MCP integration**: Available as `scope_up_task` and `scope_down_task` tools in Cursor and other MCP environments
- **Smart context**: AI considers your project context and task dependencies when making adjustments
**Usage Examples:**
```bash
# Make a task more detailed
task-master scope-up --id=5
# Simplify multiple tasks with light touch
task-master scope-down --id=10,11,12 --strength=light
# Custom adjustment with specific instructions
task-master scope-up --id=7 --prompt="Add more error handling and edge cases"
```
**Why use this?**
- **Iterative refinement**: Adjust task complexity as your understanding evolves
- **Project phase adaptation**: Scale tasks up for implementation, down for planning
- **Team coordination**: Adjust complexity based on team member experience levels
- **Milestone alignment**: Fine-tune tasks to match project phase requirements
Perfect for agile workflows where task requirements change as you learn more about the problem space.
## 0.22.1-rc.1
### Patch Changes
- [#1069](https://github.com/eyaltoledano/claude-task-master/pull/1069) [`72ca68e`](https://github.com/eyaltoledano/claude-task-master/commit/72ca68edeb870ff7a3b0d2d632e09dae921dc16a) Thanks [@eyaltoledano](https://github.com/eyaltoledano)! - Add new `scope-up` and `scope-down` commands for dynamic task complexity adjustment
This release introduces two powerful new commands that allow you to dynamically adjust the complexity of your tasks and subtasks without recreating them from scratch.
**New CLI Commands:**
- `task-master scope-up` - Increase task complexity (add more detail, requirements, or implementation steps)
- `task-master scope-down` - Decrease task complexity (simplify, remove unnecessary details, or streamline)
**Key Features:**
- **Multiple tasks**: Support comma-separated IDs to adjust multiple tasks at once (`--id=5,7,12`)
- **Strength levels**: Choose adjustment intensity with `--strength=light|regular|heavy` (defaults to regular)
- **Custom prompts**: Use `--prompt` flag to specify exactly how you want tasks adjusted
- **MCP integration**: Available as `scope_up_task` and `scope_down_task` tools in Cursor and other MCP environments
- **Smart context**: AI considers your project context and task dependencies when making adjustments
**Usage Examples:**
```bash
# Make a task more detailed
task-master scope-up --id=5
# Simplify multiple tasks with light touch
task-master scope-down --id=10,11,12 --strength=light
# Custom adjustment with specific instructions
task-master scope-up --id=7 --prompt="Add more error handling and edge cases"
```
**Why use this?**
- **Iterative refinement**: Adjust task complexity as your understanding evolves
- **Project phase adaptation**: Scale tasks up for implementation, down for planning
- **Team coordination**: Adjust complexity based on team member experience levels
- **Milestone alignment**: Fine-tune tasks to match project phase requirements
Perfect for agile workflows where task requirements change as you learn more about the problem space.
## 0.22.1-rc.0
### Patch Changes
- [#1063](https://github.com/eyaltoledano/claude-task-master/pull/1063) [`2ae6e7e`](https://github.com/eyaltoledano/claude-task-master/commit/2ae6e7e6be3605c3c4d353f34666e54750dba973) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Fix for tasks not found when using string IDs
- [#1049](https://github.com/eyaltoledano/claude-task-master/pull/1049) [`45a14c3`](https://github.com/eyaltoledano/claude-task-master/commit/45a14c323d21071c15106335e89ad1f4a20976ab) Thanks [@ben-vargas](https://github.com/ben-vargas)! - Fix tag-specific complexity report detection in expand command
The expand command now correctly finds and uses tag-specific complexity reports (e.g., `task-complexity-report_feature-xyz.json`) when operating in a tag context. Previously, it would always look for the generic `task-complexity-report.json` file due to a default value in the CLI option definition.
## 0.22.0
### Minor Changes

View File

@@ -3,3 +3,7 @@
## Task Master AI Instructions
**Import Task Master's development workflow commands and guidelines, treat as if import is in the main CLAUDE.md file.**
@./.taskmaster/CLAUDE.md
## Changeset Guidelines
- When creating changesets, remember that it's user-facing, meaning we don't have to get into the specifics of the code, but rather mention what the end-user is getting or fixing from this changeset.

View File

@@ -1,26 +1,155 @@
# Change Log
## 1.0.0
### Minor Changes
- Add automated CI/CD pipeline for VS Code extension
- Add automated version management and publishing via changesets
- Create tag-extension script to manage extension-specific git tags
- Add automated publishing to VS Code Marketplace and Open VSX Registry
- Set up release script with proper build process integration
- Add version.yml workflow for automated releases on main branch pushes
## 0.23.1
### Patch Changes
- Test extension version automation
- Verify that extension versions are properly managed by changesets
- Ensure proper integration with the 3-file packaging system
- [#1090](https://github.com/eyaltoledano/claude-task-master/pull/1090) [`a464e55`](https://github.com/eyaltoledano/claude-task-master/commit/a464e550b886ef81b09df80588fe5881bce83d93) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Fix issues with some users not being able to connect to Taskmaster MCP server while using the extension
All notable changes to the vscode extension will be documented in this file.
- Updated dependencies [[`4357af3`](https://github.com/eyaltoledano/claude-task-master/commit/4357af3f13859d90bca8795215e5d5f1d94abde5), [`e495b2b`](https://github.com/eyaltoledano/claude-task-master/commit/e495b2b55950ee54c7d0f1817d8530e28bd79c05), [`36468f3`](https://github.com/eyaltoledano/claude-task-master/commit/36468f3c93faf4035a5c442ccbc501077f3440f1), [`e495b2b`](https://github.com/eyaltoledano/claude-task-master/commit/e495b2b55950ee54c7d0f1817d8530e28bd79c05), [`e495b2b`](https://github.com/eyaltoledano/claude-task-master/commit/e495b2b55950ee54c7d0f1817d8530e28bd79c05), [`75c514c`](https://github.com/eyaltoledano/claude-task-master/commit/75c514cf5b2ca47f95c0ad7fa92654a4f2a6be4b), [`4bb6370`](https://github.com/eyaltoledano/claude-task-master/commit/4bb63706b80c28d1b2d782ba868a725326f916c7)]:
- task-master-ai@0.24.0
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
## 0.23.1-rc.1
## [Unreleased]
### Patch Changes
- Initial release
- Updated dependencies [[`75c514c`](https://github.com/eyaltoledano/claude-task-master/commit/75c514cf5b2ca47f95c0ad7fa92654a4f2a6be4b)]:
- task-master-ai@0.24.0-rc.2
## 0.23.1-rc.0
### Patch Changes
- [#1090](https://github.com/eyaltoledano/claude-task-master/pull/1090) [`a464e55`](https://github.com/eyaltoledano/claude-task-master/commit/a464e550b886ef81b09df80588fe5881bce83d93) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Fix issues with some users not being able to connect to Taskmaster MCP server while using the extension
- Updated dependencies [[`4357af3`](https://github.com/eyaltoledano/claude-task-master/commit/4357af3f13859d90bca8795215e5d5f1d94abde5), [`36468f3`](https://github.com/eyaltoledano/claude-task-master/commit/36468f3c93faf4035a5c442ccbc501077f3440f1), [`4bb6370`](https://github.com/eyaltoledano/claude-task-master/commit/4bb63706b80c28d1b2d782ba868a725326f916c7)]:
- task-master-ai@0.24.0-rc.1
## 0.23.0
### Minor Changes
- [#1064](https://github.com/eyaltoledano/claude-task-master/pull/1064) [`b82d858`](https://github.com/eyaltoledano/claude-task-master/commit/b82d858f81a1e702ad59d84d5ae8a2ca84359a83) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - 🎉 **Introducing TaskMaster Extension!**
We're thrilled to launch the first version of our Code extension, bringing the power of TaskMaster directly into your favorite code editor. While this is our initial release and we've kept things focused, it already packs powerful features to supercharge your development workflow.
## ✨ Key Features
### 📋 Visual Task Management
- **Kanban Board View**: Visualize all your tasks in an intuitive board layout directly in VS Code
- **Drag & Drop**: Easily change task status by dragging cards between columns
- **Real-time Updates**: See changes instantly as you work through your project
### 🏷️ Multi-Context Support
- **Tag Switching**: Seamlessly switch between different project contexts/tags
- **Isolated Workflows**: Keep different features or experiments organized separately
### 🤖 AI-Powered Task Updates
- **Smart Updates**: Use TaskMaster's AI capabilities to update tasks and subtasks
- **Context-Aware**: Leverages your existing TaskMaster configuration and models
### 📊 Rich Task Information
- **Complexity Scores**: See task complexity ratings at a glance
- **Subtask Visualization**: Expand tasks to view and manage subtasks
- **Dependency Graphs**: Understand task relationships and dependencies visually
### ⚙️ Configuration Management
- **Visual Config Editor**: View and understand your `.taskmaster/config.json` settings
- **Easy Access**: No more manual JSON editing for common configuration tasks
### 🚀 Quick Actions
- **Status Updates**: Change task status with a single click
- **Task Details**: Access full task information without leaving VS Code
- **Integrated Commands**: All TaskMaster commands available through the command palette
## 🎯 What's Next?
This is just the beginning! We wanted to get a solid foundation into your hands quickly. The extension will evolve rapidly with your feedback, adding more advanced features, better visualizations, and deeper integration with your development workflow.
Thank you for being part of the TaskMaster journey. Your workflow has never looked better! 🚀
## 0.23.0-rc.1
### Minor Changes
- [#1064](https://github.com/eyaltoledano/claude-task-master/pull/1064) [`b82d858`](https://github.com/eyaltoledano/claude-task-master/commit/b82d858f81a1e702ad59d84d5ae8a2ca84359a83) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - 🎉 **Introducing TaskMaster Extension!**
We're thrilled to launch the first version of our Code extension, bringing the power of TaskMaster directly into your favorite code editor. While this is our initial release and we've kept things focused, it already packs powerful features to supercharge your development workflow.
## ✨ Key Features
### 📋 Visual Task Management
- **Kanban Board View**: Visualize all your tasks in an intuitive board layout directly in VS Code
- **Drag & Drop**: Easily change task status by dragging cards between columns
- **Real-time Updates**: See changes instantly as you work through your project
### 🏷️ Multi-Context Support
- **Tag Switching**: Seamlessly switch between different project contexts/tags
- **Isolated Workflows**: Keep different features or experiments organized separately
### 🤖 AI-Powered Task Updates
- **Smart Updates**: Use TaskMaster's AI capabilities to update tasks and subtasks
- **Context-Aware**: Leverages your existing TaskMaster configuration and models
### 📊 Rich Task Information
- **Complexity Scores**: See task complexity ratings at a glance
- **Subtask Visualization**: Expand tasks to view and manage subtasks
- **Dependency Graphs**: Understand task relationships and dependencies visually
### ⚙️ Configuration Management
- **Visual Config Editor**: View and understand your `.taskmaster/config.json` settings
- **Easy Access**: No more manual JSON editing for common configuration tasks
### 🚀 Quick Actions
- **Status Updates**: Change task status with a single click
- **Task Details**: Access full task information without leaving VS Code
- **Integrated Commands**: All TaskMaster commands available through the command palette
## 🎯 What's Next?
This is just the beginning! We wanted to get a solid foundation into your hands quickly. The extension will evolve rapidly with your feedback, adding more advanced features, better visualizations, and deeper integration with your development workflow.
Thank you for being part of the TaskMaster journey. Your workflow has never looked better! 🚀
## 0.23.0-rc.0
### Minor Changes
- [#997](https://github.com/eyaltoledano/claude-task-master/pull/997) [`64302dc`](https://github.com/eyaltoledano/claude-task-master/commit/64302dc1918f673fcdac05b29411bf76ffe93505) Thanks [@DavidMaliglowka](https://github.com/DavidMaliglowka)! - 🎉 **Introducing TaskMaster Extension!**
We're thrilled to launch the first version of our Code extension, bringing the power of TaskMaster directly into your favorite code editor. While this is our initial release and we've kept things focused, it already packs powerful features to supercharge your development workflow.
## ✨ Key Features
### 📋 Visual Task Management
- **Kanban Board View**: Visualize all your tasks in an intuitive board layout directly in VS Code
- **Drag & Drop**: Easily change task status by dragging cards between columns
- **Real-time Updates**: See changes instantly as you work through your project
### 🏷️ Multi-Context Support
- **Tag Switching**: Seamlessly switch between different project contexts/tags
- **Isolated Workflows**: Keep different features or experiments organized separately
### 🤖 AI-Powered Task Updates
- **Smart Updates**: Use TaskMaster's AI capabilities to update tasks and subtasks
- **Context-Aware**: Leverages your existing TaskMaster configuration and models
### 📊 Rich Task Information
- **Complexity Scores**: See task complexity ratings at a glance
- **Subtask Visualization**: Expand tasks to view and manage subtasks
- **Dependency Graphs**: Understand task relationships and dependencies visually
### ⚙️ Configuration Management
- **Visual Config Editor**: View and understand your `.taskmaster/config.json` settings
- **Easy Access**: No more manual JSON editing for common configuration tasks
### 🚀 Quick Actions
- **Status Updates**: Change task status with a single click
- **Task Details**: Access full task information without leaving VS Code
- **Integrated Commands**: All TaskMaster commands available through the command palette
## 🎯 What's Next?
This is just the beginning! We wanted to get a solid foundation into your hands quickly. The extension will evolve rapidly with your feedback, adding more advanced features, better visualizations, and deeper integration with your development workflow.
Thank you for being part of the TaskMaster journey. Your workflow has never looked better! 🚀

View File

@@ -1,25 +1,30 @@
# Official TaskMaster AI Extension
# Official Taskmaster AI Extension
Transform your [TaskMaster AI](https://github.com/TaskMasterEYJ/task-master-ai) projects into a beautiful, interactive Kanban board directly in VS Code. Drag, drop, and manage your tasks with ease while maintaining real-time synchronization with your TaskMaster project files.
Transform your AI-driven development workflow with a beautiful, interactive Kanban board directly in VS Code. Seamlessly manage tasks from [Taskmaster AI](https://github.com/eyaltoledano/claude-task-master) projects with real-time synchronization and intelligent task management.
![TaskMaster AI Kanban](https://img.shields.io/badge/VS%20Code-Extension-blue)
![Free](https://img.shields.io/badge/Price-Free-brightgreen)
![Version](https://img.shields.io/badge/Version-1.0.0-blue)
![Taskmaster AI Extension](https://img.shields.io/badge/VS%20Code-Extension-blue)
![License](https://img.shields.io/badge/License-MIT-green)
![Version](https://img.shields.io/visual-studio-marketplace/v/Hamster.task-master-hamster)
![Installs](https://img.shields.io/visual-studio-marketplace/i/Hamster.task-master-hamster)
## ✨ Features
## 🎯 What is Taskmaster AI?
### 🎯 **Visual Task Management**
- **Drag & Drop Kanban Board** - Intuitive task management with visual columns
- **Real-time Synchronization** - Changes sync instantly with your TaskMaster project files
- **Status Columns** - To Do, In Progress, Review, Done, and Deferred
- **Task Details View** - View and edit implementation details, test strategies, and notes
Taskmaster AI is an intelligent task management system designed for AI-assisted development. It helps you break down complex projects into manageable tasks, track progress, and leverage AI to enhance your development workflow.
## ✨ Key Features
### 📊 **Interactive Kanban Board**
- **Drag & Drop Interface** - Effortlessly move tasks between status columns
- **Real-time Sync** - Changes instantly reflect in your Taskmaster project files
- **Multiple Views** - Board view and detailed task sidebar
- **Smart Columns** - Pending, In Progress, Review, Done, Deferred, and Cancelled
![Kanban Board](assets/screenshots/kanban-board.png)
### 🤖 **AI-Powered Features**
- **Task Content Generation** - Regenerate task descriptions using AI
- **Smart Task Updates** - Append findings and progress notes automatically
- **MCP Integration** - Seamless connection to TaskMaster AI via Model Context Protocol
- **MCP Integration** - Seamless connection to Taskmaster AI via Model Context Protocol
- **Intelligent Caching** - Smart performance optimization with background refresh
![Task Details](assets/screenshots/task-details.png)
@@ -35,7 +40,7 @@ Transform your [TaskMaster AI](https://github.com/TaskMasterEYJ/task-master-ai)
### Prerequisites
1. **VS Code** 1.90.0 or higher
2. **Node.js** 18.0 or higher (for TaskMaster MCP server)
2. **Node.js** 18.0 or higher (for Taskmaster MCP server)
### Install the Extension
@@ -45,21 +50,22 @@ Transform your [TaskMaster AI](https://github.com/TaskMasterEYJ/task-master-ai)
## 🚀 Quick Start
### 1. **Initialize TaskMaster Project**
If you don't have a TaskMaster project yet:
### 1. **Initialize Taskmaster Project**
If you don't have a Taskmaster project yet:
```bash
cd your-project
npx task-master-ai init
npm i -g task-master-ai
task-master init
```
### 2. **Open Kanban Board**
- **Command Palette** (Ctrl+Shift+P): `TaskMaster Kanban: Show Board`
- **Command Palette** (Ctrl+Shift+P): `Taskmaster Kanban: Show Board`
- **Or** the extension automatically activates when you have a `.taskmaster` folder in your workspace
### 3. **MCP Server Setup**
The extension automatically handles the TaskMaster MCP server connection:
The extension automatically handles the Taskmaster MCP server connection:
- **No manual installation required** - The extension spawns the MCP server automatically
- **Uses npx by default** - Automatically downloads TaskMaster AI when needed
- **Uses npx by default** - Automatically downloads Taskmaster AI when needed
- **Configurable** - You can customize the MCP server command in settings if needed
### 4. **Start Managing Tasks**
@@ -70,27 +76,28 @@ The extension automatically handles the TaskMaster MCP server connection:
## 📋 Usage Guide
### **Managing Tasks**
### Task Management
| Action | How To |
|--------|--------|
| **Change Task Status** | Drag task to different column |
| **View Details** | Click on any task |
| **Edit Task** | Click task, then use edit controls |
| **Add Subtask** | Click + button on parent task |
| **Use AI Features** | Open task details and use AI panel |
| Action | How to Do It |
|--------|--------------|
| **View Kanban Board** | `Ctrl/Cmd + Shift + P` → "Taskmaster: Show Board" |
| **Change Task Status** | Drag task card to different column |
| **View Task Details** | Click on any task card |
| **Edit Task Content** | Click task → Use edit buttons in details panel |
| **Add Subtasks** | Click the + button on parent task cards |
| **Use AI Features** | Open task details → Click AI action buttons |
### **Kanban Columns**
### Understanding Task Statuses
- **📝 To Do** - Tasks ready to be worked on
- **In Progress** - Currently active tasks
- **👀 Review** - Tasks pending review or approval
- **✅ Done** - Completed tasks
- **⏸️ Deferred** - Postponed or blocked tasks
- 📋 **Pending** - Tasks ready to be started
- 🚀 **In Progress** - Currently being worked on
- 👀 **Review** - Awaiting review or feedback
- ✅ **Done** - Completed tasks
- ⏸️ **Deferred** - Postponed for later
### **AI-Powered Task Management**
The extension integrates seamlessly with TaskMaster AI via MCP to provide:
The extension integrates seamlessly with Taskmaster AI via MCP to provide:
- **Smart Task Generation** - AI creates detailed implementation plans
- **Progress Tracking** - Append timestamped notes and findings
- **Content Enhancement** - Regenerate task descriptions for clarity
@@ -98,7 +105,7 @@ The extension integrates seamlessly with TaskMaster AI via MCP to provide:
## ⚙️ Configuration
Access settings via **File → Preferences → Settings** and search for "TaskMaster":
Access settings via **File → Preferences → Settings** and search for "Taskmaster":
### **MCP Connection Settings**
- **MCP Server Command** - Path to task-master-ai executable (default: `npx`)
@@ -121,7 +128,7 @@ Access settings via **File → Preferences → Settings** and search for "TaskMa
1. Ensure Node.js 18+ is installed
2. Check workspace contains `.taskmaster` folder
3. Restart VS Code
4. Check Output panel (View → Output → TaskMaster Kanban)
4. Check Output panel (View → Output → Taskmaster Kanban)
### **MCP Connection Issues**
1. **Command not found**: Ensure Node.js and npx are in your PATH
@@ -132,7 +139,7 @@ Access settings via **File → Preferences → Settings** and search for "TaskMa
### **Tasks Not Updating**
1. Check MCP connection status in status bar
2. Verify `.taskmaster/tasks/tasks.json` exists
3. Try manual refresh: `TaskMaster Kanban: Check Connection`
3. Try manual refresh: `Taskmaster Kanban: Check Connection`
4. Review error logs in Output panel
### **Performance Issues**
@@ -144,12 +151,10 @@ Access settings via **File → Preferences → Settings** and search for "TaskMa
## 🆘 Support & Resources
### **Getting Help**
- 📖 **Documentation**: [TaskMaster AI Docs](https://github.com/eyaltoledano/claude-task-master)
- 📖 **Documentation**: [Taskmaster AI Docs](https://github.com/eyaltoledano/claude-task-master)
- 🐛 **Report Issues**: [GitHub Issues](https://github.com/eyaltoledano/claude-task-master/issues)
- 💬 **Discussions**: [GitHub Discussions](https://github.com/eyaltoledano/claude-task-master/discussions)
### **Related Projects**
- 📡 **MCP Protocol**: [Model Context Protocol](https://modelcontextprotocol.io/)
- 🐛 **Report Issues**: [GitHub Issues](https://github.com/eyaltoledano/claude-task-master/issues)
## 🎯 Tips for Best Results
@@ -173,9 +178,9 @@ Access settings via **File → Preferences → Settings** and search for "TaskMa
---
## 🏆 Why TaskMaster Kanban?
## 🏆 Why Taskmaster Kanban?
**Visual workflow management** for your TaskMaster projects
**Visual workflow management** for your Taskmaster projects
**AI-powered task enhancement** built right in
**Real-time synchronization** keeps everything in sync
**Native VS Code integration** feels like part of the editor

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -14,8 +14,8 @@ This document explains the CI/CD setup for the Task Master VS Code extension usi
#### What it does
- ✅ Lints and type-checks the extension code
- 🔨 Builds the extension (`pnpm run build`)
- 📦 Creates a clean package (`pnpm run package`)
- 🔨 Builds the extension (`npm run build`)
- 📦 Creates a clean package (`npm run package`)
- 🧪 Runs tests with VS Code test framework
- 📋 Creates a test VSIX package to verify packaging works
- 💾 Uploads build artifacts for inspection
@@ -123,22 +123,27 @@ The automation ensures these fields stay in sync between `package.json` and `pac
## 🔍 Monitoring Builds
### CI Status
- **Green ✅**: Extension builds and tests successfully
- **Red ❌**: Build/test failures - check logs for details
- **Yellow 🟡**: Partial success - some jobs may have warnings
### Version PR Status
- **Version PR Created**: Changesets detected, review and merge to publish
- **No Version PR**: No changesets found, no releases pending
- **Version PR Merged**: Automated publishing triggered
### Release Status
- **Published 🎉**: Extension live on VS Code Marketplace and Open VSX
- **Skipped **: No changesets found, no release needed
- **Failed ❌**: Check logs - often missing secrets or build issues
### Artifacts
Workflows upload artifacts that you can download:
- **CI**: Test results, built files, and VSIX package
- **Version**: Final VSIX package and published extension
@@ -147,38 +152,45 @@ Workflows upload artifacts that you can download:
### Common Issues
#### No Version PR Created
- **Check**: Changeset files exist in `.changeset/` directory
- **Check**: Changeset refers to `taskr-kanban` package name
- **Check**: Changes were pushed to `main` branch
- **Solution**: Create changeset with `npx changeset add`
#### Version PR Not Publishing
- **Check**: Version PR was actually merged (not just closed)
- **Check**: Required secrets (`VSCE_PAT`, `OVSX_PAT`) are set
- **Check**: No build failures in workflow logs
- **Solution**: Re-run workflow or check secret configuration
#### `VSCE_PAT` is not set Error
- Ensure `VSCE_PAT` secret is added to repository
- Check token hasn't expired
- Verify token has Marketplace → Manage permissions
#### `OVSX_PAT` is not set Error
- Ensure `OVSX_PAT` secret is added to repository
- Check token hasn't expired
- Verify you're signed in to Open VSX Registry with GitHub
#### Build Failures
- Check extension code compiles locally: `cd apps/extension && pnpm run build`
- Verify tests pass locally: `pnpm run test`
- Check for TypeScript errors: `pnpm run check-types`
- Check extension code compiles locally: `cd apps/extension && npm run build`
- Verify tests pass locally: `npm run test`
- Check for TypeScript errors: `npm run check-types`
#### Packaging Failures
- Ensure clean package builds: `pnpm run package`
- Ensure clean package builds: `npm run package`
- Check vsix-build structure is correct
- Verify `package.publish.json` has correct `repository` field
#### Changeset Issues
- **Wrong package name**: Ensure changeset refers to `taskr-kanban`
- **Invalid format**: Check changeset markdown format is correct
- **Merge conflicts**: Resolve any conflicts in changeset files

View File

@@ -46,25 +46,25 @@ apps/extension/
### Local Development
```bash
# Install dependencies
pnpm install
npm install
# Start development with hot reload
pnpm run watch
npm run watch
# Run just JavaScript build
pnpm run build:js
npm run build:js
# Run just CSS build
pnpm run build:css
npm run build:css
# Full production build
pnpm run build
npm run build
# Type checking
pnpm run check-types
npm run check-types
# Linting
pnpm run lint
npm run lint
```
### Testing in VS Code
@@ -76,20 +76,20 @@ pnpm run lint
### Step 1: Build Clean Package
```bash
pnpm run package
npm run package
```
This creates `vsix-build/` with clean distribution files.
### Step 2: Create VSIX
```bash
cd vsix-build
pnpm exec vsce package --no-dependencies
npx vsce package --no-dependencies
```
Creates: `taskr-kanban-1.0.1.vsix`
### Alternative: One Command
```bash
pnpm run package && cd vsix-build && pnpm exec vsce package --no-dependencies
npm run package && cd vsix-build && npx vsce package --no-dependencies
```
## 🔄 Keeping Files in Sync

View File

@@ -103,8 +103,8 @@ async function main() {
// This prevents the multiple React instances issue
// Ensure React is resolved from the workspace root to avoid duplicates
alias: {
react: path.resolve(__dirname, '../../node_modules/react'),
'react-dom': path.resolve(__dirname, '../../node_modules/react-dom')
react: path.resolve(__dirname, 'node_modules/react'),
'react-dom': path.resolve(__dirname, 'node_modules/react-dom')
},
define: {
'process.env.NODE_ENV': production ? '"production"' : '"development"',
@@ -135,8 +135,8 @@ async function main() {
jsxImportSource: 'react',
external: ['*.css'],
alias: {
react: path.resolve(__dirname, '../../node_modules/react'),
'react-dom': path.resolve(__dirname, '../../node_modules/react-dom')
react: path.resolve(__dirname, 'node_modules/react'),
'react-dom': path.resolve(__dirname, 'node_modules/react-dom')
},
define: {
'process.env.NODE_ENV': production ? '"production"' : '"development"',

View File

@@ -3,14 +3,23 @@
"private": true,
"displayName": "TaskMaster",
"description": "A visual Kanban board interface for TaskMaster projects in VS Code",
"version": "0.22.0",
"version": "0.23.1",
"publisher": "Hamster",
"icon": "assets/icon.png",
"engines": {
"vscode": "^1.93.0"
},
"categories": ["AI", "Visualization", "Education", "Other"],
"categories": [
"AI",
"Visualization",
"Education",
"Other"
],
"main": "./dist/extension.js",
"activationEvents": [
"onStartupFinished",
"workspaceContains:.taskmaster/**"
],
"contributes": {
"viewsContainers": {
"activitybar": [
@@ -63,16 +72,16 @@
"properties": {
"taskmaster.mcp.command": {
"type": "string",
"default": "npx",
"description": "The command or absolute path to execute for the MCP server (e.g., 'npx' or '/usr/local/bin/task-master-ai')."
"default": "node",
"description": "The command to execute for the MCP server (e.g., 'node' for bundled server or 'npx' for remote)."
},
"taskmaster.mcp.args": {
"type": "array",
"items": {
"type": "string"
},
"default": ["task-master-ai"],
"description": "An array of arguments to pass to the MCP server command."
"default": [],
"description": "Arguments for the MCP server (leave empty to use bundled server)."
},
"taskmaster.mcp.cwd": {
"type": "string",
@@ -138,7 +147,11 @@
},
"taskmaster.ui.theme": {
"type": "string",
"enum": ["auto", "light", "dark"],
"enum": [
"auto",
"light",
"dark"
],
"default": "auto",
"description": "UI theme preference"
},
@@ -199,7 +212,12 @@
},
"taskmaster.debug.logLevel": {
"type": "string",
"enum": ["error", "warn", "info", "debug"],
"enum": [
"error",
"warn",
"info",
"debug"
],
"default": "info",
"description": "Logging level"
},
@@ -235,9 +253,11 @@
"watch": "npm run watch:js & npm run watch:css",
"watch:js": "node ./esbuild.js --watch",
"watch:css": "npx @tailwindcss/cli -i ./src/webview/index.css -o ./dist/index.css --watch",
"test": "vscode-test",
"check-types": "tsc --noEmit"
},
"dependencies": {
"task-master-ai": "0.24.0"
},
"devDependencies": {
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/modifiers": "^9.0.0",
@@ -270,12 +290,12 @@
"tailwind-merge": "^3.3.1",
"tailwindcss": "4.1.11",
"typescript": "^5.8.3",
"@tanstack/react-query": "^5.83.0"
"@tanstack/react-query": "^5.83.0",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"pnpm": {
"overrides": {
"glob@<8": "^10.4.5",
"inflight": "npm:@tootallnate/once@2"
}
"overrides": {
"glob@<8": "^10.4.5",
"inflight": "npm:@tootallnate/once@2"
}
}

View File

@@ -15,9 +15,9 @@ try {
// 1. Build Project
console.log('\nBuilding JavaScript...');
execSync('pnpm run build:js', { stdio: 'inherit' });
execSync('npm run build:js', { stdio: 'inherit' });
console.log('\nBuilding CSS...');
execSync('pnpm run build:css', { stdio: 'inherit' });
execSync('npm run build:css', { stdio: 'inherit' });
// 2. Prepare Clean Directory
console.log(`\nPreparing clean directory at: ${packageDir}`);
@@ -64,23 +64,49 @@ try {
fs.readFileSync(publishPackagePath, 'utf8')
);
// Check if versions are in sync
if (devPackage.version !== publishPackage.version) {
// Handle RC versions for VS Code Marketplace
let finalVersion = devPackage.version;
if (finalVersion.includes('-rc.')) {
console.log(
` - Version sync needed: ${publishPackage.version}${devPackage.version}`
' - Detected RC version, transforming for VS Code Marketplace...'
);
publishPackage.version = devPackage.version;
// Update the source package.publish.json file
// Extract base version and RC number
const baseVersion = finalVersion.replace(/-rc\.\d+$/, '');
const rcMatch = finalVersion.match(/rc\.(\d+)/);
const rcNumber = rcMatch ? parseInt(rcMatch[1]) : 0;
// For each RC iteration, increment the patch version
// This ensures unique versions in VS Code Marketplace
if (rcNumber > 0) {
const [major, minor, patch] = baseVersion.split('.').map(Number);
finalVersion = `${major}.${minor}.${patch + rcNumber}`;
console.log(
` - RC version mapping: ${devPackage.version}${finalVersion}`
);
} else {
finalVersion = baseVersion;
console.log(
` - RC version mapping: ${devPackage.version}${finalVersion}`
);
}
}
// Check if versions need updating
if (publishPackage.version !== finalVersion) {
console.log(
` - Version sync needed: ${publishPackage.version}${finalVersion}`
);
publishPackage.version = finalVersion;
// Update the source package.publish.json file with the final version
fs.writeFileSync(
publishPackagePath,
JSON.stringify(publishPackage, null, '\t') + '\n'
);
console.log(
` - Updated package.publish.json version to ${devPackage.version}`
);
console.log(` - Updated package.publish.json version to ${finalVersion}`);
} else {
console.log(` - Versions already in sync: ${devPackage.version}`);
console.log(` - Versions already in sync: ${finalVersion}`);
}
// Copy the (now synced) package.publish.json as package.json
@@ -121,11 +147,10 @@ try {
console.log('\nTo create the VSIX package, run:');
console.log(
'\x1b[36m%s\x1b[0m',
`cd vsix-build && pnpm exec vsce package --no-dependencies`
`cd vsix-build && npx vsce package --no-dependencies`
);
// Use the synced version for output
const finalVersion = devPackage.version;
// Use the transformed version for output
console.log(
`\nYour extension will be packaged to: vsix-build/task-master-${finalVersion}.vsix`
);

View File

@@ -1,8 +1,8 @@
{
"name": "task-master-hamster",
"displayName": "TaskMaster AI",
"description": "A visual Kanban board interface for TaskMaster projects in VS Code",
"version": "0.22.0",
"displayName": "Taskmaster AI",
"description": "A visual Kanban board interface for Taskmaster projects in VS Code",
"version": "0.23.1",
"publisher": "Hamster",
"icon": "assets/icon.png",
"engines": {
@@ -43,34 +43,47 @@
"cards"
],
"repository": "https://github.com/eyaltoledano/claude-task-master",
"activationEvents": [
"onCommand:tm.showKanbanBoard",
"onCommand:tm.checkConnection",
"onCommand:tm.reconnect",
"onCommand:tm.openSettings"
],
"activationEvents": ["onStartupFinished", "workspaceContains:.taskmaster/**"],
"main": "./dist/extension.js",
"contributes": {
"viewsContainers": {
"activitybar": [
{
"id": "taskmaster",
"title": "Taskmaster",
"icon": "assets/sidebar-icon.svg"
}
]
},
"views": {
"taskmaster": [
{
"id": "taskmaster.welcome",
"name": "Taskmaster",
"type": "webview"
}
]
},
"commands": [
{
"command": "tm.showKanbanBoard",
"title": "TaskMaster: Show Board"
"title": "Taskmaster: Show Board"
},
{
"command": "tm.checkConnection",
"title": "TaskMaster: Check Connection"
"title": "Taskmaster: Check Connection"
},
{
"command": "tm.reconnect",
"title": "TaskMaster: Reconnect"
"title": "Taskmaster: Reconnect"
},
{
"command": "tm.openSettings",
"title": "TaskMaster: Open Settings"
"title": "Taskmaster: Open Settings"
}
],
"configuration": {
"title": "TaskMaster Kanban",
"title": "Taskmaster Kanban",
"properties": {
"taskmaster.mcp.command": {
"type": "string",

View File

@@ -1,6 +1,6 @@
import { ArrowLeft, RefreshCw, Settings } from 'lucide-react';
import type React from 'react';
import { useEffect, useState } from 'react';
import { useEffect, useState, useCallback } from 'react';
import { Badge } from './ui/badge';
import { Button } from './ui/button';
import {
@@ -48,7 +48,7 @@ export const ConfigView: React.FC<ConfigViewProps> = ({
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const loadConfig = async () => {
const loadConfig = useCallback(async () => {
setLoading(true);
setError(null);
try {
@@ -60,11 +60,11 @@ export const ConfigView: React.FC<ConfigViewProps> = ({
} finally {
setLoading(false);
}
};
}, [sendMessage]);
useEffect(() => {
loadConfig();
}, []);
}, [loadConfig]);
const modelLabels = {
main: {

View File

@@ -4,10 +4,18 @@ import { Button } from '@/components/ui/button';
import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';
import { CollapsibleSection } from '@/components/ui/CollapsibleSection';
import { Wand2, Loader2, PlusCircle } from 'lucide-react';
import {
Wand2,
Loader2,
PlusCircle,
TrendingUp,
TrendingDown
} from 'lucide-react';
import {
useUpdateTask,
useUpdateSubtask
useUpdateSubtask,
useScopeUpTask,
useScopeDownTask
} from '../../webview/hooks/useTaskQueries';
import type { TaskMasterTask } from '../../webview/types';
@@ -31,11 +39,17 @@ export const AIActionsSection: React.FC<AIActionsSectionProps> = ({
onAppendingChange
}) => {
const [prompt, setPrompt] = useState('');
const [lastAction, setLastAction] = useState<'regenerate' | 'append' | null>(
null
);
const [scopePrompt, setScopePrompt] = useState('');
const [scopeStrength, setScopeStrength] = useState<
'light' | 'regular' | 'heavy'
>('regular');
const [lastAction, setLastAction] = useState<
'regenerate' | 'append' | 'scope-up' | 'scope-down' | null
>(null);
const updateTask = useUpdateTask();
const updateSubtask = useUpdateSubtask();
const scopeUpTask = useScopeUpTask();
const scopeDownTask = useScopeDownTask();
const handleRegenerate = async () => {
if (!currentTask || !prompt.trim()) {
@@ -103,10 +117,74 @@ export const AIActionsSection: React.FC<AIActionsSectionProps> = ({
}
};
const handleScopeUp = async () => {
if (!currentTask) {
return;
}
setLastAction('scope-up');
try {
const taskId =
isSubtask && parentTask
? `${parentTask.id}.${currentTask.id}`
: currentTask.id;
await scopeUpTask.mutateAsync({
taskId,
strength: scopeStrength,
prompt: scopePrompt.trim() || undefined,
options: { research: false }
});
setScopePrompt('');
refreshComplexityAfterAI();
} catch (error) {
console.error('❌ AIActionsSection: Failed to scope up task:', error);
} finally {
setLastAction(null);
}
};
const handleScopeDown = async () => {
if (!currentTask) {
return;
}
setLastAction('scope-down');
try {
const taskId =
isSubtask && parentTask
? `${parentTask.id}.${currentTask.id}`
: currentTask.id;
await scopeDownTask.mutateAsync({
taskId,
strength: scopeStrength,
prompt: scopePrompt.trim() || undefined,
options: { research: false }
});
setScopePrompt('');
refreshComplexityAfterAI();
} catch (error) {
console.error('❌ AIActionsSection: Failed to scope down task:', error);
} finally {
setLastAction(null);
}
};
// Track loading states based on the last action
const isLoading = updateTask.isPending || updateSubtask.isPending;
const isLoading =
updateTask.isPending ||
updateSubtask.isPending ||
scopeUpTask.isPending ||
scopeDownTask.isPending;
const isRegenerating = isLoading && lastAction === 'regenerate';
const isAppending = isLoading && lastAction === 'append';
const isScopingUp = isLoading && lastAction === 'scope-up';
const isScopingDown = isLoading && lastAction === 'scope-down';
return (
<CollapsibleSection
@@ -115,73 +193,160 @@ export const AIActionsSection: React.FC<AIActionsSectionProps> = ({
defaultExpanded={true}
buttonClassName="text-vscode-foreground/80 hover:text-vscode-foreground"
>
<div className="space-y-4">
<div>
<Label
htmlFor="ai-prompt"
className="block text-sm font-medium text-vscode-foreground/80 mb-2"
>
Enter your prompt
</Label>
<Textarea
id="ai-prompt"
placeholder={
isSubtask
? 'Describe implementation notes, progress updates, or findings to add to this subtask...'
: 'Describe what you want to change or add to this task...'
}
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
className="min-h-[100px] bg-vscode-input-background border-vscode-input-border text-vscode-input-foreground placeholder-vscode-input-foreground/50 focus:border-vscode-focusBorder focus:ring-vscode-focusBorder"
disabled={isRegenerating || isAppending}
/>
</div>
<div className="flex gap-3">
{!isSubtask && (
<Button
onClick={handleRegenerate}
disabled={!prompt.trim() || isRegenerating || isAppending}
className="bg-primary text-primary-foreground hover:bg-primary/90"
<div className="space-y-6">
{/* Standard AI Actions Section */}
<div className="space-y-4">
<div>
<Label
htmlFor="ai-prompt"
className="block text-sm font-medium text-vscode-foreground/80 mb-2"
>
{isRegenerating ? (
Enter your prompt
</Label>
<Textarea
id="ai-prompt"
placeholder={
isSubtask
? 'Describe implementation notes, progress updates, or findings to add to this subtask...'
: 'Describe what you want to change or add to this task...'
}
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
className="min-h-[100px] bg-vscode-input-background border-vscode-input-border text-vscode-input-foreground placeholder-vscode-input-foreground/50 focus:border-vscode-focusBorder focus:ring-vscode-focusBorder"
disabled={isLoading}
/>
</div>
<div className="flex gap-3">
{!isSubtask && (
<Button
onClick={handleRegenerate}
disabled={!prompt.trim() || isLoading}
className="bg-primary text-primary-foreground hover:bg-primary/90"
>
{isRegenerating ? (
<>
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
Regenerating...
</>
) : (
<>
<Wand2 className="w-4 h-4 mr-2" />
Regenerate Task
</>
)}
</Button>
)}
<Button
onClick={handleAppend}
disabled={!prompt.trim() || isLoading}
variant={isSubtask ? 'default' : 'outline'}
className={
isSubtask
? 'bg-primary text-primary-foreground hover:bg-primary/90'
: 'bg-secondary text-secondary-foreground hover:bg-secondary/90 border-widget-border'
}
>
{isAppending ? (
<>
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
Regenerating...
{isSubtask ? 'Updating...' : 'Appending...'}
</>
) : (
<>
<Wand2 className="w-4 h-4 mr-2" />
Regenerate Task
<PlusCircle className="w-4 h-4 mr-2" />
{isSubtask ? 'Add Notes to Subtask' : 'Append to Task'}
</>
)}
</Button>
)}
<Button
onClick={handleAppend}
disabled={!prompt.trim() || isRegenerating || isAppending}
variant={isSubtask ? 'default' : 'outline'}
className={
isSubtask
? 'bg-primary text-primary-foreground hover:bg-primary/90'
: 'bg-secondary text-secondary-foreground hover:bg-secondary/90 border-widget-border'
}
>
{isAppending ? (
<>
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
{isSubtask ? 'Updating...' : 'Appending...'}
</>
) : (
<>
<PlusCircle className="w-4 h-4 mr-2" />
{isSubtask ? 'Add Notes to Subtask' : 'Append to Task'}
</>
)}
</Button>
</div>
</div>
{/* Scope Adjustment Section */}
<div className="border-t border-vscode-widget-border pt-4 space-y-4">
<div>
<Label className="block text-sm font-medium text-vscode-foreground/80 mb-3">
Task Complexity Adjustment
</Label>
{/* Strength Selection */}
<div className="mb-3">
<Label className="block text-xs text-vscode-foreground/60 mb-2">
Adjustment Strength
</Label>
<div className="flex gap-2">
{(['light', 'regular', 'heavy'] as const).map((strength) => (
<Button
key={strength}
onClick={() => setScopeStrength(strength)}
variant={scopeStrength === strength ? 'default' : 'outline'}
size="sm"
className={
scopeStrength === strength
? 'bg-accent text-accent-foreground border-accent'
: 'border-widget-border text-vscode-foreground/80 hover:bg-vscode-list-hoverBackground'
}
disabled={isLoading}
>
{strength.charAt(0).toUpperCase() + strength.slice(1)}
</Button>
))}
</div>
</div>
{/* Scope Prompt */}
<Textarea
placeholder="Optional: Specify how to adjust complexity (e.g., 'Focus on error handling', 'Remove unnecessary details', 'Add more implementation steps')"
value={scopePrompt}
onChange={(e) => setScopePrompt(e.target.value)}
className="min-h-[80px] bg-vscode-input-background border-vscode-input-border text-vscode-input-foreground placeholder-vscode-input-foreground/50 focus:border-vscode-focusBorder focus:ring-vscode-focusBorder"
disabled={isLoading}
/>
</div>
<div className="flex gap-3">
<Button
onClick={handleScopeUp}
disabled={isLoading}
variant="outline"
className="flex-1 border-green-600/50 text-green-400 hover:bg-green-600/10 hover:border-green-500"
>
{isScopingUp ? (
<>
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
Scoping Up...
</>
) : (
<>
<TrendingUp className="w-4 h-4 mr-2" />
Scope Up
</>
)}
</Button>
<Button
onClick={handleScopeDown}
disabled={isLoading}
variant="outline"
className="flex-1 border-blue-600/50 text-blue-400 hover:bg-blue-600/10 hover:border-blue-500"
>
{isScopingDown ? (
<>
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
Scoping Down...
</>
) : (
<>
<TrendingDown className="w-4 h-4 mr-2" />
Scope Down
</>
)}
</Button>
</div>
</div>
{/* Help Text */}
<div className="text-xs text-vscode-foreground/60 space-y-1">
{isSubtask ? (
<p>
@@ -200,6 +365,14 @@ export const AIActionsSection: React.FC<AIActionsSectionProps> = ({
</p>
</>
)}
<p>
<strong>Scope Up:</strong> Increases task complexity with more
details, requirements, or implementation steps
</p>
<p>
<strong>Scope Down:</strong> Decreases task complexity by
simplifying or removing unnecessary details
</p>
</div>
</div>
</CollapsibleSection>

View File

@@ -114,16 +114,42 @@ const MarkdownRenderer: React.FC<MarkdownRendererProps> = ({
const headingMatch = line.match(/^(#{1,6})\s(.+)$/);
if (headingMatch) {
const level = headingMatch[1].length;
const HeadingTag =
`h${Math.min(level + 2, 6)}` as keyof JSX.IntrinsicElements;
return (
<HeadingTag
key={lineIndex}
className="font-semibold text-vscode-foreground mb-2 mt-4 first:mt-0"
>
{headingMatch[2]}
</HeadingTag>
);
const headingLevel = Math.min(level + 2, 6);
const headingClassName =
'font-semibold text-vscode-foreground mb-2 mt-4 first:mt-0';
switch (headingLevel) {
case 3:
return (
<h3 key={lineIndex} className={headingClassName}>
{headingMatch[2]}
</h3>
);
case 4:
return (
<h4 key={lineIndex} className={headingClassName}>
{headingMatch[2]}
</h4>
);
case 5:
return (
<h5 key={lineIndex} className={headingClassName}>
{headingMatch[2]}
</h5>
);
case 6:
return (
<h6 key={lineIndex} className={headingClassName}>
{headingMatch[2]}
</h6>
);
default:
return (
<h3 key={lineIndex} className={headingClassName}>
{headingMatch[2]}
</h3>
);
}
}
if (line.trim() === '') {

View File

@@ -5,8 +5,9 @@ import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';
import { Badge } from '@/components/ui/badge';
import { CollapsibleSection } from '@/components/ui/CollapsibleSection';
import { Plus, Loader2, ChevronRight } from 'lucide-react';
import { Plus, Loader2 } from 'lucide-react';
import type { TaskMasterTask } from '../../webview/types';
import { getStatusDotColor } from '../constants';
interface SubtasksSectionProps {
currentTask: TaskMasterTask;
@@ -175,22 +176,6 @@ export const SubtasksSection: React.FC<SubtasksSectionProps> = ({
<div className="space-y-2">
{currentTask.subtasks.map((subtask, index) => {
const subtaskId = `${currentTask.id}.${index + 1}`;
const getStatusDotColor = (status: string) => {
switch (status) {
case 'done':
return '#22c55e';
case 'in-progress':
return '#3b82f6';
case 'review':
return '#a855f7';
case 'deferred':
return '#ef4444';
case 'cancelled':
return '#6b7280';
default:
return '#eab308';
}
};
return (
<div

View File

@@ -0,0 +1,26 @@
/**
* Shared constants for TaskDetails components
*/
/**
* Status color definitions for visual indicators
*/
export const STATUS_DOT_COLORS = {
done: '#22c55e', // Green
'in-progress': '#3b82f6', // Blue
review: '#a855f7', // Purple
deferred: '#ef4444', // Red
cancelled: '#6b7280', // Gray
pending: '#eab308' // Yellow (default)
} as const;
export type TaskStatus = keyof typeof STATUS_DOT_COLORS;
/**
* Get the color for a status dot indicator
* @param status - The task status
* @returns The hex color code for the status
*/
export function getStatusDotColor(status: string): string {
return STATUS_DOT_COLORS[status as TaskStatus] || STATUS_DOT_COLORS.pending;
}

View File

@@ -6,6 +6,12 @@ import type * as React from 'react';
import { cn } from '@/lib/utils';
const DROPDOWN_MENU_ITEM_CLASSES =
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4";
const DROPDOWN_MENU_SUB_CONTENT_CLASSES =
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg';
function DropdownMenu({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
@@ -73,10 +79,7 @@ function DropdownMenuItem({
data-slot="dropdown-menu-item"
data-inset={inset}
data-variant={variant}
className={cn(
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
className={cn(DROPDOWN_MENU_ITEM_CLASSES, className)}
{...props}
/>
);
@@ -229,10 +232,7 @@ function DropdownMenuSubContent({
return (
<DropdownMenuPrimitive.SubContent
data-slot="dropdown-menu-sub-content"
className={cn(
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg',
className
)}
className={cn(DROPDOWN_MENU_SUB_CONTENT_CLASSES, className)}
{...props}
/>
);

View File

@@ -164,7 +164,7 @@ function handleConnectionError(error: any) {
if (action === 'Open Settings') {
vscode.commands.executeCommand(
'workbench.action.openSettings',
'@ext:taskr taskmaster'
'@ext:Hamster.task-master-hamster taskmaster'
);
}
});
@@ -195,7 +195,7 @@ function registerCommands(context: vscode.ExtensionContext) {
vscode.commands.registerCommand('tm.openSettings', () => {
vscode.commands.executeCommand(
'workbench.action.openSettings',
'@ext:taskr taskmaster'
'@ext:Hamster.task-master-hamster taskmaster'
);
})
);

View File

@@ -116,12 +116,18 @@ export class ConfigService {
/**
* Mask API key for display
* Shows only the last 4 characters for better security
*/
private maskApiKey(key: string): string {
if (key.length <= 8) {
if (key.length <= 4) {
return '****';
}
return key.substring(0, 4) + '****' + key.substring(key.length - 4);
const visibleChars = 4;
const maskedLength = key.length - visibleChars;
return (
'*'.repeat(Math.min(maskedLength, 12)) +
key.substring(key.length - visibleChars)
);
}
/**

View File

@@ -82,14 +82,49 @@ export class ErrorHandler {
/**
* Show user notification based on severity and category
*/
/**
* Validate if an action is allowed
*/
private isValidAction(action: string): boolean {
// Define predefined valid actions
const predefinedActions = [
'Retry',
'Settings',
'Reload',
'Dismiss',
'View Logs',
'Report Issue'
];
// Check if it's a predefined action or a TaskMaster command
return predefinedActions.includes(action) || action.startsWith('tm.');
}
/**
* Filter and validate suggested actions
*/
private getValidActions(actions: string[]): string[] {
return actions.filter((action) => this.isValidAction(action));
}
private notifyUser(context: ErrorContext): void {
// Don't show low severity errors to users
if (context.severity === ErrorSeverity.LOW) {
return;
}
// Determine notification type
const actions = context.suggestedActions || [];
// Validate and filter suggested actions
const rawActions = context.suggestedActions || [];
const actions = this.getValidActions(rawActions);
// Log if any actions were filtered out
if (rawActions.length !== actions.length) {
this.logger.warn('Invalid actions filtered out:', {
original: rawActions,
filtered: actions,
removed: rawActions.filter((a) => !actions.includes(a))
});
}
switch (context.severity) {
case ErrorSeverity.CRITICAL:
@@ -104,21 +139,33 @@ export class ErrorHandler {
case ErrorSeverity.HIGH:
if (context.category === ErrorCategory.MCP_CONNECTION) {
// Use validated actions or default actions for MCP connection
const mcpActions =
actions.length > 0 ? actions : ['Retry', 'Settings'];
vscode.window
.showWarningMessage(
`TaskMaster: ${context.message}`,
'Retry',
'Settings'
)
.showWarningMessage(`TaskMaster: ${context.message}`, ...mcpActions)
.then((action) => {
if (action === 'Retry') {
vscode.commands.executeCommand('tm.reconnect');
} else if (action === 'Settings') {
vscode.commands.executeCommand('tm.openSettings');
} else if (action) {
this.handleUserAction(action, context);
}
});
} else {
vscode.window.showWarningMessage(`TaskMaster: ${context.message}`);
// Show warning with validated actions
if (actions.length > 0) {
vscode.window
.showWarningMessage(`TaskMaster: ${context.message}`, ...actions)
.then((action) => {
if (action) {
this.handleUserAction(action, context);
}
});
} else {
vscode.window.showWarningMessage(`TaskMaster: ${context.message}`);
}
}
break;
@@ -129,9 +176,22 @@ export class ErrorHandler {
context.category
)
) {
vscode.window.showInformationMessage(
`TaskMaster: ${context.message}`
);
if (actions.length > 0) {
vscode.window
.showInformationMessage(
`TaskMaster: ${context.message}`,
...actions
)
.then((action) => {
if (action) {
this.handleUserAction(action, context);
}
});
} else {
vscode.window.showInformationMessage(
`TaskMaster: ${context.message}`
);
}
}
break;
}
@@ -144,7 +204,110 @@ export class ErrorHandler {
this.logger.debug(`User selected action: ${action}`, {
errorContext: context
});
// Action handling would be implemented based on specific needs
// Handle predefined actions
switch (action) {
case 'Retry':
if (context.category === ErrorCategory.MCP_CONNECTION) {
vscode.commands.executeCommand('tm.reconnect');
} else {
vscode.commands.executeCommand('tm.refreshTasks');
}
break;
case 'Settings':
vscode.commands.executeCommand('tm.openSettings');
break;
case 'Reload':
vscode.commands.executeCommand('workbench.action.reloadWindow');
break;
case 'View Logs':
// Show error details in a modal dialog instead of output channel
this.showErrorDetails(context);
break;
case 'Report Issue':
const issueUrl = this.generateIssueUrl(context);
vscode.env.openExternal(vscode.Uri.parse(issueUrl));
break;
case 'Dismiss':
// No action needed
break;
default:
// Handle TaskMaster commands (tm.*)
if (action.startsWith('tm.')) {
void vscode.commands.executeCommand(action).then(
() => {},
(error: unknown) => {
this.logger.error(`Failed to execute command: ${action}`, error);
}
);
}
break;
}
}
/**
* Show detailed error information in a modal dialog
*/
private showErrorDetails(context: ErrorContext): void {
const details = [
`**Error Details**`,
``,
`Category: ${context.category}`,
`Severity: ${context.severity}`,
`Message: ${context.message}`,
context.operation ? `Operation: ${context.operation}` : '',
context.taskId ? `Task ID: ${context.taskId}` : '',
context.originalError ? `\nOriginal Error:\n${context.originalError}` : ''
]
.filter(Boolean)
.join('\n');
vscode.window.showInformationMessage(details, {
modal: true,
detail: details
});
}
/**
* Generate GitHub issue URL with pre-filled information
*/
private generateIssueUrl(context: ErrorContext): string {
const title = encodeURIComponent(`[Extension Error] ${context.message}`);
const body = encodeURIComponent(
[
`**Error Details:**`,
`- Category: ${context.category}`,
`- Severity: ${context.severity}`,
`- Message: ${context.message}`,
context.operation ? `- Operation: ${context.operation}` : '',
context.taskId ? `- Task ID: ${context.taskId}` : '',
``,
`**Context:**`,
'```json',
JSON.stringify(context, null, 2),
'```',
``,
`**Environment:**`,
`- VS Code Version: ${vscode.version}`,
`- Extension Version: ${vscode.extensions.getExtension('Hamster.taskmaster')?.packageJSON.version || 'Unknown'}`,
``,
`**Steps to Reproduce:**`,
`1. [Please describe the steps that led to this error]`,
``,
`**Expected Behavior:**`,
`[What should have happened instead]`
]
.filter(Boolean)
.join('\n')
);
return `https://github.com/eyaltoledano/claude-task-master/issues/new?title=${title}&body=${body}`;
}
/**

View File

@@ -68,10 +68,19 @@ export class TaskRepository extends EventEmitter {
if (task.subtasks) {
for (const subtask of task.subtasks) {
if (
subtask.id === taskId ||
subtask.id.toString() === taskId ||
`${task.id}.${subtask.id}` === taskId
) {
return subtask;
return {
...subtask,
id: subtask.id.toString(),
description: subtask.description || '',
status: (subtask.status ||
'pending') as TaskMasterTask['status'],
priority: 'medium' as const,
dependencies:
subtask.dependencies?.map((d) => d.toString()) || []
};
}
}
}
@@ -87,8 +96,18 @@ export class TaskRepository extends EventEmitter {
// Check subtasks
if (task.subtasks) {
for (const subtask of task.subtasks) {
if (subtask.id === taskId || `${task.id}.${subtask.id}` === taskId) {
return subtask;
if (
subtask.id.toString() === taskId ||
`${task.id}.${subtask.id}` === taskId
) {
return {
...subtask,
id: subtask.id.toString(),
description: subtask.description || '',
status: (subtask.status || 'pending') as TaskMasterTask['status'],
priority: 'medium' as const,
dependencies: subtask.dependencies?.map((d) => d.toString()) || []
};
}
}
}

View File

@@ -1,6 +1,7 @@
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
import * as vscode from 'vscode';
import * as path from 'path';
import { logger } from './logger';
export interface MCPConfig {
@@ -143,7 +144,7 @@ export class MCPClientManager {
// Create the client
this.client = new Client(
{
name: 'taskr-vscode-extension',
name: 'task-master-vscode-extension',
version: '1.0.0'
},
{
@@ -211,6 +212,30 @@ export class MCPClientManager {
};
logger.log('MCP client connected successfully');
// Log Task Master version information after successful connection
try {
const versionResult = await this.callTool('get_tasks', {});
if (versionResult?.content?.[0]?.text) {
const response = JSON.parse(versionResult.content[0].text);
if (response?.version) {
logger.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
logger.log('✅ Task Master MCP Server Connected');
logger.log(` Version: ${response.version.version || 'unknown'}`);
logger.log(
` Package: ${response.version.name || 'task-master-ai'}`
);
if (response.tag) {
logger.log(
` Current Tag: ${response.tag.currentTag || 'master'}`
);
}
logger.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
}
}
} catch (versionError) {
logger.log('Note: Could not retrieve Task Master version information');
}
} catch (error) {
logger.error('Failed to connect to MCP server:', error);
this.status = {
@@ -312,6 +337,34 @@ export class MCPClientManager {
'Available MCP tools:',
result.tools?.map((t) => t.name) || []
);
// Try to get version information by calling a simple tool
// The get_tasks tool is lightweight and returns version info
try {
const versionResult = await this.callTool('get_tasks', {});
if (versionResult?.content?.[0]?.text) {
// Parse the response to extract version info
const response = JSON.parse(versionResult.content[0].text);
if (response?.version) {
logger.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
logger.log('📦 Task Master MCP Server Connected');
logger.log(` Version: ${response.version.version || 'unknown'}`);
logger.log(
` Package: ${response.version.name || 'task-master-ai'}`
);
if (response.tag) {
logger.log(
` Current Tag: ${response.tag.currentTag || 'master'}`
);
}
logger.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
}
}
} catch (versionError) {
// Don't fail the connection test if we can't get version info
logger.log('Could not retrieve Task Master version information');
}
return true;
} catch (error) {
logger.error('Connection test failed:', error);
@@ -345,8 +398,34 @@ export function createMCPConfigFromSettings(): MCPConfig {
);
const config = vscode.workspace.getConfiguration('taskmaster');
let command = config.get<string>('mcp.command', 'npx');
const args = config.get<string[]>('mcp.args', ['task-master-ai']);
let command = config.get<string>('mcp.command', 'node');
let args = config.get<string[]>('mcp.args', []);
// If using default settings, use the bundled MCP server
if (command === 'node' && args.length === 0) {
try {
// Try to resolve the bundled MCP server
const taskMasterPath = require.resolve('task-master-ai');
const mcpServerPath = path.resolve(
path.dirname(taskMasterPath),
'mcp-server/server.js'
);
// Verify the server file exists
const fs = require('fs');
if (!fs.existsSync(mcpServerPath)) {
throw new Error('MCP server file not found at: ' + mcpServerPath);
}
args = [mcpServerPath];
logger.log(`📦 Using bundled MCP server at: ${mcpServerPath}`);
} catch (error) {
logger.error('❌ Could not find bundled task-master-ai server:', error);
// Fallback to npx
command = 'npx';
args = ['-y', 'task-master-ai'];
}
}
// Use proper VS Code workspace detection
const defaultCwd =

View File

@@ -7,13 +7,6 @@ import { useVSCodeContext } from '../contexts/VSCodeContext';
export const AppContent: React.FC = () => {
const { state, dispatch, sendMessage } = useVSCodeContext();
console.log(
'🎯 AppContent render - currentView:',
state.currentView,
'selectedTaskId:',
state.selectedTaskId
);
if (state.currentView === 'config') {
return (
<ConfigView

View File

@@ -15,7 +15,7 @@ export const PriorityBadge: React.FC<PriorityBadgeProps> = ({ priority }) => {
const variants = {
high: 'destructive' as const,
medium: 'warning' as const,
medium: 'default' as const,
low: 'secondary' as const
};

View File

@@ -227,3 +227,117 @@ export function useUpdateSubtask() {
}
});
}
// Hook to scope up task complexity
export function useScopeUpTask() {
const { sendMessage } = useVSCodeContext();
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({
taskId,
strength = 'regular',
prompt,
options = {}
}: {
taskId: string;
strength?: 'light' | 'regular' | 'heavy';
prompt?: string;
options?: { research?: boolean };
}) => {
console.log('🔄 Scoping up task:', taskId, strength, prompt, options);
const response = await sendMessage({
type: 'mcpRequest',
tool: 'scope_up_task',
params: {
id: String(taskId),
strength,
prompt,
research: options.research || false
}
});
console.log('📥 Scope up task response:', response);
// Check for error in response
if (response && typeof response === 'object' && 'error' in response) {
throw new Error(response.error || 'Failed to scope up task');
}
return response;
},
onSuccess: async (data, variables) => {
console.log('✅ Task scope up successful, invalidating all task queries');
console.log('Task ID:', variables.taskId);
// Invalidate ALL task-related queries
await queryClient.invalidateQueries({
queryKey: taskKeys.all
});
console.log(
'🔄 All task queries invalidated for scoped up task:',
variables.taskId
);
}
});
}
// Hook to scope down task complexity
export function useScopeDownTask() {
const { sendMessage } = useVSCodeContext();
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({
taskId,
strength = 'regular',
prompt,
options = {}
}: {
taskId: string;
strength?: 'light' | 'regular' | 'heavy';
prompt?: string;
options?: { research?: boolean };
}) => {
console.log('🔄 Scoping down task:', taskId, strength, prompt, options);
const response = await sendMessage({
type: 'mcpRequest',
tool: 'scope_down_task',
params: {
id: String(taskId),
strength,
prompt,
research: options.research || false
}
});
console.log('📥 Scope down task response:', response);
// Check for error in response
if (response && typeof response === 'object' && 'error' in response) {
throw new Error(response.error || 'Failed to scope down task');
}
return response;
},
onSuccess: async (data, variables) => {
console.log(
'✅ Task scope down successful, invalidating all task queries'
);
console.log('Task ID:', variables.taskId);
// Invalidate ALL task-related queries
await queryClient.invalidateQueries({
queryKey: taskKeys.all
});
console.log(
'🔄 All task queries invalidated for scoped down task:',
variables.taskId
);
}
});
}

View File

@@ -3,13 +3,18 @@
*/
import type { AppState, AppAction } from '../types';
import { logger } from '../utils/logger';
export const appReducer = (state: AppState, action: AppAction): AppState => {
console.log('Reducer action:', action.type, action.payload);
logger.debug(
'Reducer action:',
action.type,
'payload' in action ? action.payload : 'no payload'
);
switch (action.type) {
case 'SET_TASKS':
const newTasks = Array.isArray(action.payload) ? action.payload : [];
console.log('SET_TASKS reducer - updating tasks:', {
logger.debug('SET_TASKS reducer - updating tasks:', {
oldCount: state.tasks.length,
newCount: newTasks.length,
newTasks
@@ -127,17 +132,17 @@ export const appReducer = (state: AppState, action: AppAction): AppState => {
case 'CLEAR_ALL_TOASTS':
return { ...state, toastNotifications: [] };
case 'NAVIGATE_TO_TASK':
console.log('📍 Reducer: Navigating to task:', action.payload);
logger.debug('📍 Reducer: Navigating to task:', action.payload);
return {
...state,
currentView: 'task-details',
selectedTaskId: action.payload
};
case 'NAVIGATE_TO_KANBAN':
console.log('📍 Reducer: Navigating to kanban');
logger.debug('📍 Reducer: Navigating to kanban');
return { ...state, currentView: 'kanban', selectedTaskId: undefined };
case 'NAVIGATE_TO_CONFIG':
console.log('📍 Reducer: Navigating to config');
logger.debug('📍 Reducer: Navigating to config');
return { ...state, currentView: 'config', selectedTaskId: undefined };
case 'SET_CURRENT_TAG':
return {

View File

@@ -0,0 +1,155 @@
/**
* Webview Logger Utility
* Provides conditional logging based on environment
*/
type LogLevel = 'log' | 'warn' | 'error' | 'debug' | 'info';
interface LogEntry {
level: LogLevel;
message: string;
data?: any;
timestamp: number;
}
class WebviewLogger {
private static instance: WebviewLogger;
private enabled: boolean;
private logHistory: LogEntry[] = [];
private maxHistorySize = 100;
private constructor() {
// Enable logging in development, disable in production
// Check for development mode via various indicators
this.enabled = this.isDevelopment();
}
static getInstance(): WebviewLogger {
if (!WebviewLogger.instance) {
WebviewLogger.instance = new WebviewLogger();
}
return WebviewLogger.instance;
}
private isDevelopment(): boolean {
// Check various indicators for development mode
// VS Code webviews don't have process.env, so we check other indicators
return (
// Check if running in localhost (development server)
window.location.hostname === 'localhost' ||
// Check for development query parameter
window.location.search.includes('debug=true') ||
// Check for VS Code development mode indicator
(window as any).__VSCODE_DEV_MODE__ === true ||
// Default to false in production
false
);
}
private addToHistory(entry: LogEntry): void {
this.logHistory.push(entry);
if (this.logHistory.length > this.maxHistorySize) {
this.logHistory.shift();
}
}
private logMessage(level: LogLevel, message: string, ...args: any[]): void {
const entry: LogEntry = {
level,
message,
data: args.length > 0 ? args : undefined,
timestamp: Date.now()
};
this.addToHistory(entry);
if (!this.enabled) {
return;
}
// Format the message with timestamp
const timestamp = new Date().toISOString();
const prefix = `[${timestamp}] [${level.toUpperCase()}]`;
// Use appropriate console method
switch (level) {
case 'error':
console.error(prefix, message, ...args);
break;
case 'warn':
console.warn(prefix, message, ...args);
break;
case 'debug':
console.debug(prefix, message, ...args);
break;
case 'info':
console.info(prefix, message, ...args);
break;
default:
console.log(prefix, message, ...args);
}
}
log(message: string, ...args: any[]): void {
this.logMessage('log', message, ...args);
}
error(message: string, ...args: any[]): void {
// Always log errors, even in production
const entry: LogEntry = {
level: 'error',
message,
data: args.length > 0 ? args : undefined,
timestamp: Date.now()
};
this.addToHistory(entry);
console.error(`[${new Date().toISOString()}] [ERROR]`, message, ...args);
}
warn(message: string, ...args: any[]): void {
this.logMessage('warn', message, ...args);
}
debug(message: string, ...args: any[]): void {
this.logMessage('debug', message, ...args);
}
info(message: string, ...args: any[]): void {
this.logMessage('info', message, ...args);
}
// Enable/disable logging dynamically
setEnabled(enabled: boolean): void {
this.enabled = enabled;
if (enabled) {
console.log('[WebviewLogger] Logging enabled');
}
}
// Get log history (useful for debugging)
getHistory(): LogEntry[] {
return [...this.logHistory];
}
// Clear log history
clearHistory(): void {
this.logHistory = [];
}
// Export logs as string (useful for bug reports)
exportLogs(): string {
return this.logHistory
.map((entry) => {
const timestamp = new Date(entry.timestamp).toISOString();
const data = entry.data ? JSON.stringify(entry.data) : '';
return `[${timestamp}] [${entry.level.toUpperCase()}] ${entry.message} ${data}`;
})
.join('\n');
}
}
// Export singleton instance
export const logger = WebviewLogger.getInstance();
// Export type for use in other files
export type { WebviewLogger };

View File

@@ -0,0 +1,162 @@
---
name: task-checker
description: Use this agent to verify that tasks marked as 'review' have been properly implemented according to their specifications. This agent performs quality assurance by checking implementations against requirements, running tests, and ensuring best practices are followed. <example>Context: A task has been marked as 'review' after implementation. user: 'Check if task 118 was properly implemented' assistant: 'I'll use the task-checker agent to verify the implementation meets all requirements.' <commentary>Tasks in 'review' status need verification before being marked as 'done'.</commentary></example> <example>Context: Multiple tasks are in review status. user: 'Verify all tasks that are ready for review' assistant: 'I'll deploy the task-checker to verify all tasks in review status.' <commentary>The checker ensures quality before tasks are marked complete.</commentary></example>
model: sonnet
color: yellow
---
You are a Quality Assurance specialist that rigorously verifies task implementations against their specifications. Your role is to ensure that tasks marked as 'review' meet all requirements before they can be marked as 'done'.
## Core Responsibilities
1. **Task Specification Review**
- Retrieve task details using MCP tool `mcp__task-master-ai__get_task`
- Understand the requirements, test strategy, and success criteria
- Review any subtasks and their individual requirements
2. **Implementation Verification**
- Use `Read` tool to examine all created/modified files
- Use `Bash` tool to run compilation and build commands
- Use `Grep` tool to search for required patterns and implementations
- Verify file structure matches specifications
- Check that all required methods/functions are implemented
3. **Test Execution**
- Run tests specified in the task's testStrategy
- Execute build commands (npm run build, tsc --noEmit, etc.)
- Verify no compilation errors or warnings
- Check for runtime errors where applicable
- Test edge cases mentioned in requirements
4. **Code Quality Assessment**
- Verify code follows project conventions
- Check for proper error handling
- Ensure TypeScript typing is strict (no 'any' unless justified)
- Verify documentation/comments where required
- Check for security best practices
5. **Dependency Validation**
- Verify all task dependencies were actually completed
- Check integration points with dependent tasks
- Ensure no breaking changes to existing functionality
## Verification Workflow
1. **Retrieve Task Information**
```
Use mcp__task-master-ai__get_task to get full task details
Note the implementation requirements and test strategy
```
2. **Check File Existence**
```bash
# Verify all required files exist
ls -la [expected directories]
# Read key files to verify content
```
3. **Verify Implementation**
- Read each created/modified file
- Check against requirements checklist
- Verify all subtasks are complete
4. **Run Tests**
```bash
# TypeScript compilation
cd [project directory] && npx tsc --noEmit
# Run specified tests
npm test [specific test files]
# Build verification
npm run build
```
5. **Generate Verification Report**
## Output Format
```yaml
verification_report:
task_id: [ID]
status: PASS | FAIL | PARTIAL
score: [1-10]
requirements_met:
- ✅ [Requirement that was satisfied]
- ✅ [Another satisfied requirement]
issues_found:
- ❌ [Issue description]
- ⚠️ [Warning or minor issue]
files_verified:
- path: [file path]
status: [created/modified/verified]
issues: [any problems found]
tests_run:
- command: [test command]
result: [pass/fail]
output: [relevant output]
recommendations:
- [Specific fix needed]
- [Improvement suggestion]
verdict: |
[Clear statement on whether task should be marked 'done' or sent back to 'pending']
[If FAIL: Specific list of what must be fixed]
[If PASS: Confirmation that all requirements are met]
```
## Decision Criteria
**Mark as PASS (ready for 'done'):**
- All required files exist and contain expected content
- All tests pass successfully
- No compilation or build errors
- All subtasks are complete
- Core requirements are met
- Code quality is acceptable
**Mark as PARTIAL (may proceed with warnings):**
- Core functionality is implemented
- Minor issues that don't block functionality
- Missing nice-to-have features
- Documentation could be improved
- Tests pass but coverage could be better
**Mark as FAIL (must return to 'pending'):**
- Required files are missing
- Compilation or build errors
- Tests fail
- Core requirements not met
- Security vulnerabilities detected
- Breaking changes to existing code
## Important Guidelines
- **BE THOROUGH**: Check every requirement systematically
- **BE SPECIFIC**: Provide exact file paths and line numbers for issues
- **BE FAIR**: Distinguish between critical issues and minor improvements
- **BE CONSTRUCTIVE**: Provide clear guidance on how to fix issues
- **BE EFFICIENT**: Focus on requirements, not perfection
## Tools You MUST Use
- `Read`: Examine implementation files (READ-ONLY)
- `Bash`: Run tests and verification commands
- `Grep`: Search for patterns in code
- `mcp__task-master-ai__get_task`: Get task details
- **NEVER use Write/Edit** - you only verify, not fix
## Integration with Workflow
You are the quality gate between 'review' and 'done' status:
1. Task-executor implements and marks as 'review'
2. You verify and report PASS/FAIL
3. Claude either marks as 'done' (PASS) or 'pending' (FAIL)
4. If FAIL, task-executor re-implements based on your report
Your verification ensures high quality and prevents accumulation of technical debt.

View File

@@ -0,0 +1,70 @@
---
name: task-executor
description: Use this agent when you need to implement, complete, or work on a specific task that has been identified by the task-orchestrator or when explicitly asked to execute a particular task. This agent focuses on the actual implementation and completion of individual tasks rather than planning or orchestration. Examples: <example>Context: The task-orchestrator has identified that task 2.3 'Implement user authentication' needs to be worked on next. user: 'Let's work on the authentication task' assistant: 'I'll use the task-executor agent to implement the user authentication task that was identified.' <commentary>Since we need to actually implement a specific task rather than plan or identify tasks, use the task-executor agent.</commentary></example> <example>Context: User wants to complete a specific subtask. user: 'Please implement the JWT token validation for task 2.3.1' assistant: 'I'll launch the task-executor agent to implement the JWT token validation subtask.' <commentary>The user is asking for specific implementation work on a known task, so the task-executor is appropriate.</commentary></example> <example>Context: After reviewing the task list, implementation is needed. user: 'Now let's actually build the API endpoint for user registration' assistant: 'I'll use the task-executor agent to implement the user registration API endpoint.' <commentary>Moving from planning to execution phase requires the task-executor agent.</commentary></example>
model: sonnet
color: blue
---
You are an elite implementation specialist focused on executing and completing specific tasks with precision and thoroughness. Your role is to take identified tasks and transform them into working implementations, following best practices and project standards.
**Core Responsibilities:**
1. **Task Analysis**: When given a task, first retrieve its full details using `task-master show <id>` to understand requirements, dependencies, and acceptance criteria.
2. **Implementation Planning**: Before coding, briefly outline your implementation approach:
- Identify files that need to be created or modified
- Note any dependencies or prerequisites
- Consider the testing strategy defined in the task
3. **Focused Execution**:
- Implement one subtask at a time for clarity and traceability
- Follow the project's coding standards from CLAUDE.md if available
- Prefer editing existing files over creating new ones
- Only create files that are essential for the task completion
4. **Progress Documentation**:
- Use `task-master update-subtask --id=<id> --prompt="implementation notes"` to log your approach and any important decisions
- Update task status to 'in-progress' when starting: `task-master set-status --id=<id> --status=in-progress`
- Mark as 'done' only after verification: `task-master set-status --id=<id> --status=done`
5. **Quality Assurance**:
- Implement the testing strategy specified in the task
- Verify that all acceptance criteria are met
- Check for any dependency conflicts or integration issues
- Run relevant tests before marking task as complete
6. **Dependency Management**:
- Check task dependencies before starting implementation
- If blocked by incomplete dependencies, clearly communicate this
- Use `task-master validate-dependencies` when needed
**Implementation Workflow:**
1. Retrieve task details and understand requirements
2. Check dependencies and prerequisites
3. Plan implementation approach
4. Update task status to in-progress
5. Implement the solution incrementally
6. Log progress and decisions in subtask updates
7. Test and verify the implementation
8. Mark task as done when complete
9. Suggest next task if appropriate
**Key Principles:**
- Focus on completing one task thoroughly before moving to the next
- Maintain clear communication about what you're implementing and why
- Follow existing code patterns and project conventions
- Prioritize working code over extensive documentation unless docs are the task
- Ask for clarification if task requirements are ambiguous
- Consider edge cases and error handling in your implementations
**Integration with Task Master:**
You work in tandem with the task-orchestrator agent. While the orchestrator identifies and plans tasks, you execute them. Always use Task Master commands to:
- Track your progress
- Update task information
- Maintain project state
- Coordinate with the broader development workflow
When you complete a task, briefly summarize what was implemented and suggest whether to continue with the next task or if review/testing is needed first.

View File

@@ -0,0 +1,130 @@
---
name: task-orchestrator
description: Use this agent when you need to coordinate and manage the execution of Task Master tasks, especially when dealing with complex task dependencies and parallel execution opportunities. This agent should be invoked at the beginning of a work session to analyze the task queue, identify parallelizable work, and orchestrate the deployment of task-executor agents. It should also be used when tasks complete to reassess the dependency graph and deploy new executors as needed.\n\n<example>\nContext: User wants to start working on their project tasks using Task Master\nuser: "Let's work on the next available tasks in the project"\nassistant: "I'll use the task-orchestrator agent to analyze the task queue and coordinate execution"\n<commentary>\nThe user wants to work on tasks, so the task-orchestrator should be deployed to analyze dependencies and coordinate execution.\n</commentary>\n</example>\n\n<example>\nContext: Multiple independent tasks are available in the queue\nuser: "Can we work on multiple tasks at once?"\nassistant: "Let me deploy the task-orchestrator to analyze task dependencies and parallelize the work"\n<commentary>\nWhen parallelization is mentioned or multiple tasks could be worked on, the orchestrator should coordinate the effort.\n</commentary>\n</example>\n\n<example>\nContext: A complex feature with many subtasks needs implementation\nuser: "Implement the authentication system tasks"\nassistant: "I'll use the task-orchestrator to break down the authentication tasks and coordinate their execution"\n<commentary>\nFor complex multi-task features, the orchestrator manages the overall execution strategy.\n</commentary>\n</example>
model: opus
color: green
---
You are the Task Orchestrator, an elite coordination agent specialized in managing Task Master workflows for maximum efficiency and parallelization. You excel at analyzing task dependency graphs, identifying opportunities for concurrent execution, and deploying specialized task-executor agents to complete work efficiently.
## Core Responsibilities
1. **Task Queue Analysis**: You continuously monitor and analyze the task queue using Task Master MCP tools to understand the current state of work, dependencies, and priorities.
2. **Dependency Graph Management**: You build and maintain a mental model of task dependencies, identifying which tasks can be executed in parallel and which must wait for prerequisites.
3. **Executor Deployment**: You strategically deploy task-executor agents for individual tasks or task groups, ensuring each executor has the necessary context and clear success criteria.
4. **Progress Coordination**: You track the progress of deployed executors, handle task completion notifications, and reassess the execution strategy as tasks complete.
## Operational Workflow
### Initial Assessment Phase
1. Use `get_tasks` or `task-master list` to retrieve all available tasks
2. Analyze task statuses, priorities, and dependencies
3. Identify tasks with status 'pending' that have no blocking dependencies
4. Group related tasks that could benefit from specialized executors
5. Create an execution plan that maximizes parallelization
### Executor Deployment Phase
1. For each independent task or task group:
- Deploy a task-executor agent with specific instructions
- Provide the executor with task ID, requirements, and context
- Set clear completion criteria and reporting expectations
2. Maintain a registry of active executors and their assigned tasks
3. Establish communication protocols for progress updates
### Coordination Phase
1. Monitor executor progress through task status updates
2. When a task completes:
- Verify completion with `get_task` or `task-master show <id>`
- Update task status if needed using `set_task_status`
- Reassess dependency graph for newly unblocked tasks
- Deploy new executors for available work
3. Handle executor failures or blocks:
- Reassign tasks to new executors if needed
- Escalate complex issues to the user
- Update task status to 'blocked' when appropriate
### Optimization Strategies
**Parallel Execution Rules**:
- Never assign dependent tasks to different executors simultaneously
- Prioritize high-priority tasks when resources are limited
- Group small, related subtasks for single executor efficiency
- Balance executor load to prevent bottlenecks
**Context Management**:
- Provide executors with minimal but sufficient context
- Share relevant completed task information when it aids execution
- Maintain a shared knowledge base of project-specific patterns
**Quality Assurance**:
- Verify task completion before marking as done
- Ensure test strategies are followed when specified
- Coordinate cross-task integration testing when needed
## Communication Protocols
When deploying executors, provide them with:
```
TASK ASSIGNMENT:
- Task ID: [specific ID]
- Objective: [clear goal]
- Dependencies: [list any completed prerequisites]
- Success Criteria: [specific completion requirements]
- Context: [relevant project information]
- Reporting: [when and how to report back]
```
When receiving executor updates:
1. Acknowledge completion or issues
2. Update task status in Task Master
3. Reassess execution strategy
4. Deploy new executors as appropriate
## Decision Framework
**When to parallelize**:
- Multiple pending tasks with no interdependencies
- Sufficient context available for independent execution
- Tasks are well-defined with clear success criteria
**When to serialize**:
- Strong dependencies between tasks
- Limited context or unclear requirements
- Integration points requiring careful coordination
**When to escalate**:
- Circular dependencies detected
- Critical blockers affecting multiple tasks
- Ambiguous requirements needing clarification
- Resource conflicts between executors
## Error Handling
1. **Executor Failure**: Reassign task to new executor with additional context about the failure
2. **Dependency Conflicts**: Halt affected executors, resolve conflict, then resume
3. **Task Ambiguity**: Request clarification from user before proceeding
4. **System Errors**: Implement graceful degradation, falling back to serial execution if needed
## Performance Metrics
Track and optimize for:
- Task completion rate
- Parallel execution efficiency
- Executor success rate
- Time to completion for task groups
- Dependency resolution speed
## Integration with Task Master
Leverage these Task Master MCP tools effectively:
- `get_tasks` - Continuous queue monitoring
- `get_task` - Detailed task analysis
- `set_task_status` - Progress tracking
- `next_task` - Fallback for serial execution
- `analyze_project_complexity` - Strategic planning
- `complexity_report` - Resource allocation
You are the strategic mind coordinating the entire task execution effort. Your success is measured by the efficient completion of all tasks while maintaining quality and respecting dependencies. Think systematically, act decisively, and continuously optimize the execution strategy based on real-time progress.

View File

@@ -1,4 +1,4 @@
# Available Models as of July 23, 2025
# Available Models as of August 8, 2025
## Main Models
@@ -24,6 +24,7 @@
| openai | gpt-4-1-mini | — | 0.4 | 1.6 |
| openai | gpt-4-1-nano | — | 0.1 | 0.4 |
| openai | gpt-4o-mini | 0.3 | 0.15 | 0.6 |
| openai | gpt-5 | 0.749 | 5 | 20 |
| 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 | — | — |
@@ -134,6 +135,7 @@
| openai | gpt-4o | 0.332 | 2.5 | 10 |
| openai | o3 | 0.5 | 2 | 8 |
| openai | o4-mini | 0.45 | 1.1 | 4.4 |
| openai | gpt-5 | 0.749 | 5 | 20 |
| 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 | — | — |

View File

@@ -0,0 +1,124 @@
/**
* scope-down.js
* Direct function implementation for scoping down task complexity
*/
import { scopeDownTask } from '../../../../scripts/modules/task-manager.js';
import {
enableSilentMode,
disableSilentMode
} from '../../../../scripts/modules/utils.js';
import { createLogWrapper } from '../../tools/utils.js';
/**
* Direct function wrapper for scoping down task complexity with error handling.
*
* @param {Object} args - Command arguments
* @param {string} args.id - Comma-separated list of task IDs to scope down
* @param {string} [args.strength='regular'] - Strength level (light, regular, heavy)
* @param {string} [args.prompt] - Custom prompt for scoping adjustments
* @param {string} [args.tasksJsonPath] - Path to the tasks.json file (resolved by tool)
* @param {boolean} [args.research=false] - Whether to use research capabilities for scoping
* @param {string} args.projectRoot - Project root path
* @param {string} [args.tag] - Tag for the task context (optional)
* @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 scopeDownDirect(args, log, context = {}) {
// Destructure expected args
const {
tasksJsonPath,
id,
strength = 'regular',
prompt: customPrompt,
research = false,
projectRoot,
tag
} = args;
const { session } = context; // Destructure session from context
// Enable silent mode to prevent console logs from interfering with JSON response
enableSilentMode();
// Create logger wrapper using the utility
const mcpLog = createLogWrapper(log);
try {
// Check if tasksJsonPath was provided
if (!tasksJsonPath) {
log.error('scopeDownDirect called without tasksJsonPath');
disableSilentMode(); // Disable before returning
return {
success: false,
error: {
code: 'MISSING_ARGUMENT',
message: 'tasksJsonPath is required'
}
};
}
// Check required parameters
if (!id) {
log.error('Missing required parameter: id');
disableSilentMode();
return {
success: false,
error: {
code: 'MISSING_PARAMETER',
message: 'The id parameter is required for scoping down tasks'
}
};
}
// Parse task IDs - convert to numbers as expected by scopeDownTask
const taskIds = id.split(',').map((taskId) => parseInt(taskId.trim(), 10));
log.info(
`Scoping down tasks: ${taskIds.join(', ')}, strength: ${strength}, research: ${research}`
);
// Call the scopeDownTask function
const result = await scopeDownTask(
tasksJsonPath,
taskIds,
strength,
customPrompt,
{
session,
mcpLog,
projectRoot,
commandName: 'scope-down',
outputType: 'mcp',
tag,
research
},
'json' // outputFormat
);
// Restore normal logging
disableSilentMode();
return {
success: true,
data: {
updatedTasks: result.updatedTasks,
tasksUpdated: result.updatedTasks.length,
message: `Successfully scoped down ${result.updatedTasks.length} task(s)`,
telemetryData: result.telemetryData
}
};
} catch (error) {
// Make sure to restore normal logging even if there's an error
disableSilentMode();
log.error(`Error in scopeDownDirect: ${error.message}`);
return {
success: false,
error: {
code: error.code || 'SCOPE_DOWN_ERROR',
message: error.message
}
};
}
}

View File

@@ -0,0 +1,124 @@
/**
* scope-up.js
* Direct function implementation for scoping up task complexity
*/
import { scopeUpTask } from '../../../../scripts/modules/task-manager.js';
import {
enableSilentMode,
disableSilentMode
} from '../../../../scripts/modules/utils.js';
import { createLogWrapper } from '../../tools/utils.js';
/**
* Direct function wrapper for scoping up task complexity with error handling.
*
* @param {Object} args - Command arguments
* @param {string} args.id - Comma-separated list of task IDs to scope up
* @param {string} [args.strength='regular'] - Strength level (light, regular, heavy)
* @param {string} [args.prompt] - Custom prompt for scoping adjustments
* @param {string} [args.tasksJsonPath] - Path to the tasks.json file (resolved by tool)
* @param {boolean} [args.research=false] - Whether to use research capabilities for scoping
* @param {string} args.projectRoot - Project root path
* @param {string} [args.tag] - Tag for the task context (optional)
* @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 scopeUpDirect(args, log, context = {}) {
// Destructure expected args
const {
tasksJsonPath,
id,
strength = 'regular',
prompt: customPrompt,
research = false,
projectRoot,
tag
} = args;
const { session } = context; // Destructure session from context
// Enable silent mode to prevent console logs from interfering with JSON response
enableSilentMode();
// Create logger wrapper using the utility
const mcpLog = createLogWrapper(log);
try {
// Check if tasksJsonPath was provided
if (!tasksJsonPath) {
log.error('scopeUpDirect called without tasksJsonPath');
disableSilentMode(); // Disable before returning
return {
success: false,
error: {
code: 'MISSING_ARGUMENT',
message: 'tasksJsonPath is required'
}
};
}
// Check required parameters
if (!id) {
log.error('Missing required parameter: id');
disableSilentMode();
return {
success: false,
error: {
code: 'MISSING_PARAMETER',
message: 'The id parameter is required for scoping up tasks'
}
};
}
// Parse task IDs - convert to numbers as expected by scopeUpTask
const taskIds = id.split(',').map((taskId) => parseInt(taskId.trim(), 10));
log.info(
`Scoping up tasks: ${taskIds.join(', ')}, strength: ${strength}, research: ${research}`
);
// Call the scopeUpTask function
const result = await scopeUpTask(
tasksJsonPath,
taskIds,
strength,
customPrompt,
{
session,
mcpLog,
projectRoot,
commandName: 'scope-up',
outputType: 'mcp',
tag,
research
},
'json' // outputFormat
);
// Restore normal logging
disableSilentMode();
return {
success: true,
data: {
updatedTasks: result.updatedTasks,
tasksUpdated: result.updatedTasks.length,
message: `Successfully scoped up ${result.updatedTasks.length} task(s)`,
telemetryData: result.telemetryData
}
};
} catch (error) {
// Make sure to restore normal logging even if there's an error
disableSilentMode();
log.error(`Error in scopeUpDirect: ${error.message}`);
return {
success: false,
error: {
code: error.code || 'SCOPE_UP_ERROR',
message: error.message
}
};
}
}

View File

@@ -38,6 +38,8 @@ import { listTagsDirect } from './direct-functions/list-tags.js';
import { useTagDirect } from './direct-functions/use-tag.js';
import { renameTagDirect } from './direct-functions/rename-tag.js';
import { copyTagDirect } from './direct-functions/copy-tag.js';
import { scopeUpDirect } from './direct-functions/scope-up.js';
import { scopeDownDirect } from './direct-functions/scope-down.js';
// Re-export utility functions
export { findTasksPath } from './utils/path-utils.js';
@@ -76,7 +78,9 @@ export const directFunctions = new Map([
['listTagsDirect', listTagsDirect],
['useTagDirect', useTagDirect],
['renameTagDirect', renameTagDirect],
['copyTagDirect', copyTagDirect]
['copyTagDirect', copyTagDirect],
['scopeUpDirect', scopeUpDirect],
['scopeDownDirect', scopeDownDirect]
]);
// Re-export all direct function implementations
@@ -113,5 +117,7 @@ export {
listTagsDirect,
useTagDirect,
renameTagDirect,
copyTagDirect
copyTagDirect,
scopeUpDirect,
scopeDownDirect
};

View File

@@ -38,6 +38,8 @@ import { registerRenameTagTool } from './rename-tag.js';
import { registerCopyTagTool } from './copy-tag.js';
import { registerResearchTool } from './research.js';
import { registerRulesTool } from './rules.js';
import { registerScopeUpTool } from './scope-up.js';
import { registerScopeDownTool } from './scope-down.js';
/**
* Register all Task Master tools with the MCP server
@@ -57,6 +59,8 @@ export function registerTaskMasterTools(server) {
registerAnalyzeProjectComplexityTool(server);
registerExpandTaskTool(server);
registerExpandAllTool(server);
registerScopeUpTool(server);
registerScopeDownTool(server);
// Group 3: Task Listing & Viewing
registerListTasksTool(server);

View File

@@ -0,0 +1,104 @@
/**
* tools/scope-down.js
* Tool to scope down task complexity
*/
import { z } from 'zod';
import {
createErrorResponse,
handleApiResult,
withNormalizedProjectRoot
} from './utils.js';
import { scopeDownDirect } from '../core/task-master-core.js';
import { findTasksPath } from '../core/utils/path-utils.js';
import { resolveTag } from '../../../scripts/modules/utils.js';
/**
* Register the scopeDown tool with the MCP server
* @param {Object} server - FastMCP server instance
*/
export function registerScopeDownTool(server) {
server.addTool({
name: 'scope_down_task',
description: 'Decrease the complexity of one or more tasks using AI',
parameters: z.object({
id: z
.string()
.describe(
'Comma-separated list of task IDs to scope down (e.g., "1,3,5")'
),
strength: z
.string()
.optional()
.describe(
'Strength level: light, regular, or heavy (default: regular)'
),
prompt: z
.string()
.optional()
.describe('Custom prompt for specific scoping adjustments'),
file: z
.string()
.optional()
.describe('Path to the tasks file (default: tasks/tasks.json)'),
projectRoot: z
.string()
.describe('The directory of the project. Must be an absolute path.'),
tag: z.string().optional().describe('Tag context to operate on'),
research: z
.boolean()
.optional()
.describe('Whether to use research capabilities for scoping')
}),
execute: withNormalizedProjectRoot(async (args, { log, session }) => {
try {
log.info(`Starting scope-down with args: ${JSON.stringify(args)}`);
const resolvedTag = resolveTag({
projectRoot: args.projectRoot,
tag: args.tag
});
// Use args.projectRoot directly (guaranteed by withNormalizedProjectRoot)
let tasksJsonPath;
try {
tasksJsonPath = findTasksPath(
{ projectRoot: args.projectRoot, file: args.file },
log
);
} catch (error) {
log.error(`Error finding tasks.json: ${error.message}`);
return createErrorResponse(
`Failed to find tasks.json: ${error.message}`
);
}
// Call the direct function
const result = await scopeDownDirect(
{
tasksJsonPath: tasksJsonPath,
id: args.id,
strength: args.strength,
prompt: args.prompt,
research: args.research,
projectRoot: args.projectRoot,
tag: resolvedTag
},
log,
{ session }
);
return handleApiResult(
result,
log,
'Error scoping down task',
undefined,
args.projectRoot
);
} catch (error) {
log.error(`Error in scope-down tool: ${error.message}`);
return createErrorResponse(error.message);
}
})
});
}

View File

@@ -0,0 +1,104 @@
/**
* tools/scope-up.js
* Tool to scope up task complexity
*/
import { z } from 'zod';
import {
createErrorResponse,
handleApiResult,
withNormalizedProjectRoot
} from './utils.js';
import { scopeUpDirect } from '../core/task-master-core.js';
import { findTasksPath } from '../core/utils/path-utils.js';
import { resolveTag } from '../../../scripts/modules/utils.js';
/**
* Register the scopeUp tool with the MCP server
* @param {Object} server - FastMCP server instance
*/
export function registerScopeUpTool(server) {
server.addTool({
name: 'scope_up_task',
description: 'Increase the complexity of one or more tasks using AI',
parameters: z.object({
id: z
.string()
.describe(
'Comma-separated list of task IDs to scope up (e.g., "1,3,5")'
),
strength: z
.string()
.optional()
.describe(
'Strength level: light, regular, or heavy (default: regular)'
),
prompt: z
.string()
.optional()
.describe('Custom prompt for specific scoping adjustments'),
file: z
.string()
.optional()
.describe('Path to the tasks file (default: tasks/tasks.json)'),
projectRoot: z
.string()
.describe('The directory of the project. Must be an absolute path.'),
tag: z.string().optional().describe('Tag context to operate on'),
research: z
.boolean()
.optional()
.describe('Whether to use research capabilities for scoping')
}),
execute: withNormalizedProjectRoot(async (args, { log, session }) => {
try {
log.info(`Starting scope-up with args: ${JSON.stringify(args)}`);
const resolvedTag = resolveTag({
projectRoot: args.projectRoot,
tag: args.tag
});
// Use args.projectRoot directly (guaranteed by withNormalizedProjectRoot)
let tasksJsonPath;
try {
tasksJsonPath = findTasksPath(
{ projectRoot: args.projectRoot, file: args.file },
log
);
} catch (error) {
log.error(`Error finding tasks.json: ${error.message}`);
return createErrorResponse(
`Failed to find tasks.json: ${error.message}`
);
}
// Call the direct function
const result = await scopeUpDirect(
{
tasksJsonPath: tasksJsonPath,
id: args.id,
strength: args.strength,
prompt: args.prompt,
research: args.research,
projectRoot: args.projectRoot,
tag: resolvedTag
},
log,
{ session }
);
return handleApiResult(
result,
log,
'Error scoping up task',
undefined,
args.projectRoot
);
} catch (error) {
log.error(`Error in scope-up tool: ${error.message}`);
return createErrorResponse(error.message);
}
})
});
}

1501
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "task-master-ai",
"version": "0.22.0",
"version": "0.24.0",
"description": "A task management system for ambitious AI-driven development that doesn't overwhelm and confuse Cursor.",
"main": "index.js",
"type": "module",
@@ -9,7 +9,10 @@
"task-master-mcp": "mcp-server/server.js",
"task-master-ai": "mcp-server/server.js"
},
"workspaces": ["apps/*", "."],
"workspaces": [
"apps/*",
"."
],
"scripts": {
"test": "node --experimental-vm-modules node_modules/.bin/jest",
"test:fails": "node --experimental-vm-modules node_modules/.bin/jest --onlyFailures",
@@ -23,9 +26,7 @@
"inspector": "npx @modelcontextprotocol/inspector node mcp-server/server.js",
"mcp-server": "node mcp-server/server.js",
"format-check": "biome format .",
"format": "biome format . --write",
"lint": "biome check .",
"lint:fix": "biome check . --write"
"format": "biome format . --write"
},
"keywords": [
"claude",
@@ -75,6 +76,7 @@
"helmet": "^8.1.0",
"inquirer": "^12.5.0",
"jsonc-parser": "^3.3.1",
"jsonrepair": "^3.13.0",
"jsonwebtoken": "^9.0.2",
"lru-cache": "^10.2.0",
"ollama-ai-provider": "^1.2.0",
@@ -125,8 +127,6 @@
"jest-environment-node": "^29.7.0",
"mock-fs": "^5.5.0",
"prettier": "^3.5.3",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"supertest": "^7.1.0",
"tsx": "^4.16.2"
}

View File

@@ -42,7 +42,10 @@ import {
taskExists,
moveTask,
migrateProject,
setResponseLanguage
setResponseLanguage,
scopeUpTask,
scopeDownTask,
validateStrength
} from './task-manager.js';
import {
@@ -1386,6 +1389,260 @@ function registerCommands(programInstance) {
}
});
// scope-up command
programInstance
.command('scope-up')
.description('Increase task complexity with AI assistance')
.option(
'-f, --file <file>',
'Path to the tasks file',
TASKMASTER_TASKS_FILE
)
.option(
'-i, --id <ids>',
'Comma-separated task/subtask IDs to scope up (required)'
)
.option(
'-s, --strength <level>',
'Complexity increase strength: light, regular, heavy',
'regular'
)
.option(
'-p, --prompt <text>',
'Custom instructions for targeted scope adjustments'
)
.option('-r, --research', 'Use research AI for more informed adjustments')
.option('--tag <tag>', 'Specify tag context for task operations')
.action(async (options) => {
try {
// Initialize TaskMaster
const taskMaster = initTaskMaster({
tasksPath: options.file || true,
tag: options.tag
});
const tasksPath = taskMaster.getTasksPath();
const tag = taskMaster.getCurrentTag();
// Show current tag context
displayCurrentTagIndicator(tag);
// Validate required parameters
if (!options.id) {
console.error(chalk.red('Error: --id parameter is required'));
console.log(
chalk.yellow(
'Usage example: task-master scope-up --id=1,2,3 --strength=regular'
)
);
process.exit(1);
}
// Parse and validate task IDs
const taskIds = options.id.split(',').map((id) => {
const parsed = parseInt(id.trim(), 10);
if (Number.isNaN(parsed) || parsed <= 0) {
console.error(chalk.red(`Error: Invalid task ID: ${id.trim()}`));
process.exit(1);
}
return parsed;
});
// Validate strength level
if (!validateStrength(options.strength)) {
console.error(
chalk.red(
`Error: Invalid strength level: ${options.strength}. Must be one of: light, regular, heavy`
)
);
process.exit(1);
}
// Validate tasks file exists
if (!fs.existsSync(tasksPath)) {
console.error(
chalk.red(`Error: Tasks file not found at path: ${tasksPath}`)
);
process.exit(1);
}
console.log(
chalk.blue(
`Scoping up ${taskIds.length} task(s): ${taskIds.join(', ')}`
)
);
console.log(chalk.blue(`Strength level: ${options.strength}`));
if (options.prompt) {
console.log(chalk.blue(`Custom instructions: ${options.prompt}`));
}
const context = {
projectRoot: taskMaster.getProjectRoot(),
tag,
commandName: 'scope-up',
outputType: 'cli',
research: options.research || false
};
const result = await scopeUpTask(
tasksPath,
taskIds,
options.strength,
options.prompt || null,
context,
'text'
);
console.log(
chalk.green(
`✅ Successfully scoped up ${result.updatedTasks.length} task(s)`
)
);
} catch (error) {
console.error(chalk.red(`Error: ${error.message}`));
if (error.message.includes('not found')) {
console.log(chalk.yellow('\nTo fix this issue:'));
console.log(
' 1. Run task-master list to see all available task IDs'
);
console.log(' 2. Use valid task IDs with the --id parameter');
}
if (getDebugFlag()) {
console.error(error);
}
process.exit(1);
}
});
// scope-down command
programInstance
.command('scope-down')
.description('Decrease task complexity with AI assistance')
.option(
'-f, --file <file>',
'Path to the tasks file',
TASKMASTER_TASKS_FILE
)
.option(
'-i, --id <ids>',
'Comma-separated task/subtask IDs to scope down (required)'
)
.option(
'-s, --strength <level>',
'Complexity decrease strength: light, regular, heavy',
'regular'
)
.option(
'-p, --prompt <text>',
'Custom instructions for targeted scope adjustments'
)
.option('-r, --research', 'Use research AI for more informed adjustments')
.option('--tag <tag>', 'Specify tag context for task operations')
.action(async (options) => {
try {
// Initialize TaskMaster
const taskMaster = initTaskMaster({
tasksPath: options.file || true,
tag: options.tag
});
const tasksPath = taskMaster.getTasksPath();
const tag = taskMaster.getCurrentTag();
// Show current tag context
displayCurrentTagIndicator(tag);
// Validate required parameters
if (!options.id) {
console.error(chalk.red('Error: --id parameter is required'));
console.log(
chalk.yellow(
'Usage example: task-master scope-down --id=1,2,3 --strength=regular'
)
);
process.exit(1);
}
// Parse and validate task IDs
const taskIds = options.id.split(',').map((id) => {
const parsed = parseInt(id.trim(), 10);
if (Number.isNaN(parsed) || parsed <= 0) {
console.error(chalk.red(`Error: Invalid task ID: ${id.trim()}`));
process.exit(1);
}
return parsed;
});
// Validate strength level
if (!validateStrength(options.strength)) {
console.error(
chalk.red(
`Error: Invalid strength level: ${options.strength}. Must be one of: light, regular, heavy`
)
);
process.exit(1);
}
// Validate tasks file exists
if (!fs.existsSync(tasksPath)) {
console.error(
chalk.red(`Error: Tasks file not found at path: ${tasksPath}`)
);
process.exit(1);
}
console.log(
chalk.blue(
`Scoping down ${taskIds.length} task(s): ${taskIds.join(', ')}`
)
);
console.log(chalk.blue(`Strength level: ${options.strength}`));
if (options.prompt) {
console.log(chalk.blue(`Custom instructions: ${options.prompt}`));
}
const context = {
projectRoot: taskMaster.getProjectRoot(),
tag,
commandName: 'scope-down',
outputType: 'cli',
research: options.research || false
};
const result = await scopeDownTask(
tasksPath,
taskIds,
options.strength,
options.prompt || null,
context,
'text'
);
console.log(
chalk.green(
`✅ Successfully scoped down ${result.updatedTasks.length} task(s)`
)
);
} catch (error) {
console.error(chalk.red(`Error: ${error.message}`));
if (error.message.includes('not found')) {
console.log(chalk.yellow('\nTo fix this issue:'));
console.log(
' 1. Run task-master list to see all available task IDs'
);
console.log(' 2. Use valid task IDs with the --id parameter');
}
if (getDebugFlag()) {
console.error(error);
}
process.exit(1);
}
});
// generate command
programInstance
.command('generate')

View File

@@ -557,6 +557,7 @@ function getParametersForRole(role, explicitRoot = null) {
const providerName = roleConfig.provider;
let effectiveMaxTokens = roleMaxTokens; // Start with the role's default
let effectiveTemperature = roleTemperature; // Start with the role's default
try {
// Find the model definition in MODEL_MAP
@@ -583,6 +584,20 @@ function getParametersForRole(role, explicitRoot = null) {
`No valid model-specific max_tokens override found for ${modelId}. Using role default: ${roleMaxTokens}`
);
}
// Check if a model-specific temperature is defined
if (
modelDefinition &&
typeof modelDefinition.temperature === 'number' &&
modelDefinition.temperature >= 0 &&
modelDefinition.temperature <= 1
) {
effectiveTemperature = modelDefinition.temperature;
log(
'debug',
`Applying model-specific temperature (${modelDefinition.temperature}) for ${modelId}`
);
}
} else {
// Special handling for custom OpenRouter models
if (providerName === CUSTOM_PROVIDERS.OPENROUTER) {
@@ -603,15 +618,16 @@ function getParametersForRole(role, explicitRoot = null) {
} catch (lookupError) {
log(
'warn',
`Error looking up model-specific max_tokens for ${modelId}: ${lookupError.message}. Using role default: ${roleMaxTokens}`
`Error looking up model-specific parameters for ${modelId}: ${lookupError.message}. Using role defaults.`
);
// Fallback to role default on error
// Fallback to role defaults on error
effectiveMaxTokens = roleMaxTokens;
effectiveTemperature = roleTemperature;
}
return {
maxTokens: effectiveMaxTokens,
temperature: roleTemperature
temperature: effectiveTemperature
};
}

View File

@@ -239,6 +239,18 @@
},
"allowed_roles": ["research"],
"supported": true
},
{
"id": "gpt-5",
"swe_score": 0.749,
"cost_per_1m_tokens": {
"input": 5.0,
"output": 20.0
},
"allowed_roles": ["main", "fallback"],
"max_tokens": 100000,
"temperature": 1,
"supported": true
}
],
"google": [

View File

@@ -28,6 +28,11 @@ import moveTask from './task-manager/move-task.js';
import { migrateProject } from './task-manager/migrate.js';
import { performResearch } from './task-manager/research.js';
import { readComplexityReport } from './utils.js';
import {
scopeUpTask,
scopeDownTask,
validateStrength
} from './task-manager/scope-adjustment.js';
// Export task manager functions
export {
@@ -55,5 +60,8 @@ export {
moveTask,
readComplexityReport,
migrateProject,
performResearch
performResearch,
scopeUpTask,
scopeDownTask,
validateStrength
};

View File

@@ -13,12 +13,18 @@ import {
import { generateTextService } from '../ai-services-unified.js';
import { getDebugFlag, getProjectName } from '../config-manager.js';
import {
getDebugFlag,
getProjectName,
getMainProvider,
getResearchProvider
} from '../config-manager.js';
import { getPromptManager } from '../prompt-manager.js';
import {
COMPLEXITY_REPORT_FILE,
LEGACY_TASKS_FILE
} from '../../../src/constants/paths.js';
import { CUSTOM_PROVIDERS } from '../../../src/constants/providers.js';
import { resolveComplexityReportOutputPath } from '../../../src/utils/path-utils.js';
import { ContextGatherer } from '../utils/contextGatherer.js';
import { FuzzyTaskSearch } from '../utils/fuzzyTaskSearch.js';
@@ -408,10 +414,18 @@ async function analyzeTaskComplexity(options, context = {}) {
// Load prompts using PromptManager
const promptManager = getPromptManager();
// Check if Claude Code is being used as the provider
const currentProvider = useResearch
? getResearchProvider(projectRoot)
: getMainProvider(projectRoot);
const isClaudeCode = currentProvider === CUSTOM_PROVIDERS.CLAUDE_CODE;
const promptParams = {
tasks: tasksData.tasks,
gatheredContext: gatheredContext || '',
useResearch: useResearch
useResearch: useResearch,
isClaudeCode: isClaudeCode,
projectRoot: projectRoot || ''
};
const { systemPrompt, userPrompt: prompt } = await promptManager.loadPrompt(

View File

@@ -18,10 +18,16 @@ import {
import { generateTextService } from '../ai-services-unified.js';
import { getDefaultSubtasks, getDebugFlag } from '../config-manager.js';
import {
getDefaultSubtasks,
getDebugFlag,
getMainProvider,
getResearchProvider
} from '../config-manager.js';
import { getPromptManager } from '../prompt-manager.js';
import generateTaskFiles from './generate-task-files.js';
import { COMPLEXITY_REPORT_FILE } from '../../../src/constants/paths.js';
import { CUSTOM_PROVIDERS } from '../../../src/constants/providers.js';
import { ContextGatherer } from '../utils/contextGatherer.js';
import { FuzzyTaskSearch } from '../utils/fuzzyTaskSearch.js';
import { flattenTasksWithSubtasks, findProjectRoot } from '../utils.js';
@@ -451,6 +457,12 @@ async function expandTask(
// Load prompts using PromptManager
const promptManager = getPromptManager();
// Check if Claude Code is being used as the provider
const currentProvider = useResearch
? getResearchProvider(projectRoot)
: getMainProvider(projectRoot);
const isClaudeCode = currentProvider === CUSTOM_PROVIDERS.CLAUDE_CODE;
// Combine all context sources into a single additionalContext parameter
let combinedAdditionalContext = '';
if (additionalContext || complexityReasoningContext) {
@@ -495,7 +507,9 @@ async function expandTask(
complexityReasoningContext: complexityReasoningContext,
gatheredContext: gatheredContextText || '',
useResearch: useResearch,
expansionPrompt: expansionPromptText || undefined
expansionPrompt: expansionPromptText || undefined,
isClaudeCode: isClaudeCode,
projectRoot: projectRoot || ''
};
let variantKey = 'default';
@@ -513,6 +527,18 @@ async function expandTask(
const { systemPrompt, userPrompt: promptContent } =
await promptManager.loadPrompt('expand-task', promptParams, variantKey);
// Debug logging to identify the issue
logger.debug(`Selected variant: ${variantKey}`);
logger.debug(
`Prompt params passed: ${JSON.stringify(promptParams, null, 2)}`
);
logger.debug(
`System prompt (first 500 chars): ${systemPrompt.substring(0, 500)}...`
);
logger.debug(
`User prompt (first 500 chars): ${promptContent.substring(0, 500)}...`
);
// --- End Complexity Report / Prompt Logic ---
// --- AI Subtask Generation using generateTextService ---

View File

@@ -17,20 +17,26 @@ import {
} from '../utils.js';
import { generateObjectService } from '../ai-services-unified.js';
import { getDebugFlag } from '../config-manager.js';
import {
getDebugFlag,
getMainProvider,
getResearchProvider,
getDefaultPriority
} from '../config-manager.js';
import { getPromptManager } from '../prompt-manager.js';
import { displayAiUsageSummary } from '../ui.js';
import { CUSTOM_PROVIDERS } from '../../../src/constants/providers.js';
// Define the Zod schema for a SINGLE task object
const prdSingleTaskSchema = z.object({
id: z.number().int().positive(),
id: z.number(),
title: z.string().min(1),
description: z.string().min(1),
details: z.string().nullable(),
testStrategy: z.string().nullable(),
priority: z.enum(['high', 'medium', 'low']).nullable(),
dependencies: z.array(z.number().int().positive()).nullable(),
status: z.string().nullable()
details: z.string(),
testStrategy: z.string(),
priority: z.enum(['high', 'medium', 'low']),
dependencies: z.array(z.number()),
status: z.string()
});
// Define the Zod schema for the ENTIRE expected AI response object
@@ -174,9 +180,14 @@ async function parsePRD(prdPath, tasksPath, numTasks, options = {}) {
const promptManager = getPromptManager();
// Get defaultTaskPriority from config
const { getDefaultPriority } = await import('../config-manager.js');
const defaultTaskPriority = getDefaultPriority(projectRoot) || 'medium';
// Check if Claude Code is being used as the provider
const currentProvider = research
? getResearchProvider(projectRoot)
: getMainProvider(projectRoot);
const isClaudeCode = currentProvider === CUSTOM_PROVIDERS.CLAUDE_CODE;
const { systemPrompt, userPrompt } = await promptManager.loadPrompt(
'parse-prd',
{
@@ -185,7 +196,9 @@ async function parsePRD(prdPath, tasksPath, numTasks, options = {}) {
nextId,
prdContent,
prdPath,
defaultTaskPriority
defaultTaskPriority,
isClaudeCode,
projectRoot: projectRoot || ''
}
);
@@ -257,10 +270,15 @@ async function parsePRD(prdPath, tasksPath, numTasks, options = {}) {
return {
...task,
id: newId,
status: 'pending',
status: task.status || 'pending',
priority: task.priority || 'medium',
dependencies: Array.isArray(task.dependencies) ? task.dependencies : [],
subtasks: []
subtasks: [],
// Ensure all required fields have values (even if empty strings)
title: task.title || '',
description: task.description || '',
details: task.details || '',
testStrategy: task.testStrategy || ''
};
});

View File

@@ -0,0 +1,845 @@
/**
* scope-adjustment.js
* Core logic for dynamic task complexity adjustment (scope-up and scope-down)
*/
import { z } from 'zod';
import {
log,
readJSON,
writeJSON,
getCurrentTag,
readComplexityReport,
findTaskInComplexityReport
} from '../utils.js';
import {
generateObjectService,
generateTextService
} from '../ai-services-unified.js';
import { findTaskById, taskExists } from '../task-manager.js';
import analyzeTaskComplexity from './analyze-task-complexity.js';
import { findComplexityReportPath } from '../../../src/utils/path-utils.js';
/**
* Valid strength levels for scope adjustments
*/
const VALID_STRENGTHS = ['light', 'regular', 'heavy'];
/**
* Statuses that should be preserved during subtask regeneration
* These represent work that has been started or intentionally set by the user
*/
const PRESERVE_STATUSES = [
'done',
'in-progress',
'review',
'cancelled',
'deferred',
'blocked'
];
/**
* Statuses that should be regenerated during subtask regeneration
* These represent work that hasn't been started yet
*/
const REGENERATE_STATUSES = ['pending'];
/**
* Validates strength parameter
* @param {string} strength - The strength level to validate
* @returns {boolean} True if valid, false otherwise
*/
export function validateStrength(strength) {
return VALID_STRENGTHS.includes(strength);
}
/**
* Re-analyzes the complexity of a single task after scope adjustment
* @param {Object} task - The task to analyze
* @param {string} tasksPath - Path to tasks.json
* @param {Object} context - Context containing projectRoot, tag, session
* @returns {Promise<number|null>} New complexity score or null if analysis failed
*/
async function reanalyzeTaskComplexity(task, tasksPath, context) {
const { projectRoot, tag, session } = context;
try {
// Create a minimal tasks data structure for analysis
const tasksForAnalysis = {
tasks: [task],
metadata: { analyzedAt: new Date().toISOString() }
};
// Find the complexity report path for this tag
const complexityReportPath = findComplexityReportPath(
null,
{ projectRoot, tag },
null
);
if (!complexityReportPath) {
log('warn', 'No complexity report found - cannot re-analyze complexity');
return null;
}
// Use analyze-task-complexity to re-analyze just this task
const analysisOptions = {
file: tasksPath,
output: complexityReportPath,
id: task.id.toString(), // Analyze only this specific task
projectRoot,
tag,
_filteredTasksData: tasksForAnalysis, // Pass pre-filtered data
_originalTaskCount: 1
};
// Run the analysis with proper context
await analyzeTaskComplexity(analysisOptions, { session });
// Read the updated complexity report to get the new score
const updatedReport = readComplexityReport(complexityReportPath);
if (updatedReport) {
const taskAnalysis = findTaskInComplexityReport(updatedReport, task.id);
if (taskAnalysis) {
log(
'info',
`Re-analyzed task ${task.id} complexity: ${taskAnalysis.complexityScore}/10`
);
return taskAnalysis.complexityScore;
}
}
log(
'warn',
`Could not find updated complexity analysis for task ${task.id}`
);
return null;
} catch (error) {
log('error', `Failed to re-analyze task complexity: ${error.message}`);
return null;
}
}
/**
* Gets the current complexity score for a task from the complexity report
* @param {number} taskId - Task ID to look up
* @param {Object} context - Context containing projectRoot, tag
* @returns {number|null} Current complexity score or null if not found
*/
function getCurrentComplexityScore(taskId, context) {
const { projectRoot, tag } = context;
try {
// Find the complexity report path for this tag
const complexityReportPath = findComplexityReportPath(
null,
{ projectRoot, tag },
null
);
if (!complexityReportPath) {
return null;
}
// Read the current complexity report
const complexityReport = readComplexityReport(complexityReportPath);
if (!complexityReport) {
return null;
}
// Find this task's current complexity
const taskAnalysis = findTaskInComplexityReport(complexityReport, taskId);
return taskAnalysis ? taskAnalysis.complexityScore : null;
} catch (error) {
log('debug', `Could not read current complexity score: ${error.message}`);
return null;
}
}
/**
* Regenerates subtasks for a task based on new complexity while preserving completed work
* @param {Object} task - The updated task object
* @param {string} tasksPath - Path to tasks.json
* @param {Object} context - Context containing projectRoot, tag, session
* @param {string} direction - Direction of scope change (up/down) for logging
* @param {string} strength - Strength level ('light', 'regular', 'heavy')
* @param {number|null} originalComplexity - Original complexity score for smarter adjustments
* @returns {Promise<Object>} Object with updated task and regeneration info
*/
async function regenerateSubtasksForComplexity(
task,
tasksPath,
context,
direction,
strength = 'regular',
originalComplexity = null
) {
const { projectRoot, tag, session } = context;
// Check if task has subtasks
if (
!task.subtasks ||
!Array.isArray(task.subtasks) ||
task.subtasks.length === 0
) {
return {
updatedTask: task,
regenerated: false,
preserved: 0,
generated: 0
};
}
// Identify subtasks to preserve vs regenerate
const preservedSubtasks = task.subtasks.filter((subtask) =>
PRESERVE_STATUSES.includes(subtask.status)
);
const pendingSubtasks = task.subtasks.filter((subtask) =>
REGENERATE_STATUSES.includes(subtask.status)
);
// If no pending subtasks, nothing to regenerate
if (pendingSubtasks.length === 0) {
return {
updatedTask: task,
regenerated: false,
preserved: preservedSubtasks.length,
generated: 0
};
}
// Calculate appropriate number of total subtasks based on direction, complexity, strength, and original complexity
let targetSubtaskCount;
const preservedCount = preservedSubtasks.length;
const currentPendingCount = pendingSubtasks.length;
// Use original complexity to inform decisions (if available)
const complexityFactor = originalComplexity
? Math.max(0.5, originalComplexity / 10)
: 1.0;
const complexityInfo = originalComplexity
? ` (original complexity: ${originalComplexity}/10)`
: '';
if (direction === 'up') {
// Scope up: More subtasks for increased complexity
if (strength === 'light') {
const base = Math.max(
5,
preservedCount + Math.ceil(currentPendingCount * 1.1)
);
targetSubtaskCount = Math.ceil(base * (0.8 + 0.4 * complexityFactor));
} else if (strength === 'regular') {
const base = Math.max(
6,
preservedCount + Math.ceil(currentPendingCount * 1.3)
);
targetSubtaskCount = Math.ceil(base * (0.8 + 0.4 * complexityFactor));
} else {
// heavy
const base = Math.max(
8,
preservedCount + Math.ceil(currentPendingCount * 1.6)
);
targetSubtaskCount = Math.ceil(base * (0.8 + 0.6 * complexityFactor));
}
} else {
// Scope down: Fewer subtasks for decreased complexity
// High complexity tasks get reduced more aggressively
const aggressiveFactor =
originalComplexity >= 8 ? 0.7 : originalComplexity >= 6 ? 0.85 : 1.0;
if (strength === 'light') {
const base = Math.max(
3,
preservedCount + Math.ceil(currentPendingCount * 0.8)
);
targetSubtaskCount = Math.ceil(base * aggressiveFactor);
} else if (strength === 'regular') {
const base = Math.max(
3,
preservedCount + Math.ceil(currentPendingCount * 0.5)
);
targetSubtaskCount = Math.ceil(base * aggressiveFactor);
} else {
// heavy
// Heavy scope-down should be much more aggressive - aim for only core functionality
// Very high complexity tasks (9-10) get reduced to almost nothing
const ultraAggressiveFactor =
originalComplexity >= 9 ? 0.3 : originalComplexity >= 7 ? 0.5 : 0.7;
const base = Math.max(
2,
preservedCount + Math.ceil(currentPendingCount * 0.25)
);
targetSubtaskCount = Math.max(1, Math.ceil(base * ultraAggressiveFactor));
}
}
log(
'debug',
`Complexity-aware subtask calculation${complexityInfo}: ${currentPendingCount} pending -> target ${targetSubtaskCount} total`
);
log(
'debug',
`Complexity-aware calculation${complexityInfo}: ${currentPendingCount} pending -> ${targetSubtaskCount} total subtasks (${strength} ${direction})`
);
const newSubtasksNeeded = Math.max(1, targetSubtaskCount - preservedCount);
try {
// Generate new subtasks using AI to match the new complexity level
const systemPrompt = `You are an expert project manager who creates task breakdowns that match complexity levels.`;
const prompt = `Based on this updated task, generate ${newSubtasksNeeded} NEW subtasks that reflect the ${direction === 'up' ? 'increased' : 'decreased'} complexity level:
**Task Title**: ${task.title}
**Task Description**: ${task.description}
**Implementation Details**: ${task.details}
**Test Strategy**: ${task.testStrategy}
**Complexity Direction**: This task was recently scoped ${direction} (${strength} strength) to ${direction === 'up' ? 'increase' : 'decrease'} complexity.
${originalComplexity ? `**Original Complexity**: ${originalComplexity}/10 - consider this when determining appropriate scope level.` : ''}
${preservedCount > 0 ? `**Preserved Subtasks**: ${preservedCount} existing subtasks with work already done will be kept.` : ''}
Generate subtasks that:
${
direction === 'up'
? strength === 'heavy'
? `- Add comprehensive implementation steps with advanced features
- Include extensive error handling, validation, and edge cases
- Cover multiple integration scenarios and advanced testing
- Provide thorough documentation and optimization approaches`
: strength === 'regular'
? `- Add more detailed implementation steps
- Include additional error handling and validation
- Cover more edge cases and advanced features
- Provide more comprehensive testing approaches`
: `- Add some additional implementation details
- Include basic error handling considerations
- Cover a few common edge cases
- Enhance testing approaches slightly`
: strength === 'heavy'
? `- Focus ONLY on absolutely essential core functionality
- Strip out ALL non-critical features (error handling, advanced testing, etc.)
- Provide only the minimum viable implementation
- Eliminate any complex integrations or advanced scenarios
- Aim for the simplest possible working solution`
: strength === 'regular'
? `- Focus on core functionality only
- Simplify implementation steps
- Remove non-essential features
- Streamline to basic requirements`
: `- Focus mainly on core functionality
- Slightly simplify implementation steps
- Remove some non-essential features
- Streamline most requirements`
}
Return a JSON object with a "subtasks" array. Each subtask should have:
- id: Sequential NUMBER starting from 1 (e.g., 1, 2, 3 - NOT "1", "2", "3")
- title: Clear, specific title
- description: Detailed description
- dependencies: Array of dependency IDs as STRINGS (use format ["${task.id}.1", "${task.id}.2"] for siblings, or empty array [] for no dependencies)
- details: Implementation guidance
- status: "pending"
- testStrategy: Testing approach
IMPORTANT:
- The 'id' field must be a NUMBER, not a string!
- Dependencies must be strings, not numbers!
Ensure the JSON is valid and properly formatted.`;
// Define subtask schema
const subtaskSchema = z.object({
subtasks: z.array(
z.object({
id: z.number().int().positive(),
title: z.string().min(5),
description: z.string().min(10),
dependencies: z.array(z.string()),
details: z.string().min(20),
status: z.string(),
testStrategy: z.string()
})
)
});
const aiResult = await generateObjectService({
role: context.research ? 'research' : 'main',
session: context.session,
systemPrompt,
prompt,
schema: subtaskSchema,
objectName: 'subtask_regeneration',
commandName: context.commandName || `subtask-regen-${direction}`,
outputType: context.outputType || 'cli'
});
const generatedSubtasks = aiResult.mainResult.subtasks || [];
// Post-process generated subtasks to ensure defaults
const processedGeneratedSubtasks = generatedSubtasks.map((subtask) => ({
...subtask,
status: subtask.status || 'pending',
testStrategy: subtask.testStrategy || ''
}));
// Update task with preserved subtasks + newly generated ones
task.subtasks = [...preservedSubtasks, ...processedGeneratedSubtasks];
return {
updatedTask: task,
regenerated: true,
preserved: preservedSubtasks.length,
generated: processedGeneratedSubtasks.length
};
} catch (error) {
log(
'warn',
`Failed to regenerate subtasks for task ${task.id}: ${error.message}`
);
// Don't fail the whole operation if subtask regeneration fails
return {
updatedTask: task,
regenerated: false,
preserved: preservedSubtasks.length,
generated: 0,
error: error.message
};
}
}
/**
* Generates AI prompt for scope adjustment
* @param {Object} task - The task to adjust
* @param {string} direction - 'up' or 'down'
* @param {string} strength - 'light', 'regular', or 'heavy'
* @param {string} customPrompt - Optional custom instructions
* @returns {string} The generated prompt
*/
function generateScopePrompt(task, direction, strength, customPrompt) {
const isUp = direction === 'up';
const strengthDescriptions = {
light: isUp ? 'minor enhancements' : 'slight simplifications',
regular: isUp
? 'moderate complexity increases'
: 'moderate simplifications',
heavy: isUp ? 'significant complexity additions' : 'major simplifications'
};
let basePrompt = `You are tasked with adjusting the complexity of a task.
CURRENT TASK:
Title: ${task.title}
Description: ${task.description}
Details: ${task.details}
Test Strategy: ${task.testStrategy || 'Not specified'}
ADJUSTMENT REQUIREMENTS:
- Direction: ${isUp ? 'INCREASE' : 'DECREASE'} complexity
- Strength: ${strength} (${strengthDescriptions[strength]})
- Preserve the core purpose and functionality of the task
- Maintain consistency with the existing task structure`;
if (isUp) {
basePrompt += `
- Add more detailed requirements, edge cases, or advanced features
- Include additional implementation considerations
- Enhance error handling and validation requirements
- Expand testing strategies with more comprehensive scenarios`;
} else {
basePrompt += `
- Focus on core functionality and essential requirements
- Remove or simplify non-essential features
- Streamline implementation details
- Simplify testing to focus on basic functionality`;
}
if (customPrompt) {
basePrompt += `\n\nCUSTOM INSTRUCTIONS:\n${customPrompt}`;
}
basePrompt += `\n\nReturn a JSON object with the updated task containing these fields:
- title: Updated task title
- description: Updated task description
- details: Updated implementation details
- testStrategy: Updated test strategy
- priority: Task priority ('low', 'medium', or 'high')
Ensure the JSON is valid and properly formatted.`;
return basePrompt;
}
/**
* Adjusts task complexity using AI
* @param {Object} task - The task to adjust
* @param {string} direction - 'up' or 'down'
* @param {string} strength - 'light', 'regular', or 'heavy'
* @param {string} customPrompt - Optional custom instructions
* @param {Object} context - Context object with projectRoot, tag, etc.
* @returns {Promise<Object>} Updated task data and telemetry
*/
async function adjustTaskComplexity(
task,
direction,
strength,
customPrompt,
context
) {
const systemPrompt = `You are an expert software project manager who helps adjust task complexity while maintaining clarity and actionability.`;
const prompt = generateScopePrompt(task, direction, strength, customPrompt);
// Define the task schema for structured response using Zod
const taskSchema = z.object({
title: z
.string()
.min(1)
.describe('Updated task title reflecting scope adjustment'),
description: z
.string()
.min(1)
.describe('Updated task description with adjusted scope'),
details: z
.string()
.min(1)
.describe('Updated implementation details with adjusted complexity'),
testStrategy: z
.string()
.min(1)
.describe('Updated testing approach for the adjusted scope'),
priority: z.enum(['low', 'medium', 'high']).describe('Task priority level')
});
const aiResult = await generateObjectService({
role: context.research ? 'research' : 'main',
session: context.session,
systemPrompt,
prompt,
schema: taskSchema,
objectName: 'updated_task',
commandName: context.commandName || `scope-${direction}`,
outputType: context.outputType || 'cli'
});
const updatedTaskData = aiResult.mainResult;
// Ensure priority has a value (in case AI didn't provide one)
const processedTaskData = {
...updatedTaskData,
priority: updatedTaskData.priority || task.priority || 'medium'
};
return {
updatedTask: {
...task,
...processedTaskData
},
telemetryData: aiResult.telemetryData
};
}
/**
* Increases task complexity (scope-up)
* @param {string} tasksPath - Path to tasks.json file
* @param {Array<number>} taskIds - Array of task IDs to scope up
* @param {string} strength - Strength level ('light', 'regular', 'heavy')
* @param {string} customPrompt - Optional custom instructions
* @param {Object} context - Context object with projectRoot, tag, etc.
* @param {string} outputFormat - Output format ('text' or 'json')
* @returns {Promise<Object>} Results of the scope-up operation
*/
export async function scopeUpTask(
tasksPath,
taskIds,
strength = 'regular',
customPrompt = null,
context = {},
outputFormat = 'text'
) {
// Validate inputs
if (!validateStrength(strength)) {
throw new Error(
`Invalid strength level: ${strength}. Must be one of: ${VALID_STRENGTHS.join(', ')}`
);
}
const { projectRoot = '.', tag = 'master' } = context;
// Read tasks data
const data = readJSON(tasksPath, projectRoot, tag);
const tasks = data?.tasks || [];
// Validate all task IDs exist
for (const taskId of taskIds) {
if (!taskExists(tasks, taskId)) {
throw new Error(`Task with ID ${taskId} not found`);
}
}
const updatedTasks = [];
let combinedTelemetryData = null;
// Process each task
for (const taskId of taskIds) {
const taskResult = findTaskById(tasks, taskId);
const task = taskResult.task;
if (!task) {
throw new Error(`Task with ID ${taskId} not found`);
}
if (outputFormat === 'text') {
log('info', `Scoping up task ${taskId}: ${task.title}`);
}
// Get original complexity score (if available)
const originalComplexity = getCurrentComplexityScore(taskId, context);
if (originalComplexity && outputFormat === 'text') {
log('info', `Original complexity: ${originalComplexity}/10`);
}
const adjustResult = await adjustTaskComplexity(
task,
'up',
strength,
customPrompt,
context
);
// Regenerate subtasks based on new complexity while preserving completed work
const subtaskResult = await regenerateSubtasksForComplexity(
adjustResult.updatedTask,
tasksPath,
context,
'up',
strength,
originalComplexity
);
// Log subtask regeneration info if in text mode
if (outputFormat === 'text' && subtaskResult.regenerated) {
log(
'info',
`Regenerated ${subtaskResult.generated} pending subtasks (preserved ${subtaskResult.preserved} completed)`
);
}
// Update task in data
const taskIndex = data.tasks.findIndex((t) => t.id === taskId);
if (taskIndex !== -1) {
data.tasks[taskIndex] = subtaskResult.updatedTask;
updatedTasks.push(subtaskResult.updatedTask);
}
// Re-analyze complexity after scoping (if we have a session for AI calls)
if (context.session && originalComplexity) {
try {
// Write the updated task first so complexity analysis can read it
writeJSON(tasksPath, data, projectRoot, tag);
// Re-analyze complexity
const newComplexity = await reanalyzeTaskComplexity(
subtaskResult.updatedTask,
tasksPath,
context
);
if (newComplexity && outputFormat === 'text') {
const complexityChange = newComplexity - originalComplexity;
const arrow =
complexityChange > 0 ? '↗️' : complexityChange < 0 ? '↘️' : '➡️';
log(
'info',
`New complexity: ${originalComplexity}/10 ${arrow} ${newComplexity}/10 (${complexityChange > 0 ? '+' : ''}${complexityChange})`
);
}
} catch (error) {
if (outputFormat === 'text') {
log('warn', `Could not re-analyze complexity: ${error.message}`);
}
}
}
// Combine telemetry data
if (adjustResult.telemetryData) {
if (!combinedTelemetryData) {
combinedTelemetryData = { ...adjustResult.telemetryData };
} else {
// Sum up costs and tokens
combinedTelemetryData.inputTokens +=
adjustResult.telemetryData.inputTokens || 0;
combinedTelemetryData.outputTokens +=
adjustResult.telemetryData.outputTokens || 0;
combinedTelemetryData.totalTokens +=
adjustResult.telemetryData.totalTokens || 0;
combinedTelemetryData.totalCost +=
adjustResult.telemetryData.totalCost || 0;
}
}
}
// Write updated data
writeJSON(tasksPath, data, projectRoot, tag);
if (outputFormat === 'text') {
log('info', `Successfully scoped up ${updatedTasks.length} task(s)`);
}
return {
updatedTasks,
telemetryData: combinedTelemetryData
};
}
/**
* Decreases task complexity (scope-down)
* @param {string} tasksPath - Path to tasks.json file
* @param {Array<number>} taskIds - Array of task IDs to scope down
* @param {string} strength - Strength level ('light', 'regular', 'heavy')
* @param {string} customPrompt - Optional custom instructions
* @param {Object} context - Context object with projectRoot, tag, etc.
* @param {string} outputFormat - Output format ('text' or 'json')
* @returns {Promise<Object>} Results of the scope-down operation
*/
export async function scopeDownTask(
tasksPath,
taskIds,
strength = 'regular',
customPrompt = null,
context = {},
outputFormat = 'text'
) {
// Validate inputs
if (!validateStrength(strength)) {
throw new Error(
`Invalid strength level: ${strength}. Must be one of: ${VALID_STRENGTHS.join(', ')}`
);
}
const { projectRoot = '.', tag = 'master' } = context;
// Read tasks data
const data = readJSON(tasksPath, projectRoot, tag);
const tasks = data?.tasks || [];
// Validate all task IDs exist
for (const taskId of taskIds) {
if (!taskExists(tasks, taskId)) {
throw new Error(`Task with ID ${taskId} not found`);
}
}
const updatedTasks = [];
let combinedTelemetryData = null;
// Process each task
for (const taskId of taskIds) {
const taskResult = findTaskById(tasks, taskId);
const task = taskResult.task;
if (!task) {
throw new Error(`Task with ID ${taskId} not found`);
}
if (outputFormat === 'text') {
log('info', `Scoping down task ${taskId}: ${task.title}`);
}
// Get original complexity score (if available)
const originalComplexity = getCurrentComplexityScore(taskId, context);
if (originalComplexity && outputFormat === 'text') {
log('info', `Original complexity: ${originalComplexity}/10`);
}
const adjustResult = await adjustTaskComplexity(
task,
'down',
strength,
customPrompt,
context
);
// Regenerate subtasks based on new complexity while preserving completed work
const subtaskResult = await regenerateSubtasksForComplexity(
adjustResult.updatedTask,
tasksPath,
context,
'down',
strength,
originalComplexity
);
// Log subtask regeneration info if in text mode
if (outputFormat === 'text' && subtaskResult.regenerated) {
log(
'info',
`Regenerated ${subtaskResult.generated} pending subtasks (preserved ${subtaskResult.preserved} completed)`
);
}
// Update task in data
const taskIndex = data.tasks.findIndex((t) => t.id === taskId);
if (taskIndex !== -1) {
data.tasks[taskIndex] = subtaskResult.updatedTask;
updatedTasks.push(subtaskResult.updatedTask);
}
// Re-analyze complexity after scoping (if we have a session for AI calls)
if (context.session && originalComplexity) {
try {
// Write the updated task first so complexity analysis can read it
writeJSON(tasksPath, data, projectRoot, tag);
// Re-analyze complexity
const newComplexity = await reanalyzeTaskComplexity(
subtaskResult.updatedTask,
tasksPath,
context
);
if (newComplexity && outputFormat === 'text') {
const complexityChange = newComplexity - originalComplexity;
const arrow =
complexityChange > 0 ? '↗️' : complexityChange < 0 ? '↘️' : '➡️';
log(
'info',
`New complexity: ${originalComplexity}/10 ${arrow} ${newComplexity}/10 (${complexityChange > 0 ? '+' : ''}${complexityChange})`
);
}
} catch (error) {
if (outputFormat === 'text') {
log('warn', `Could not re-analyze complexity: ${error.message}`);
}
}
}
// Combine telemetry data
if (adjustResult.telemetryData) {
if (!combinedTelemetryData) {
combinedTelemetryData = { ...adjustResult.telemetryData };
} else {
// Sum up costs and tokens
combinedTelemetryData.inputTokens +=
adjustResult.telemetryData.inputTokens || 0;
combinedTelemetryData.outputTokens +=
adjustResult.telemetryData.outputTokens || 0;
combinedTelemetryData.totalTokens +=
adjustResult.telemetryData.totalTokens || 0;
combinedTelemetryData.totalCost +=
adjustResult.telemetryData.totalCost || 0;
}
}
}
// Write updated data
writeJSON(tasksPath, data, projectRoot, tag);
if (outputFormat === 'text') {
log('info', `Successfully scoped down ${updatedTasks.length} task(s)`);
}
return {
updatedTasks,
telemetryData: combinedTelemetryData
};
}

View File

@@ -1640,23 +1640,80 @@ async function displayTaskById(
}
// --- Suggested Actions ---
console.log(
boxen(
chalk.white.bold('Suggested Actions:') +
'\n' +
`${chalk.cyan('1.')} Mark as in-progress: ${chalk.yellow(`task-master set-status --id=${task.id} --status=in-progress`)}\n` +
`${chalk.cyan('2.')} Mark as done when completed: ${chalk.yellow(`task-master set-status --id=${task.id} --status=done`)}\n` +
// Determine action 3 based on whether subtasks *exist* (use the source list for progress)
(subtasksForProgress && subtasksForProgress.length > 0
? `${chalk.cyan('3.')} Update subtask status: ${chalk.yellow(`task-master set-status --id=${task.id}.1 --status=done`)}` // Example uses .1
: `${chalk.cyan('3.')} Break down into subtasks: ${chalk.yellow(`task-master expand --id=${task.id}`)}`),
{
padding: { top: 0, bottom: 0, left: 1, right: 1 },
borderColor: 'green',
borderStyle: 'round',
margin: { top: 1 }
const actions = [];
let actionNumber = 1;
// Basic actions
actions.push(
`${chalk.cyan(`${actionNumber}.`)} Mark as in-progress: ${chalk.yellow(`task-master set-status --id=${task.id} --status=in-progress`)}`
);
actionNumber++;
actions.push(
`${chalk.cyan(`${actionNumber}.`)} Mark as done when completed: ${chalk.yellow(`task-master set-status --id=${task.id} --status=done`)}`
);
actionNumber++;
// Subtask-related action
if (subtasksForProgress && subtasksForProgress.length > 0) {
actions.push(
`${chalk.cyan(`${actionNumber}.`)} Update subtask status: ${chalk.yellow(`task-master set-status --id=${task.id}.1 --status=done`)}`
);
} else {
actions.push(
`${chalk.cyan(`${actionNumber}.`)} Break down into subtasks: ${chalk.yellow(`task-master expand --id=${task.id}`)}`
);
}
actionNumber++;
// Complexity-based scope adjustment actions
if (task.complexityScore) {
const complexityScore = task.complexityScore;
actions.push(
`${chalk.cyan(`${actionNumber}.`)} Re-analyze complexity: ${chalk.yellow(`task-master analyze-complexity --id=${task.id}`)}`
);
actionNumber++;
// Add scope adjustment suggestions based on current complexity
if (complexityScore >= 7) {
// High complexity - suggest scoping down
actions.push(
`${chalk.cyan(`${actionNumber}.`)} Scope down (simplify): ${chalk.yellow(`task-master scope-down --id=${task.id} --strength=regular`)}`
);
actionNumber++;
if (complexityScore >= 9) {
actions.push(
`${chalk.cyan(`${actionNumber}.`)} Heavy scope down: ${chalk.yellow(`task-master scope-down --id=${task.id} --strength=heavy`)}`
);
actionNumber++;
}
)
} else if (complexityScore <= 4) {
// Low complexity - suggest scoping up
actions.push(
`${chalk.cyan(`${actionNumber}.`)} Scope up (add detail): ${chalk.yellow(`task-master scope-up --id=${task.id} --strength=regular`)}`
);
actionNumber++;
if (complexityScore <= 2) {
actions.push(
`${chalk.cyan(`${actionNumber}.`)} Heavy scope up: ${chalk.yellow(`task-master scope-up --id=${task.id} --strength=heavy`)}`
);
actionNumber++;
}
} else {
// Medium complexity (5-6) - offer both options
actions.push(
`${chalk.cyan(`${actionNumber}.`)} Scope up/down: ${chalk.yellow(`task-master scope-up --id=${task.id} --strength=light`)} or ${chalk.yellow(`scope-down --id=${task.id} --strength=light`)}`
);
actionNumber++;
}
}
console.log(
boxen(chalk.white.bold('Suggested Actions:') + '\n' + actions.join('\n'), {
padding: { top: 0, bottom: 0, left: 1, right: 1 },
borderColor: 'green',
borderStyle: 'round',
margin: { top: 1 }
})
);
// Show FYI notice if migration occurred

View File

@@ -262,6 +262,43 @@ function hasTaggedStructure(data) {
return false;
}
/**
* Normalizes task IDs to ensure they are numbers instead of strings
* @param {Array} tasks - Array of tasks to normalize
*/
function normalizeTaskIds(tasks) {
if (!Array.isArray(tasks)) return;
tasks.forEach((task) => {
// Convert task ID to number with validation
if (task.id !== undefined) {
const parsedId = parseInt(task.id, 10);
if (!isNaN(parsedId) && parsedId > 0) {
task.id = parsedId;
}
}
// Convert subtask IDs to numbers with validation
if (Array.isArray(task.subtasks)) {
task.subtasks.forEach((subtask) => {
if (subtask.id !== undefined) {
// Check for dot notation (which shouldn't exist in storage)
if (typeof subtask.id === 'string' && subtask.id.includes('.')) {
// Extract the subtask part after the dot
const parts = subtask.id.split('.');
subtask.id = parseInt(parts[parts.length - 1], 10);
} else {
const parsedSubtaskId = parseInt(subtask.id, 10);
if (!isNaN(parsedSubtaskId) && parsedSubtaskId > 0) {
subtask.id = parsedSubtaskId;
}
}
}
});
}
});
}
/**
* Reads and parses a JSON file
* @param {string} filepath - Path to the JSON file
@@ -322,6 +359,8 @@ function readJSON(filepath, projectRoot = null, tag = null) {
console.log(`File is in legacy format, performing migration...`);
}
normalizeTaskIds(data.tasks);
// This is legacy format - migrate it to tagged format
const migratedData = {
master: {
@@ -401,6 +440,16 @@ function readJSON(filepath, projectRoot = null, tag = null) {
// Store reference to the raw tagged data for functions that need it
const originalTaggedData = JSON.parse(JSON.stringify(data));
// Normalize IDs in all tags before storing as originalTaggedData
for (const tagName in originalTaggedData) {
if (
originalTaggedData[tagName] &&
Array.isArray(originalTaggedData[tagName].tasks)
) {
normalizeTaskIds(originalTaggedData[tagName].tasks);
}
}
// Check and auto-switch git tags if enabled (for existing tagged format)
// This needs to run synchronously BEFORE tag resolution
if (projectRoot) {
@@ -448,6 +497,8 @@ function readJSON(filepath, projectRoot = null, tag = null) {
// Get the data for the resolved tag
const tagData = data[resolvedTag];
if (tagData && tagData.tasks) {
normalizeTaskIds(tagData.tasks);
// Add the _rawTaggedData property and the resolved tag to the returned data
const result = {
...tagData,
@@ -464,6 +515,8 @@ function readJSON(filepath, projectRoot = null, tag = null) {
// If the resolved tag doesn't exist, fall back to master
const masterData = data.master;
if (masterData && masterData.tasks) {
normalizeTaskIds(masterData.tasks);
if (isDebug) {
console.log(
`Tag '${resolvedTag}' not found, falling back to master with ${masterData.tasks.length} tasks`
@@ -493,6 +546,7 @@ function readJSON(filepath, projectRoot = null, tag = null) {
// If anything goes wrong, try to return master or empty
const masterData = data.master;
if (masterData && masterData.tasks) {
normalizeTaskIds(masterData.tasks);
return {
...masterData,
_rawTaggedData: originalTaggedData
@@ -1412,5 +1466,6 @@ export {
createStateJson,
markMigrationForNotice,
flattenTasksWithSubtasks,
ensureTagMetadata
ensureTagMetadata,
normalizeTaskIds
};

View File

@@ -1,91 +0,0 @@
#!/bin/bash
set -e
echo "🚀 Starting release process..."
# Ensure we're in the project root
cd "$(dirname "$0")/.."
echo "📦 Building and packaging extension..."
# Navigate to extension directory and build
cd apps/extension
# Install dependencies (in case they're not cached)
echo "📥 Installing extension dependencies..."
pnpm install --frozen-lockfile
# Run quality checks first (same as CI)
echo "🔍 Running lint checks..."
pnpm run lint
echo "🔍 Running type checks..."
pnpm run check-types
# Build the extension
echo "🔨 Building extension..."
pnpm run build
# Create clean package
echo "📦 Creating clean package..."
pnpm run package
# Verify package contents (same as CI)
echo "🔍 Verifying package contents..."
echo "Checking vsix-build contents..."
ls -la vsix-build/
echo "Checking dist contents..."
ls -la vsix-build/dist/
echo "Checking package.json exists..."
test -f vsix-build/package.json
# Create VSIX package
echo "📦 Creating VSIX package..."
cd vsix-build
pnpm exec vsce package --no-dependencies --out "$PWD"
# Run tests before publishing
echo "🧪 Running extension tests..."
cd ..
# Note: Tests run with xvfb-run in CI, but in release context we'll skip or handle differently
echo "⚠️ Skipping tests in release context (run in CI validation)"
# Go back to project root for tagging and changeset operations
cd ../..
echo "🏷️ Checking extension tag..."
node scripts/tag-extension.mjs
echo "📝 Publishing packages with changesets..."
# Let changesets handle all the npm publishing first
npx changeset publish
echo "🌐 Publishing extension to VS Code Marketplace..."
# Find the generated VSIX file
VSIX_FILE=$(find apps/extension/vsix-build -name "*.vsix" | head -n 1)
if [ -n "$VSIX_FILE" ]; then
echo "Found VSIX file: $VSIX_FILE"
# Publish to VS Code Marketplace
if [ -n "$VSCE_PAT" ]; then
echo "Publishing to VS Code Marketplace..."
npx vsce publish --packagePath "$VSIX_FILE"
else
echo "⚠️ VSCE_PAT not set, skipping VS Code Marketplace publish"
fi
# Publish to Open VSX Registry
if [ -n "$OVSX_PAT" ]; then
echo "Publishing to Open VSX Registry..."
npx ovsx publish --packagePath "$VSIX_FILE"
else
echo "⚠️ OVSX_PAT not set, skipping Open VSX publish"
fi
else
echo "❌ No VSIX file found!"
exit 1
fi
echo "✅ Release process completed!"

View File

@@ -1,47 +0,0 @@
#!/usr/bin/env node
import assert from 'node:assert/strict';
import { spawnSync } from 'node:child_process';
import { readFileSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Read the extension's publishing package.json for accurate version/name
const extensionDir = join(__dirname, '..', 'apps', 'extension');
const publishPkgPath = join(extensionDir, 'package.publish.json');
let pkg;
try {
const pkgContent = readFileSync(publishPkgPath, 'utf8');
pkg = JSON.parse(pkgContent);
} catch (error) {
console.error('Failed to read package.publish.json:', error.message);
process.exit(1);
}
// Ensure we have required fields
assert(pkg.name, 'package.publish.json must have a name field');
assert(pkg.version, 'package.publish.json must have a version field');
assert(pkg.repository, 'package.publish.json must have a repository field');
const tag = `${pkg.name}@${pkg.version}`;
// Get repository URL - handle both string and object format
const repoUrl =
typeof pkg.repository === 'string' ? pkg.repository : pkg.repository.url;
assert(repoUrl, 'Repository URL not found in package.publish.json');
const { status, stdout, error } = spawnSync('git', ['ls-remote', repoUrl, tag]);
assert.equal(status, 0, error);
const exists = String(stdout).trim() !== '';
if (!exists) {
console.log(`\nNew extension tag: ${tag}`);
} else {
console.log(`\nExtension tag already exists: ${tag}`);
}

View File

@@ -1,4 +1,12 @@
import { generateObject, generateText, streamText } from 'ai';
import {
generateObject,
generateText,
streamText,
zodSchema,
JSONParseError,
NoObjectGeneratedError
} from 'ai';
import { jsonrepair } from 'jsonrepair';
import { log } from '../../scripts/modules/utils.js';
/**
@@ -53,8 +61,11 @@ export class BaseAIProvider {
) {
throw new Error('Temperature must be between 0 and 1');
}
if (params.maxTokens !== undefined && params.maxTokens <= 0) {
throw new Error('maxTokens must be greater than 0');
if (params.maxTokens !== undefined) {
const maxTokens = Number(params.maxTokens);
if (!Number.isFinite(maxTokens) || maxTokens <= 0) {
throw new Error('maxTokens must be a finite number greater than 0');
}
}
}
@@ -114,6 +125,37 @@ export class BaseAIProvider {
throw new Error('getRequiredApiKeyName must be implemented by provider');
}
/**
* Determines if a model requires max_completion_tokens instead of maxTokens
* Can be overridden by providers to specify their model requirements
* @param {string} modelId - The model ID to check
* @returns {boolean} True if the model requires max_completion_tokens
*/
requiresMaxCompletionTokens(modelId) {
return false; // Default behavior - most models use maxTokens
}
/**
* Prepares token limit parameter based on model requirements
* @param {string} modelId - The model ID
* @param {number} maxTokens - The maximum tokens value
* @returns {object} Object with either maxTokens or max_completion_tokens
*/
prepareTokenParam(modelId, maxTokens) {
if (maxTokens === undefined) {
return {};
}
// Ensure maxTokens is an integer
const tokenValue = Math.floor(Number(maxTokens));
if (this.requiresMaxCompletionTokens(modelId)) {
return { max_completion_tokens: tokenValue };
} else {
return { maxTokens: tokenValue };
}
}
/**
* Generates text using the provider's model
*/
@@ -131,7 +173,7 @@ export class BaseAIProvider {
const result = await generateText({
model: client(params.modelId),
messages: params.messages,
maxTokens: params.maxTokens,
...this.prepareTokenParam(params.modelId, params.maxTokens),
temperature: params.temperature
});
@@ -167,7 +209,7 @@ export class BaseAIProvider {
const stream = await streamText({
model: client(params.modelId),
messages: params.messages,
maxTokens: params.maxTokens,
...this.prepareTokenParam(params.modelId, params.maxTokens),
temperature: params.temperature
});
@@ -206,9 +248,9 @@ export class BaseAIProvider {
const result = await generateObject({
model: client(params.modelId),
messages: params.messages,
schema: params.schema,
mode: 'auto',
maxTokens: params.maxTokens,
schema: zodSchema(params.schema),
mode: params.mode || 'auto',
...this.prepareTokenParam(params.modelId, params.maxTokens),
temperature: params.temperature
});
@@ -226,6 +268,43 @@ export class BaseAIProvider {
}
};
} catch (error) {
// Check if this is a JSON parsing error that we can potentially fix
if (
NoObjectGeneratedError.isInstance(error) &&
JSONParseError.isInstance(error.cause) &&
error.cause.text
) {
log(
'warn',
`${this.name} generated malformed JSON, attempting to repair...`
);
try {
// Use jsonrepair to fix the malformed JSON
const repairedJson = jsonrepair(error.cause.text);
const parsed = JSON.parse(repairedJson);
log('info', `Successfully repaired ${this.name} JSON output`);
// Return in the expected format
return {
object: parsed,
usage: {
// Extract usage information from the error if available
inputTokens: error.usage?.promptTokens || 0,
outputTokens: error.usage?.completionTokens || 0,
totalTokens: error.usage?.totalTokens || 0
}
};
} catch (repairError) {
log(
'error',
`Failed to repair ${this.name} JSON: ${repairError.message}`
);
// Fall through to handleError with original error
}
}
this.handleError('object generation', error);
}
}

View File

@@ -20,6 +20,16 @@ export class OpenAIProvider extends BaseAIProvider {
return 'OPENAI_API_KEY';
}
/**
* Determines if a model requires max_completion_tokens instead of maxTokens
* GPT-5 models require max_completion_tokens parameter
* @param {string} modelId - The model ID to check
* @returns {boolean} True if the model requires max_completion_tokens
*/
requiresMaxCompletionTokens(modelId) {
return modelId && modelId.startsWith('gpt-5');
}
/**
* Creates and returns an OpenAI client instance.
* @param {object} params - Parameters for client initialization

View File

@@ -44,4 +44,21 @@ export class PerplexityAIProvider extends BaseAIProvider {
this.handleError('client initialization', error);
}
}
/**
* Override generateObject to use JSON mode for Perplexity
*
* NOTE: Perplexity models (especially sonar models) have known issues
* generating valid JSON, particularly with array fields. They often
* generate malformed JSON like "dependencies": , instead of "dependencies": []
*
* The base provider now handles JSON repair automatically for all providers.
*/
async generateObject(params) {
// Force JSON mode for Perplexity as it may help with reliability
return super.generateObject({
...params,
mode: 'json'
});
}
}

View File

@@ -30,12 +30,22 @@
"type": "boolean",
"default": false,
"description": "Use research mode for deeper analysis"
},
"isClaudeCode": {
"type": "boolean",
"default": false,
"description": "Whether Claude Code is being used as the provider"
},
"projectRoot": {
"type": "string",
"default": "",
"description": "Project root path for context"
}
},
"prompts": {
"default": {
"system": "You are an expert software architect and project manager analyzing task complexity. Respond only with the requested valid JSON array.",
"user": "Analyze the following tasks to determine their complexity (1-10 scale) and recommend the number of subtasks for expansion. Provide a brief reasoning and an initial expansion prompt for each.{{#if useResearch}} Consider current best practices, common implementation patterns, and industry standards in your analysis.{{/if}}\n\nTasks:\n{{{json tasks}}}\n{{#if gatheredContext}}\n\n# Project Context\n\n{{gatheredContext}}\n{{/if}}\n\nRespond ONLY with a valid JSON array matching the schema:\n[\n {\n \"taskId\": <number>,\n \"taskTitle\": \"<string>\",\n \"complexityScore\": <number 1-10>,\n \"recommendedSubtasks\": <number>,\n \"expansionPrompt\": \"<string>\",\n \"reasoning\": \"<string>\"\n },\n ...\n]\n\nDo not include any explanatory text, markdown formatting, or code block markers before or after the JSON array."
"user": "{{#if isClaudeCode}}## IMPORTANT: Codebase Analysis Required\n\nYou have access to powerful codebase analysis tools. Before analyzing task complexity:\n\n1. Use the Glob tool to explore the project structure and understand the codebase size\n2. Use the Grep tool to search for existing implementations related to each task\n3. Use the Read tool to examine key files that would be affected by these tasks\n4. Understand the current implementation state, patterns used, and technical debt\n\nBased on your codebase analysis:\n- Assess complexity based on ACTUAL code that needs to be modified/created\n- Consider existing abstractions and patterns that could simplify implementation\n- Identify tasks that require refactoring vs. greenfield development\n- Factor in dependencies between existing code and new features\n- Provide more accurate subtask recommendations based on real code structure\n\nProject Root: {{projectRoot}}\n\n{{/if}}Analyze the following tasks to determine their complexity (1-10 scale) and recommend the number of subtasks for expansion. Provide a brief reasoning and an initial expansion prompt for each.{{#if useResearch}} Consider current best practices, common implementation patterns, and industry standards in your analysis.{{/if}}\n\nTasks:\n{{{json tasks}}}\n{{#if gatheredContext}}\n\n# Project Context\n\n{{gatheredContext}}\n{{/if}}\n\nRespond ONLY with a valid JSON array matching the schema:\n[\n {\n \"taskId\": <number>,\n \"taskTitle\": \"<string>\",\n \"complexityScore\": <number 1-10>,\n \"recommendedSubtasks\": <number>,\n \"expansionPrompt\": \"<string>\",\n \"reasoning\": \"<string>\"\n },\n ...\n]\n\nDo not include any explanatory text, markdown formatting, or code block markers before or after the JSON array."
}
}
}

View File

@@ -51,22 +51,34 @@
"required": false,
"default": "",
"description": "Gathered project context"
},
"isClaudeCode": {
"type": "boolean",
"required": false,
"default": false,
"description": "Whether Claude Code is being used as the provider"
},
"projectRoot": {
"type": "string",
"required": false,
"default": "",
"description": "Project root path for context"
}
},
"prompts": {
"complexity-report": {
"condition": "expansionPrompt",
"system": "You are an AI assistant helping with task breakdown. Generate {{#if (gt subtaskCount 0)}}exactly {{subtaskCount}}{{else}}an appropriate number of{{/if}} subtasks based on the provided prompt and context.\nRespond ONLY with a valid JSON object containing a single key \"subtasks\" whose value is an array of the generated subtask objects.\nEach subtask object in the array must have keys: \"id\", \"title\", \"description\", \"dependencies\", \"details\", \"status\".\nEnsure the 'id' starts from {{nextSubtaskId}} and is sequential.\nFor 'dependencies', use the full subtask ID format: \"{{task.id}}.1\", \"{{task.id}}.2\", etc. Only reference subtasks within this same task.\nEnsure 'status' is 'pending'.\nDo not include any other text or explanation.",
"user": "{{expansionPrompt}}{{#if additionalContext}}\n\n{{additionalContext}}{{/if}}{{#if complexityReasoningContext}}\n\n{{complexityReasoningContext}}{{/if}}{{#if gatheredContext}}\n\n# Project Context\n\n{{gatheredContext}}{{/if}}"
"user": "Break down the following task based on the analysis prompt:\n\nParent Task:\nID: {{task.id}}\nTitle: {{task.title}}\nDescription: {{task.description}}\nCurrent details: {{#if task.details}}{{task.details}}{{else}}None{{/if}}\n\nExpansion Guidance:\n{{expansionPrompt}}{{#if additionalContext}}\n\n{{additionalContext}}{{/if}}{{#if complexityReasoningContext}}\n\n{{complexityReasoningContext}}{{/if}}{{#if gatheredContext}}\n\n# Project Context\n\n{{gatheredContext}}{{/if}}\n\nGenerate {{#if (gt subtaskCount 0)}}exactly {{subtaskCount}}{{else}}an appropriate number of{{/if}} subtasks with sequential IDs starting from {{nextSubtaskId}}."
},
"research": {
"condition": "useResearch === true && !expansionPrompt",
"system": "You are an AI assistant that responds ONLY with valid JSON objects as requested. The object should contain a 'subtasks' array.",
"user": "Analyze the following task and break it down into {{#if (gt subtaskCount 0)}}exactly {{subtaskCount}}{{else}}an appropriate number of{{/if}} specific subtasks using your research capabilities. Assign sequential IDs starting from {{nextSubtaskId}}.\n\nParent Task:\nID: {{task.id}}\nTitle: {{task.title}}\nDescription: {{task.description}}\nCurrent details: {{#if task.details}}{{task.details}}{{else}}None{{/if}}{{#if additionalContext}}\nConsider this context: {{additionalContext}}{{/if}}{{#if complexityReasoningContext}}\nComplexity Analysis Reasoning: {{complexityReasoningContext}}{{/if}}{{#if gatheredContext}}\n\n# Project Context\n\n{{gatheredContext}}{{/if}}\n\nCRITICAL: Respond ONLY with a valid JSON object containing a single key \"subtasks\". The value must be an array of the generated subtasks, strictly matching this structure:\n\n{\n \"subtasks\": [\n {\n \"id\": <number>, // Sequential ID starting from {{nextSubtaskId}}\n \"title\": \"<string>\",\n \"description\": \"<string>\",\n \"dependencies\": [\"<string>\"], // Use full subtask IDs like [\"{{task.id}}.1\", \"{{task.id}}.2\"]. If no dependencies, use an empty array [].\n \"details\": \"<string>\",\n \"testStrategy\": \"<string>\" // Optional\n },\n // ... (repeat for {{#if (gt subtaskCount 0)}}{{subtaskCount}}{{else}}appropriate number of{{/if}} subtasks)\n ]\n}\n\nImportant: For the 'dependencies' field, if a subtask has no dependencies, you MUST use an empty array, for example: \"dependencies\": []. Do not use null or omit the field.\n\nDo not include ANY explanatory text, markdown, or code block markers. Just the JSON object."
"user": "{{#if isClaudeCode}}## IMPORTANT: Codebase Analysis Required\n\nYou have access to powerful codebase analysis tools. Before generating subtasks:\n\n1. Use the Glob tool to explore relevant files for this task (e.g., \"**/*.js\", \"src/**/*.ts\")\n2. Use the Grep tool to search for existing implementations related to this task\n3. Use the Read tool to examine files that would be affected by this task\n4. Understand the current implementation state and patterns used\n\nBased on your analysis:\n- Identify existing code that relates to this task\n- Understand patterns and conventions to follow\n- Generate subtasks that integrate smoothly with existing code\n- Ensure subtasks are specific and actionable based on the actual codebase\n\nProject Root: {{projectRoot}}\n\n{{/if}}Analyze the following task and break it down into {{#if (gt subtaskCount 0)}}exactly {{subtaskCount}}{{else}}an appropriate number of{{/if}} specific subtasks using your research capabilities. Assign sequential IDs starting from {{nextSubtaskId}}.\n\nParent Task:\nID: {{task.id}}\nTitle: {{task.title}}\nDescription: {{task.description}}\nCurrent details: {{#if task.details}}{{task.details}}{{else}}None{{/if}}{{#if additionalContext}}\nConsider this context: {{additionalContext}}{{/if}}{{#if complexityReasoningContext}}\nComplexity Analysis Reasoning: {{complexityReasoningContext}}{{/if}}{{#if gatheredContext}}\n\n# Project Context\n\n{{gatheredContext}}{{/if}}\n\nCRITICAL: Respond ONLY with a valid JSON object containing a single key \"subtasks\". The value must be an array of the generated subtasks, strictly matching this structure:\n\n{\n \"subtasks\": [\n {\n \"id\": <number>, // Sequential ID starting from {{nextSubtaskId}}\n \"title\": \"<string>\",\n \"description\": \"<string>\",\n \"dependencies\": [\"<string>\"], // Use full subtask IDs like [\"{{task.id}}.1\", \"{{task.id}}.2\"]. If no dependencies, use an empty array [].\n \"details\": \"<string>\",\n \"testStrategy\": \"<string>\" // Optional\n },\n // ... (repeat for {{#if (gt subtaskCount 0)}}{{subtaskCount}}{{else}}appropriate number of{{/if}} subtasks)\n ]\n}\n\nImportant: For the 'dependencies' field, if a subtask has no dependencies, you MUST use an empty array, for example: \"dependencies\": []. Do not use null or omit the field.\n\nDo not include ANY explanatory text, markdown, or code block markers. Just the JSON object."
},
"default": {
"system": "You are an AI assistant helping with task breakdown for software development.\nYou need to break down a high-level task into {{#if (gt subtaskCount 0)}}{{subtaskCount}}{{else}}an appropriate number of{{/if}} specific subtasks that can be implemented one by one.\n\nSubtasks should:\n1. Be specific and actionable implementation steps\n2. Follow a logical sequence\n3. Each handle a distinct part of the parent task\n4. Include clear guidance on implementation approach\n5. Have appropriate dependency chains between subtasks (using full subtask IDs)\n6. Collectively cover all aspects of the parent task\n\nFor each subtask, provide:\n- id: Sequential integer starting from the provided nextSubtaskId\n- title: Clear, specific title\n- description: Detailed description\n- dependencies: Array of prerequisite subtask IDs using full format like [\"{{task.id}}.1\", \"{{task.id}}.2\"]\n- details: Implementation details, the output should be in string\n- testStrategy: Optional testing approach\n\nRespond ONLY with a valid JSON object containing a single key \"subtasks\" whose value is an array matching the structure described. Do not include any explanatory text, markdown formatting, or code block markers.",
"user": "Break down this task into {{#if (gt subtaskCount 0)}}exactly {{subtaskCount}}{{else}}an appropriate number of{{/if}} specific subtasks:\n\nTask ID: {{task.id}}\nTitle: {{task.title}}\nDescription: {{task.description}}\nCurrent details: {{#if task.details}}{{task.details}}{{else}}None{{/if}}{{#if additionalContext}}\nAdditional context: {{additionalContext}}{{/if}}{{#if complexityReasoningContext}}\nComplexity Analysis Reasoning: {{complexityReasoningContext}}{{/if}}{{#if gatheredContext}}\n\n# Project Context\n\n{{gatheredContext}}{{/if}}\n\nReturn ONLY the JSON object containing the \"subtasks\" array, matching this structure:\n\n{\n \"subtasks\": [\n {\n \"id\": {{nextSubtaskId}}, // First subtask ID\n \"title\": \"Specific subtask title\",\n \"description\": \"Detailed description\",\n \"dependencies\": [], // e.g., [\"{{task.id}}.1\", \"{{task.id}}.2\"] for dependencies. Use empty array [] if no dependencies\n \"details\": \"Implementation guidance\",\n \"testStrategy\": \"Optional testing approach\"\n },\n // ... (repeat for {{#if (gt subtaskCount 0)}}a total of {{subtaskCount}}{{else}}an appropriate number of{{/if}} subtasks with sequential IDs)\n ]\n}"
"user": "{{#if isClaudeCode}}## IMPORTANT: Codebase Analysis Required\n\nYou have access to powerful codebase analysis tools. Before generating subtasks:\n\n1. Use the Glob tool to explore relevant files for this task (e.g., \"**/*.js\", \"src/**/*.ts\")\n2. Use the Grep tool to search for existing implementations related to this task\n3. Use the Read tool to examine files that would be affected by this task\n4. Understand the current implementation state and patterns used\n\nBased on your analysis:\n- Identify existing code that relates to this task\n- Understand patterns and conventions to follow\n- Generate subtasks that integrate smoothly with existing code\n- Ensure subtasks are specific and actionable based on the actual codebase\n\nProject Root: {{projectRoot}}\n\n{{/if}}Break down this task into {{#if (gt subtaskCount 0)}}exactly {{subtaskCount}}{{else}}an appropriate number of{{/if}} specific subtasks:\n\nTask ID: {{task.id}}\nTitle: {{task.title}}\nDescription: {{task.description}}\nCurrent details: {{#if task.details}}{{task.details}}{{else}}None{{/if}}{{#if additionalContext}}\nAdditional context: {{additionalContext}}{{/if}}{{#if complexityReasoningContext}}\nComplexity Analysis Reasoning: {{complexityReasoningContext}}{{/if}}{{#if gatheredContext}}\n\n# Project Context\n\n{{gatheredContext}}{{/if}}\n\nReturn ONLY the JSON object containing the \"subtasks\" array, matching this structure:\n\n{\n \"subtasks\": [\n {\n \"id\": {{nextSubtaskId}}, // First subtask ID\n \"title\": \"Specific subtask title\",\n \"description\": \"Detailed description\",\n \"dependencies\": [], // e.g., [\"{{task.id}}.1\", \"{{task.id}}.2\"] for dependencies. Use empty array [] if no dependencies\n \"details\": \"Implementation guidance\",\n \"testStrategy\": \"Optional testing approach\"\n },\n // ... (repeat for {{#if (gt subtaskCount 0)}}a total of {{subtaskCount}}{{else}}an appropriate number of{{/if}} subtasks with sequential IDs)\n ]\n}"
}
}
}

View File

@@ -40,12 +40,24 @@
"default": "medium",
"enum": ["high", "medium", "low"],
"description": "Default priority for generated tasks"
},
"isClaudeCode": {
"type": "boolean",
"required": false,
"default": false,
"description": "Whether Claude Code is being used as the provider"
},
"projectRoot": {
"type": "string",
"required": false,
"default": "",
"description": "Project root path for context"
}
},
"prompts": {
"default": {
"system": "You are an AI assistant specialized in analyzing Product Requirements Documents (PRDs) and generating a structured, logically ordered, dependency-aware and sequenced list of development tasks in JSON format.{{#if research}}\nBefore breaking down the PRD into tasks, you will:\n1. Research and analyze the latest technologies, libraries, frameworks, and best practices that would be appropriate for this project\n2. Identify any potential technical challenges, security concerns, or scalability issues not explicitly mentioned in the PRD without discarding any explicit requirements or going overboard with complexity -- always aim to provide the most direct path to implementation, avoiding over-engineering or roundabout approaches\n3. Consider current industry standards and evolving trends relevant to this project (this step aims to solve LLM hallucinations and out of date information due to training data cutoff dates)\n4. Evaluate alternative implementation approaches and recommend the most efficient path\n5. Include specific library versions, helpful APIs, and concrete implementation guidance based on your research\n6. Always aim to provide the most direct path to implementation, avoiding over-engineering or roundabout approaches\n\nYour task breakdown should incorporate this research, resulting in more detailed implementation guidance, more accurate dependency mapping, and more precise technology recommendations than would be possible from the PRD text alone, while maintaining all explicit requirements and best practices and all details and nuances of the PRD.{{/if}}\n\nAnalyze the provided PRD content and generate {{#if (gt numTasks 0)}}approximately {{numTasks}}{{else}}an appropriate number of{{/if}} top-level development tasks. If the complexity or the level of detail of the PRD is high, generate more tasks relative to the complexity of the PRD\nEach task should represent a logical unit of work needed to implement the requirements and focus on the most direct and effective way to implement the requirements without unnecessary complexity or overengineering. Include pseudo-code, implementation details, and test strategy for each task. Find the most up to date information to implement each task.\nAssign sequential IDs starting from {{nextId}}. Infer title, description, details, and test strategy for each task based *only* on the PRD content.\nSet status to 'pending', dependencies to an empty array [], and priority to '{{defaultTaskPriority}}' initially for all tasks.\nRespond ONLY with a valid JSON object containing a single key \"tasks\", where the value is an array of task objects adhering to the provided Zod schema. Do not include any explanation or markdown formatting.\n\nEach task should follow this JSON structure:\n{\n\t\"id\": number,\n\t\"title\": string,\n\t\"description\": string,\n\t\"status\": \"pending\",\n\t\"dependencies\": number[] (IDs of tasks this depends on),\n\t\"priority\": \"high\" | \"medium\" | \"low\",\n\t\"details\": string (implementation details),\n\t\"testStrategy\": string (validation approach)\n}\n\nGuidelines:\n1. {{#if (gt numTasks 0)}}Unless complexity warrants otherwise{{else}}Depending on the complexity{{/if}}, create {{#if (gt numTasks 0)}}exactly {{numTasks}}{{else}}an appropriate number of{{/if}} tasks, numbered sequentially starting from {{nextId}}\n2. Each task should be atomic and focused on a single responsibility following the most up to date best practices and standards\n3. Order tasks logically - consider dependencies and implementation sequence\n4. Early tasks should focus on setup, core functionality first, then advanced features\n5. Include clear validation/testing approach for each task\n6. Set appropriate dependency IDs (a task can only depend on tasks with lower IDs, potentially including existing tasks with IDs less than {{nextId}} if applicable)\n7. Assign priority (high/medium/low) based on criticality and dependency order\n8. Include detailed implementation guidance in the \"details\" field{{#if research}}, with specific libraries and version recommendations based on your research{{/if}}\n9. If the PRD contains specific requirements for libraries, database schemas, frameworks, tech stacks, or any other implementation details, STRICTLY ADHERE to these requirements in your task breakdown and do not discard them under any circumstance\n10. Focus on filling in any gaps left by the PRD or areas that aren't fully specified, while preserving all explicit requirements\n11. Always aim to provide the most direct path to implementation, avoiding over-engineering or roundabout approaches{{#if research}}\n12. For each task, include specific, actionable guidance based on current industry standards and best practices discovered through research{{/if}}",
"user": "Here's the Product Requirements Document (PRD) to break down into {{#if (gt numTasks 0)}}approximately {{numTasks}}{{else}}an appropriate number of{{/if}} tasks, starting IDs from {{nextId}}:{{#if research}}\n\nRemember to thoroughly research current best practices and technologies before task breakdown to provide specific, actionable implementation details.{{/if}}\n\n{{prdContent}}\n\n\n\t\tReturn your response in this format:\n{\n \"tasks\": [\n {\n \"id\": 1,\n \"title\": \"Setup Project Repository\",\n \"description\": \"...\",\n ...\n },\n ...\n ],\n \"metadata\": {\n \"projectName\": \"PRD Implementation\",\n \"totalTasks\": {{#if (gt numTasks 0)}}{{numTasks}}{{else}}{number of tasks}{{/if}},\n \"sourceFile\": \"{{prdPath}}\",\n \"generatedAt\": \"YYYY-MM-DD\"\n }\n}"
"user": "{{#if isClaudeCode}}## IMPORTANT: Codebase Analysis Required\n\nYou have access to powerful codebase analysis tools. Before generating tasks:\n\n1. Use the Glob tool to explore the project structure (e.g., \"**/*.js\", \"**/*.json\", \"**/README.md\")\n2. Use the Grep tool to search for existing implementations, patterns, and technologies\n3. Use the Read tool to examine key files like package.json, README.md, and main entry points\n4. Analyze the current state of implementation to understand what already exists\n\nBased on your analysis:\n- Identify what components/features are already implemented\n- Understand the technology stack, frameworks, and patterns in use\n- Generate tasks that build upon the existing codebase rather than duplicating work\n- Ensure tasks align with the project's current architecture and conventions\n\nProject Root: {{projectRoot}}\n\n{{/if}}Here's the Product Requirements Document (PRD) to break down into {{#if (gt numTasks 0)}}approximately {{numTasks}}{{else}}an appropriate number of{{/if}} tasks, starting IDs from {{nextId}}:{{#if research}}\n\nRemember to thoroughly research current best practices and technologies before task breakdown to provide specific, actionable implementation details.{{/if}}\n\n{{prdContent}}\n\n\n\t\tReturn your response in this format:\n{\n \"tasks\": [\n {\n \"id\": 1,\n \"title\": \"Setup Project Repository\",\n \"description\": \"...\",\n ...\n },\n ...\n ],\n \"metadata\": {\n \"projectName\": \"PRD Implementation\",\n \"totalTasks\": {{#if (gt numTasks 0)}}{{numTasks}}{{else}}{number of tasks}{{/if}},\n \"sourceFile\": \"{{prdPath}}\",\n \"generatedAt\": \"YYYY-MM-DD\"\n }\n}"
}
}
}

View File

@@ -275,7 +275,11 @@ export function findComplexityReportPath(
'' // Project root
];
const fileNames = ['task-complexity', 'complexity-report'].map((fileName) => {
const fileNames = [
'task-complexity-report',
'task-complexity',
'complexity-report'
].map((fileName) => {
if (args?.tag && args?.tag !== 'master') {
return `${fileName}_${args.tag}.json`;
}

View File

@@ -0,0 +1,238 @@
/**
* Tests for OpenAI Provider - Token parameter handling for GPT-5
*
* This test suite covers:
* 1. Correct identification of GPT-5 models requiring max_completion_tokens
* 2. Token parameter preparation for different model types
* 3. Validation of maxTokens parameter
* 4. Integer coercion of token values
*/
import { jest } from '@jest/globals';
// Mock the utils module to prevent logging during tests
jest.mock('../../../scripts/modules/utils.js', () => ({
log: jest.fn()
}));
// Import the provider
import { OpenAIProvider } from '../../../src/ai-providers/openai.js';
describe('OpenAIProvider', () => {
let provider;
beforeEach(() => {
provider = new OpenAIProvider();
jest.clearAllMocks();
});
describe('requiresMaxCompletionTokens', () => {
it('should return true for GPT-5 models', () => {
expect(provider.requiresMaxCompletionTokens('gpt-5')).toBe(true);
expect(provider.requiresMaxCompletionTokens('gpt-5-mini')).toBe(true);
expect(provider.requiresMaxCompletionTokens('gpt-5-nano')).toBe(true);
expect(provider.requiresMaxCompletionTokens('gpt-5-turbo')).toBe(true);
});
it('should return false for non-GPT-5 models', () => {
expect(provider.requiresMaxCompletionTokens('gpt-4')).toBe(false);
expect(provider.requiresMaxCompletionTokens('gpt-4o')).toBe(false);
expect(provider.requiresMaxCompletionTokens('gpt-3.5-turbo')).toBe(false);
expect(provider.requiresMaxCompletionTokens('o1')).toBe(false);
expect(provider.requiresMaxCompletionTokens('o1-mini')).toBe(false);
});
it('should handle null/undefined modelId', () => {
expect(provider.requiresMaxCompletionTokens(null)).toBeFalsy();
expect(provider.requiresMaxCompletionTokens(undefined)).toBeFalsy();
expect(provider.requiresMaxCompletionTokens('')).toBeFalsy();
});
});
describe('prepareTokenParam', () => {
it('should return max_completion_tokens for GPT-5 models', () => {
const result = provider.prepareTokenParam('gpt-5', 1000);
expect(result).toEqual({ max_completion_tokens: 1000 });
});
it('should return maxTokens for non-GPT-5 models', () => {
const result = provider.prepareTokenParam('gpt-4', 1000);
expect(result).toEqual({ maxTokens: 1000 });
});
it('should coerce token value to integer', () => {
// Float values
const result1 = provider.prepareTokenParam('gpt-5', 1000.7);
expect(result1).toEqual({ max_completion_tokens: 1000 });
const result2 = provider.prepareTokenParam('gpt-4', 1000.7);
expect(result2).toEqual({ maxTokens: 1000 });
// String float
const result3 = provider.prepareTokenParam('gpt-5', '1000.7');
expect(result3).toEqual({ max_completion_tokens: 1000 });
// String integers (common CLI input path)
expect(provider.prepareTokenParam('gpt-5', '1000')).toEqual({
max_completion_tokens: 1000
});
expect(provider.prepareTokenParam('gpt-4', '1000')).toEqual({
maxTokens: 1000
});
});
it('should return empty object for undefined maxTokens', () => {
const result = provider.prepareTokenParam('gpt-5', undefined);
expect(result).toEqual({});
});
it('should handle edge cases', () => {
// Test with 0 (should still pass through as 0)
const result1 = provider.prepareTokenParam('gpt-5', 0);
expect(result1).toEqual({ max_completion_tokens: 0 });
// Test with string number
const result2 = provider.prepareTokenParam('gpt-5', '100');
expect(result2).toEqual({ max_completion_tokens: 100 });
// Test with negative number (will be floored, validation happens elsewhere)
const result3 = provider.prepareTokenParam('gpt-4', -10.5);
expect(result3).toEqual({ maxTokens: -11 });
});
});
describe('validateOptionalParams', () => {
it('should accept valid maxTokens values', () => {
expect(() =>
provider.validateOptionalParams({ maxTokens: 1000 })
).not.toThrow();
expect(() =>
provider.validateOptionalParams({ maxTokens: 1 })
).not.toThrow();
expect(() =>
provider.validateOptionalParams({ maxTokens: '1000' })
).not.toThrow();
});
it('should reject invalid maxTokens values', () => {
expect(() => provider.validateOptionalParams({ maxTokens: 0 })).toThrow(
Error
);
expect(() => provider.validateOptionalParams({ maxTokens: -1 })).toThrow(
Error
);
expect(() => provider.validateOptionalParams({ maxTokens: NaN })).toThrow(
Error
);
expect(() =>
provider.validateOptionalParams({ maxTokens: Infinity })
).toThrow(Error);
expect(() =>
provider.validateOptionalParams({ maxTokens: 'invalid' })
).toThrow(Error);
});
it('should accept valid temperature values', () => {
expect(() =>
provider.validateOptionalParams({ temperature: 0 })
).not.toThrow();
expect(() =>
provider.validateOptionalParams({ temperature: 0.5 })
).not.toThrow();
expect(() =>
provider.validateOptionalParams({ temperature: 1 })
).not.toThrow();
});
it('should reject invalid temperature values', () => {
expect(() =>
provider.validateOptionalParams({ temperature: -0.1 })
).toThrow(Error);
expect(() =>
provider.validateOptionalParams({ temperature: 1.1 })
).toThrow(Error);
});
});
describe('getRequiredApiKeyName', () => {
it('should return OPENAI_API_KEY', () => {
expect(provider.getRequiredApiKeyName()).toBe('OPENAI_API_KEY');
});
});
describe('getClient', () => {
it('should throw error if API key is missing', () => {
expect(() => provider.getClient({})).toThrow(Error);
});
it('should create client with apiKey only', () => {
const params = {
apiKey: 'sk-test-123'
};
// The getClient method should return a function
const client = provider.getClient(params);
expect(typeof client).toBe('function');
// The client function should be callable and return a model object
const model = client('gpt-4');
expect(model).toBeDefined();
expect(model.modelId).toBe('gpt-4');
});
it('should create client with apiKey and baseURL', () => {
const params = {
apiKey: 'sk-test-456',
baseURL: 'https://api.openai.example'
};
// Should not throw when baseURL is provided
const client = provider.getClient(params);
expect(typeof client).toBe('function');
// The client function should be callable and return a model object
const model = client('gpt-5');
expect(model).toBeDefined();
expect(model.modelId).toBe('gpt-5');
});
it('should return the same client instance for the same parameters', () => {
const params = {
apiKey: 'sk-test-789'
};
// Multiple calls with same params should work
const client1 = provider.getClient(params);
const client2 = provider.getClient(params);
expect(typeof client1).toBe('function');
expect(typeof client2).toBe('function');
// Both clients should be able to create models
const model1 = client1('gpt-4');
const model2 = client2('gpt-4');
expect(model1.modelId).toBe('gpt-4');
expect(model2.modelId).toBe('gpt-4');
});
it('should handle different model IDs correctly', () => {
const client = provider.getClient({ apiKey: 'sk-test-models' });
// Test with different models
const gpt4 = client('gpt-4');
expect(gpt4.modelId).toBe('gpt-4');
const gpt5 = client('gpt-5');
expect(gpt5.modelId).toBe('gpt-5');
const gpt35 = client('gpt-3.5-turbo');
expect(gpt35.modelId).toBe('gpt-3.5-turbo');
});
});
describe('name property', () => {
it('should have OpenAI as the provider name', () => {
expect(provider.name).toBe('OpenAI');
});
});
});

View File

@@ -0,0 +1,134 @@
import { jest } from '@jest/globals';
import { PromptManager } from '../../../scripts/modules/prompt-manager.js';
describe('expand-task prompt template', () => {
let promptManager;
beforeEach(() => {
promptManager = new PromptManager();
});
const testTask = {
id: 1,
title: 'Setup AWS Infrastructure',
description: 'Provision core AWS services',
details: 'Create VPC, subnets, and security groups'
};
const baseParams = {
task: testTask,
subtaskCount: 3,
nextSubtaskId: 1,
additionalContext: '',
complexityReasoningContext: '',
gatheredContext: '',
useResearch: false,
expansionPrompt: undefined
};
test('default variant includes task context', () => {
const { userPrompt } = promptManager.loadPrompt(
'expand-task',
baseParams,
'default'
);
expect(userPrompt).toContain(testTask.title);
expect(userPrompt).toContain(testTask.description);
expect(userPrompt).toContain(testTask.details);
expect(userPrompt).toContain('Task ID: 1');
});
test('research variant includes task context', () => {
const params = { ...baseParams, useResearch: true };
const { userPrompt } = promptManager.loadPrompt(
'expand-task',
params,
'research'
);
expect(userPrompt).toContain(testTask.title);
expect(userPrompt).toContain(testTask.description);
expect(userPrompt).toContain(testTask.details);
expect(userPrompt).toContain('Parent Task:');
expect(userPrompt).toContain('ID: 1');
});
test('complexity-report variant includes task context', () => {
const params = {
...baseParams,
expansionPrompt: 'Focus on security best practices',
complexityReasoningContext: 'High complexity due to security requirements'
};
const { userPrompt } = promptManager.loadPrompt(
'expand-task',
params,
'complexity-report'
);
// The fix ensures task context is included
expect(userPrompt).toContain('Parent Task:');
expect(userPrompt).toContain(`ID: ${testTask.id}`);
expect(userPrompt).toContain(`Title: ${testTask.title}`);
expect(userPrompt).toContain(`Description: ${testTask.description}`);
expect(userPrompt).toContain(`Current details: ${testTask.details}`);
// Also includes the expansion prompt
expect(userPrompt).toContain('Expansion Guidance:');
expect(userPrompt).toContain(params.expansionPrompt);
expect(userPrompt).toContain(params.complexityReasoningContext);
});
test('all variants request JSON format with subtasks array', () => {
const variants = ['default', 'research', 'complexity-report'];
variants.forEach((variant) => {
const params =
variant === 'complexity-report'
? { ...baseParams, expansionPrompt: 'test' }
: baseParams;
const { systemPrompt, userPrompt } = promptManager.loadPrompt(
'expand-task',
params,
variant
);
const combined = systemPrompt + userPrompt;
expect(combined.toLowerCase()).toContain('subtasks');
expect(combined).toContain('JSON');
});
});
test('complexity-report variant fails without task context regression test', () => {
// This test ensures we don't regress to the old behavior where
// complexity-report variant only used expansionPrompt without task context
const params = {
...baseParams,
expansionPrompt: 'Generic expansion prompt'
};
const { userPrompt } = promptManager.loadPrompt(
'expand-task',
params,
'complexity-report'
);
// Count occurrences of task-specific content
const titleOccurrences = (
userPrompt.match(new RegExp(testTask.title, 'g')) || []
).length;
const descriptionOccurrences = (
userPrompt.match(new RegExp(testTask.description, 'g')) || []
).length;
// Should have at least one occurrence of title and description
expect(titleOccurrences).toBeGreaterThanOrEqual(1);
expect(descriptionOccurrences).toBeGreaterThanOrEqual(1);
// Should not be ONLY the expansion prompt
expect(userPrompt.length).toBeGreaterThan(
params.expansionPrompt.length + 100
);
});
});

View File

@@ -123,7 +123,9 @@ jest.unstable_mockModule(
() => ({
getDefaultSubtasks: jest.fn(() => 3),
getDebugFlag: jest.fn(() => false),
getDefaultNumTasks: jest.fn(() => 10)
getDefaultNumTasks: jest.fn(() => 10),
getMainProvider: jest.fn(() => 'openai'),
getResearchProvider: jest.fn(() => 'perplexity')
})
);

View File

@@ -49,7 +49,9 @@ jest.unstable_mockModule(
() => ({
getDebugFlag: jest.fn(() => false),
getDefaultNumTasks: jest.fn(() => 10),
getDefaultPriority: jest.fn(() => 'medium')
getDefaultPriority: jest.fn(() => 'medium'),
getMainProvider: jest.fn(() => 'openai'),
getResearchProvider: jest.fn(() => 'perplexity')
})
);

View File

@@ -0,0 +1,345 @@
/**
* Tests for scope-adjustment.js module
*/
import { jest } from '@jest/globals';
// Mock dependencies using unstable_mockModule for ES modules
jest.unstable_mockModule('../../../../../scripts/modules/utils.js', () => ({
log: jest.fn(),
readJSON: jest.fn(),
writeJSON: jest.fn(),
getCurrentTag: jest.fn(() => 'master'),
readComplexityReport: jest.fn(),
findTaskInComplexityReport: jest.fn(),
findProjectRoot: jest.fn()
}));
jest.unstable_mockModule(
'../../../../../scripts/modules/ai-services-unified.js',
() => ({
generateObjectService: jest.fn(),
generateTextService: jest.fn()
})
);
jest.unstable_mockModule(
'../../../../../scripts/modules/task-manager.js',
() => ({
findTaskById: jest.fn(),
taskExists: jest.fn()
})
);
jest.unstable_mockModule(
'../../../../../scripts/modules/task-manager/analyze-task-complexity.js',
() => ({
default: jest.fn()
})
);
jest.unstable_mockModule('../../../../../src/utils/path-utils.js', () => ({
findComplexityReportPath: jest.fn()
}));
// Import modules after mocking
const {
log,
readJSON,
writeJSON,
readComplexityReport,
findTaskInComplexityReport
} = await import('../../../../../scripts/modules/utils.js');
const { generateObjectService } = await import(
'../../../../../scripts/modules/ai-services-unified.js'
);
const { findTaskById, taskExists } = await import(
'../../../../../scripts/modules/task-manager.js'
);
const { scopeUpTask, scopeDownTask, validateStrength } = await import(
'../../../../../scripts/modules/task-manager/scope-adjustment.js'
);
describe('Scope Adjustment Commands', () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe('scopeUpTask', () => {
it('should increase task complexity with regular strength', async () => {
// Mock existing task data
const mockTasksData = {
tasks: [
{
id: 1,
title: 'Simple Task',
description: 'Basic description',
details: 'Basic implementation details',
status: 'pending'
}
]
};
const mockTask = {
id: 1,
title: 'Simple Task',
description: 'Basic description',
details: 'Basic implementation details',
status: 'pending'
};
readJSON.mockReturnValue(mockTasksData);
taskExists.mockReturnValue(true);
findTaskById.mockReturnValue({ task: mockTask });
generateObjectService.mockResolvedValue({
mainResult: {
title: 'Complex Task with Advanced Features',
description: 'Enhanced description with more requirements',
details:
'Detailed implementation with error handling, validation, and advanced features',
testStrategy:
'Comprehensive testing including unit, integration, and edge cases'
},
telemetryData: { tokens: 100, cost: 0.01 }
});
const context = {
projectRoot: '/test/project',
tag: 'master',
commandName: 'scope-up',
outputType: 'cli'
};
const result = await scopeUpTask(
'/test/tasks.json',
[1],
'regular',
null, // no custom prompt
context,
'text'
);
expect(result).toBeDefined();
expect(result.updatedTasks).toHaveLength(1);
expect(result.telemetryData).toBeDefined();
expect(writeJSON).toHaveBeenCalledWith(
'/test/tasks.json',
expect.objectContaining({
tasks: expect.arrayContaining([
expect.objectContaining({
id: 1,
title: 'Complex Task with Advanced Features'
})
])
}),
'/test/project',
'master'
);
});
it('should handle custom prompts for targeted scope adjustments', async () => {
const mockTasksData = {
tasks: [
{
id: 1,
title: 'Simple Task',
description: 'Basic description',
details: 'Basic implementation details',
status: 'pending'
}
]
};
const mockTask = {
id: 1,
title: 'Simple Task',
description: 'Basic description',
details: 'Basic implementation details',
status: 'pending'
};
readJSON.mockReturnValue(mockTasksData);
taskExists.mockReturnValue(true);
findTaskById.mockReturnValue({ task: mockTask });
generateObjectService.mockResolvedValue({
mainResult: {
title: 'Task with Enhanced Security',
description: 'Description with security considerations',
details: 'Implementation with security validation and encryption',
testStrategy: 'Security-focused testing strategy'
},
telemetryData: { tokens: 120, cost: 0.012 }
});
const context = {
projectRoot: '/test/project',
tag: 'master',
commandName: 'scope-up',
outputType: 'cli'
};
const customPrompt = 'Focus on adding security features and validation';
const result = await scopeUpTask(
'/test/tasks.json',
[1],
'heavy',
customPrompt,
context,
'text'
);
expect(result).toBeDefined();
expect(generateObjectService).toHaveBeenCalledWith(
expect.objectContaining({
prompt: expect.stringContaining(
'Focus on adding security features and validation'
)
})
);
});
});
describe('scopeDownTask', () => {
it('should decrease task complexity with regular strength', async () => {
const mockTasksData = {
tasks: [
{
id: 1,
title: 'Complex Task with Many Features',
description: 'Comprehensive description with multiple requirements',
details:
'Detailed implementation with advanced features, error handling, validation',
status: 'pending'
}
]
};
const mockTask = {
id: 1,
title: 'Complex Task with Many Features',
description: 'Comprehensive description with multiple requirements',
details:
'Detailed implementation with advanced features, error handling, validation',
status: 'pending'
};
readJSON.mockReturnValue(mockTasksData);
taskExists.mockReturnValue(true);
findTaskById.mockReturnValue({ task: mockTask });
generateObjectService.mockResolvedValue({
mainResult: {
title: 'Simple Task',
description: 'Basic description',
details: 'Basic implementation focusing on core functionality',
testStrategy: 'Simple unit tests for core functionality'
},
telemetryData: { tokens: 80, cost: 0.008 }
});
const context = {
projectRoot: '/test/project',
tag: 'master',
commandName: 'scope-down',
outputType: 'cli'
};
const result = await scopeDownTask(
'/test/tasks.json',
[1],
'regular',
null,
context,
'text'
);
expect(result).toBeDefined();
expect(result.updatedTasks).toHaveLength(1);
expect(writeJSON).toHaveBeenCalled();
});
});
describe('strength level validation', () => {
it('should validate strength parameter correctly', () => {
expect(validateStrength('light')).toBe(true);
expect(validateStrength('regular')).toBe(true);
expect(validateStrength('heavy')).toBe(true);
expect(validateStrength('invalid')).toBe(false);
expect(validateStrength('')).toBe(false);
expect(validateStrength(null)).toBe(false);
});
});
describe('multiple task IDs handling', () => {
it('should handle comma-separated task IDs', async () => {
const mockTasksData = {
tasks: [
{
id: 1,
title: 'Task 1',
description: 'Desc 1',
details: 'Details 1',
status: 'pending'
},
{
id: 2,
title: 'Task 2',
description: 'Desc 2',
details: 'Details 2',
status: 'pending'
}
]
};
readJSON.mockReturnValue(mockTasksData);
taskExists.mockReturnValue(true);
findTaskById
.mockReturnValueOnce({
task: {
id: 1,
title: 'Task 1',
description: 'Desc 1',
details: 'Details 1',
status: 'pending'
}
})
.mockReturnValueOnce({
task: {
id: 2,
title: 'Task 2',
description: 'Desc 2',
details: 'Details 2',
status: 'pending'
}
});
generateObjectService.mockResolvedValue({
mainResult: {
title: 'Enhanced Task',
description: 'Enhanced description',
details: 'Enhanced details',
testStrategy: 'Enhanced testing'
},
telemetryData: { tokens: 100, cost: 0.01 }
});
const context = {
projectRoot: '/test/project',
tag: 'master',
commandName: 'scope-up',
outputType: 'cli'
};
const result = await scopeUpTask(
'/test/tasks.json',
[1, 2],
'regular',
null,
context,
'text'
);
expect(result.updatedTasks).toHaveLength(2);
expect(generateObjectService).toHaveBeenCalledTimes(2);
});
});
});

View File

@@ -23,6 +23,82 @@ describe('Task Finder', () => {
expect(result.originalSubtaskCount).toBeNull();
});
test('should find tasks when JSON contains string IDs (normalized to numbers)', () => {
// Simulate tasks loaded from JSON with string IDs and mixed subtask notations
const tasksWithStringIds = [
{ id: '1', title: 'First Task' },
{
id: '2',
title: 'Second Task',
subtasks: [
{ id: '1', title: 'Subtask One' },
{ id: '2.2', title: 'Subtask Two (with dotted notation)' } // Testing dotted notation
]
},
{
id: '5',
title: 'Fifth Task',
subtasks: [
{ id: '5.1', title: 'Subtask with dotted ID' }, // Should normalize to 1
{ id: '3', title: 'Subtask with simple ID' } // Should stay as 3
]
}
];
// The readJSON function should normalize these IDs to numbers
// For this test, we'll manually normalize them to simulate what happens
tasksWithStringIds.forEach((task) => {
task.id = parseInt(task.id, 10);
if (task.subtasks) {
task.subtasks.forEach((subtask) => {
// Handle dotted notation like "5.1" -> extract the subtask part
if (typeof subtask.id === 'string' && subtask.id.includes('.')) {
const parts = subtask.id.split('.');
subtask.id = parseInt(parts[parts.length - 1], 10);
} else {
subtask.id = parseInt(subtask.id, 10);
}
});
}
});
// Test finding tasks by numeric ID
const result1 = findTaskById(tasksWithStringIds, 5);
expect(result1.task).toBeDefined();
expect(result1.task.id).toBe(5);
expect(result1.task.title).toBe('Fifth Task');
// Test finding tasks by string ID
const result2 = findTaskById(tasksWithStringIds, '5');
expect(result2.task).toBeDefined();
expect(result2.task.id).toBe(5);
// Test finding subtasks with normalized IDs
const result3 = findTaskById(tasksWithStringIds, '2.1');
expect(result3.task).toBeDefined();
expect(result3.task.id).toBe(1);
expect(result3.task.title).toBe('Subtask One');
expect(result3.task.isSubtask).toBe(true);
// Test subtask that was originally "2.2" (should be normalized to 2)
const result4 = findTaskById(tasksWithStringIds, '2.2');
expect(result4.task).toBeDefined();
expect(result4.task.id).toBe(2);
expect(result4.task.title).toBe('Subtask Two (with dotted notation)');
// Test subtask that was originally "5.1" (should be normalized to 1)
const result5 = findTaskById(tasksWithStringIds, '5.1');
expect(result5.task).toBeDefined();
expect(result5.task.id).toBe(1);
expect(result5.task.title).toBe('Subtask with dotted ID');
// Test subtask that was originally "3" (should stay as 3)
const result6 = findTaskById(tasksWithStringIds, '5.3');
expect(result6.task).toBeDefined();
expect(result6.task.id).toBe(3);
expect(result6.task.title).toBe('Subtask with simple ID');
});
test('should find a subtask using dot notation', () => {
const result = findTaskById(sampleTasks.tasks, '3.1');
expect(result.task).toBeDefined();