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.
This commit is contained in:
Eyal Toledano
2025-08-02 19:43:04 +03:00
committed by GitHub
parent 64302dc191
commit 72ca68edeb
21 changed files with 3402 additions and 553 deletions

View File

@@ -0,0 +1,38 @@
---
"task-master-ai": patch
---
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.

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)

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

@@ -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,9 +1,9 @@
{
"currentTag": "master",
"lastSwitched": "2025-07-22T13:32:03.558Z",
"branchTagMapping": {
"v017-adds": "v017-adds",
"next": "next"
},
"migrationNoticeShown": true
}
"currentTag": "master",
"lastSwitched": "2025-08-01T14:09:25.838Z",
"branchTagMapping": {
"v017-adds": "v017-adds",
"next": "next"
},
"migrationNoticeShown": true
}

File diff suppressed because one or more lines are too long

View File

@@ -4,10 +4,12 @@ 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 +33,15 @@ export const AIActionsSection: React.FC<AIActionsSectionProps> = ({
onAppendingChange
}) => {
const [prompt, setPrompt] = useState('');
const [lastAction, setLastAction] = useState<'regenerate' | 'append' | 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 +109,64 @@ 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 +175,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 +347,12 @@ 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

@@ -227,3 +227,119 @@ 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: 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: 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

@@ -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
const taskIds = id.split(',').map((taskId) => taskId.trim());
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
},
'json', // outputFormat
research
);
// 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
const taskIds = id.split(',').map((taskId) => taskId.trim());
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
},
'json', // outputFormat
research
);
// 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);
}
})
});
}

2
package-lock.json generated
View File

@@ -84,7 +84,7 @@
}
},
"apps/extension": {
"version": "0.0.0",
"version": "0.22.3",
"devDependencies": {
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/modifiers": "^9.0.0",

View File

@@ -42,7 +42,10 @@ import {
taskExists,
moveTask,
migrateProject,
setResponseLanguage
setResponseLanguage,
scopeUpTask,
scopeDownTask,
validateStrength
} from './task-manager.js';
import {
@@ -1386,6 +1389,258 @@ 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'
};
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'
};
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

@@ -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

@@ -0,0 +1,828 @@
/**
* 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`
);
console.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
- 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: 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().default('pending'),
testStrategy: z.string().nullable().default('')
})
)
});
const aiResult = await generateObjectService({
role: '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 || [];
// Update task with preserved subtasks + newly generated ones
task.subtasks = [...preservedSubtasks, ...generatedSubtasks];
return {
updatedTask: task,
regenerated: true,
preserved: preservedSubtasks.length,
generated: generatedSubtasks.length
};
} catch (error) {
console.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
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'])
.optional()
.describe('Task priority level')
});
const aiResult = await generateObjectService({
role: 'main',
session: context.session,
systemPrompt,
prompt,
schema: taskSchema,
objectName: 'updated_task',
commandName: context.commandName || `scope-${direction}`,
outputType: context.outputType || 'cli'
});
const updatedTaskData = aiResult.mainResult;
return {
updatedTask: {
...task,
...updatedTaskData
},
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') {
console.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 ? '↘️' : '➡️';
console.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') {
console.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 ? '↘️' : '➡️';
console.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

@@ -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,326 @@
/**
* 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')
}));
jest.unstable_mockModule(
'../../../../../scripts/modules/ai-services-unified.js',
() => ({
generateObjectService: jest.fn()
})
);
jest.unstable_mockModule(
'../../../../../scripts/modules/task-manager.js',
() => ({
findTaskById: jest.fn(),
taskExists: jest.fn()
})
);
// Import modules after mocking
const { log, readJSON, writeJSON } = 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);
});
});
});