diff --git a/.changeset/add-mcpb-bundle.md b/.changeset/add-mcpb-bundle.md deleted file mode 100644 index fb3d8974b1..0000000000 --- a/.changeset/add-mcpb-bundle.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"task-master-ai": minor ---- - -Add MCPB bundle for single-click Claude Desktop installation - -- Added `manifest.json` for MCP Bundle (MCPB) specification v0.3 -- Added `.mcpbignore` to exclude development files from bundle -- Added `icon.png` (512x512) for Claude Desktop display -- Enables users to install Task Master MCP server directly in Claude Desktop without manual configuration diff --git a/.changeset/add-modifyjson-utils.md b/.changeset/add-modifyjson-utils.md deleted file mode 100644 index 01721485e4..0000000000 --- a/.changeset/add-modifyjson-utils.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"task-master-ai": patch ---- - -Add modifyJSON function for safer file updates diff --git a/.changeset/complexity-tag-fix.md b/.changeset/complexity-tag-fix.md new file mode 100644 index 0000000000..8e394952bd --- /dev/null +++ b/.changeset/complexity-tag-fix.md @@ -0,0 +1,9 @@ +--- +"@tm/core": patch +--- + +Fix: Ensure ComplexityReportManager uses resolved tag consistently when loading reports + +This fixes a potential bug where the cache key and file path could use different tag values when loading complexity reports. The fix ensures that when tag is undefined, both the cache lookup and file path resolution use 'master' consistently. + +Fixes #1614 diff --git a/.changeset/cuddly-wings-drop.md b/.changeset/cuddly-wings-drop.md deleted file mode 100644 index d14f2a611e..0000000000 --- a/.changeset/cuddly-wings-drop.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"task-master-ai": minor ---- - -Add verbose output mode to loop command with `--verbose` flag - -- New `-v, --verbose` flag shows Claude's work in real-time (thinking, tool calls) rather than waiting until the iteration completes -- New `--no-output` flag excludes full Claude output from iteration results to save memory -- Improved error handling with proper validation for incompatible options (verbose + sandbox) diff --git a/.changeset/fair-heads-report.md b/.changeset/fair-heads-report.md deleted file mode 100644 index f25ed1adc1..0000000000 --- a/.changeset/fair-heads-report.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"task-master-ai": patch ---- - -Add --no-banner to suppress the startup banner. diff --git a/.changeset/task-metadata-field.md b/.changeset/task-metadata-field.md deleted file mode 100644 index a6fe79f3ee..0000000000 --- a/.changeset/task-metadata-field.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -"task-master-ai": minor ---- - -Add optional `metadata` field to tasks for storing user-defined custom data - -Tasks and subtasks now support an optional `metadata` field that allows storing arbitrary JSON data such as: -- External IDs (GitHub issues, Jira tickets, Linear issues) -- Workflow data (sprints, story points, custom statuses) -- Integration data (sync timestamps, external system references) -- Custom tracking (UUIDs, version numbers, audit information) - -Key features: -- **AI-Safe**: Metadata is preserved through all AI operations (update-task, expand, etc.) because AI schemas intentionally exclude this field -- **Flexible Schema**: Store any JSON-serializable data without schema changes -- **Backward Compatible**: The field is optional; existing tasks work without modification -- **Subtask Support**: Both tasks and subtasks can have their own metadata -- **MCP Tool Support**: Use `update_task` and `update_subtask` with the `metadata` parameter to update metadata (requires `TASK_MASTER_ALLOW_METADATA_UPDATES=true` in MCP server environment) - -Example usage: -```json -{ - "id": 1, - "title": "Implement authentication", - "metadata": { - "githubIssue": 42, - "sprint": "Q1-S3", - "storyPoints": 5 - } -} -``` - -MCP metadata update example: -```javascript -// With TASK_MASTER_ALLOW_METADATA_UPDATES=true set in MCP env -update_task({ - id: "1", - metadata: '{"githubIssue": 42, "sprint": "Q1-S3"}' -}) -``` diff --git a/CHANGELOG.md b/CHANGELOG.md index 040ce3ff8f..0e2204bad2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,65 @@ # task-master-ai +## 0.43.0 + +### Minor Changes + +- [#1599](https://github.com/eyaltoledano/claude-task-master/pull/1599) [`e689fcf`](https://github.com/eyaltoledano/claude-task-master/commit/e689fcf2a20cada4a19ee31fed723b6f35f2c13d) Thanks [@triepod-ai](https://github.com/triepod-ai)! - Add MCPB bundle for single-click Claude Desktop installation + - Added `manifest.json` for MCP Bundle (MCPB) specification v0.3 + - Added `.mcpbignore` to exclude development files from bundle + - Added `icon.png` (512x512) for Claude Desktop display + - Enables users to install Task Master MCP server directly in Claude Desktop without manual configuration + +- [#1605](https://github.com/eyaltoledano/claude-task-master/pull/1605) [`efedc85`](https://github.com/eyaltoledano/claude-task-master/commit/efedc85cb1110a75748f3df0e530f3c9e27d2155) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Add verbose output mode to loop command with `--verbose` flag + - New `-v, --verbose` flag shows Claude's work in real-time (thinking, tool calls) rather than waiting until the iteration completes + - New `--no-output` flag excludes full Claude output from iteration results to save memory + - Improved error handling with proper validation for incompatible options (verbose + sandbox) + +- [#1611](https://github.com/eyaltoledano/claude-task-master/pull/1611) [`c798639`](https://github.com/eyaltoledano/claude-task-master/commit/c798639d1a6b492de1b7cc82a28a13ddfba23eb8) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Add optional `metadata` field to tasks for storing user-defined custom data + + Tasks and subtasks now support an optional `metadata` field that allows storing arbitrary JSON data such as: + - External IDs (GitHub issues, Jira tickets, Linear issues) + - Workflow data (sprints, story points, custom statuses) + - Integration data (sync timestamps, external system references) + - Custom tracking (UUIDs, version numbers, audit information) + + Key features: + - **AI-Safe**: Metadata is preserved through all AI operations (update-task, expand, etc.) because AI schemas intentionally exclude this field + - **Flexible Schema**: Store any JSON-serializable data without schema changes + - **Backward Compatible**: The field is optional; existing tasks work without modification + - **Subtask Support**: Both tasks and subtasks can have their own metadata + - **MCP Tool Support**: Use `update_task` and `update_subtask` with the `metadata` parameter to update metadata (requires `TASK_MASTER_ALLOW_METADATA_UPDATES=true` in MCP server environment) + + Example usage: + + ```json + { + "id": 1, + "title": "Implement authentication", + "metadata": { + "githubIssue": 42, + "sprint": "Q1-S3", + "storyPoints": 5 + } + } + ``` + + MCP metadata update example: + + ```javascript + // With TASK_MASTER_ALLOW_METADATA_UPDATES=true set in MCP env + update_task({ + id: "1", + metadata: '{"githubIssue": 42, "sprint": "Q1-S3"}', + }); + ``` + +### Patch Changes + +- [#1587](https://github.com/eyaltoledano/claude-task-master/pull/1587) [`0d628ca`](https://github.com/eyaltoledano/claude-task-master/commit/0d628ca9514f22607c0a6495b701e4cde743b45c) Thanks [@bjcoombs](https://github.com/bjcoombs)! - Add modifyJSON function for safer file updates + +- [#1600](https://github.com/eyaltoledano/claude-task-master/pull/1600) [`712a078`](https://github.com/eyaltoledano/claude-task-master/commit/712a0789d6d584adf5dbb27732c783cd240014b2) Thanks [@esumerfd](https://github.com/esumerfd)! - Add --no-banner to suppress the startup banner. + ## 0.42.0 ### Minor Changes diff --git a/manifest.json b/manifest.json index daa622f03a..2bb3b8ce37 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": "0.3", "name": "Claude Task Master", - "version": "0.42.0", + "version": "0.43.0", "description": "AI-powered task management for structured development workflows. Parse PRDs, generate tasks with AI, track dependencies, and manage complexity.", "author": { "name": "Eyal Toledano", diff --git a/package-lock.json b/package-lock.json index 8cedca5ab1..f700ae5363 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "task-master-ai", - "version": "0.42.0", + "version": "0.43.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "task-master-ai", - "version": "0.42.0", + "version": "0.43.0", "license": "MIT WITH Commons-Clause", "workspaces": [ "apps/*", diff --git a/package.json b/package.json index 9231e235c4..e90aa15d1b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "task-master-ai", - "version": "0.42.0", + "version": "0.43.0", "description": "A task management system for ambitious AI-driven development that doesn't overwhelm and confuse Cursor.", "main": "index.js", "type": "module", diff --git a/packages/tm-core/src/modules/reports/managers/complexity-report-manager.ts b/packages/tm-core/src/modules/reports/managers/complexity-report-manager.ts index 13d7c55d28..dc8aa75fd5 100644 --- a/packages/tm-core/src/modules/reports/managers/complexity-report-manager.ts +++ b/packages/tm-core/src/modules/reports/managers/complexity-report-manager.ts @@ -48,7 +48,7 @@ export class ComplexityReportManager { return this.reportCache.get(cacheKey)!; } - const reportPath = this.getReportPath(tag); + const reportPath = this.getReportPath(resolvedTag); try { // Check if file exists diff --git a/packages/tm-core/tests/integration/storage/complexity-enrichment.test.ts b/packages/tm-core/tests/integration/storage/complexity-enrichment.test.ts new file mode 100644 index 0000000000..e22827ff80 --- /dev/null +++ b/packages/tm-core/tests/integration/storage/complexity-enrichment.test.ts @@ -0,0 +1,195 @@ +/** + * @fileoverview Integration test for complexity enrichment in list command + * + * This test reproduces the bug where task complexity is not shown in `list` + * after running `analyze-complexity` with a tag. + * + * Bug: https://github.com/eyaltoledano/claude-task-master/issues/1614 + */ + +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import fs from 'node:fs/promises'; +import path from 'node:path'; +import os from 'node:os'; +import { FileStorage } from '../../../src/modules/storage/adapters/file-storage/file-storage.js'; +import type { Task, ComplexityReport } from '../../../src/common/types/index.js'; + +describe('Complexity Enrichment Integration Test', () => { + let tempDir: string; + let storage: FileStorage; + + beforeEach(async () => { + // Create a temporary directory for testing + tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'tm-complexity-test-')); + + // Initialize storage + storage = new FileStorage(tempDir); + await storage.initialize(); + }); + + afterEach(async () => { + // Cleanup + await storage.close(); + await fs.rm(tempDir, { recursive: true, force: true }); + }); + + it('should enrich tasks with complexity data for tagged tasks', async () => { + const tag = 'foo'; + + // Step 1: Create tasks with the 'foo' tag + const tasks: Task[] = [ + { + id: '1', + title: 'Implement authentication', + description: 'Add JWT-based auth', + status: 'pending', + priority: 'high', + dependencies: [], + subtasks: [], + tags: [tag] + }, + { + id: '2', + title: 'Create API endpoints', + description: 'Build REST API', + status: 'pending', + priority: 'medium', + dependencies: [], + subtasks: [], + tags: [tag] + } + ]; + + await storage.saveTasks(tasks, tag); + + // Step 2: Create a complexity report for the 'foo' tag + const reportsDir = path.join(tempDir, '.taskmaster', 'reports'); + await fs.mkdir(reportsDir, { recursive: true }); + + const complexityReport: ComplexityReport = { + meta: { + generatedAt: new Date().toISOString(), + tasksAnalyzed: 2, + thresholdScore: 5, + usedResearch: false + }, + complexityAnalysis: [ + { + taskId: '1', + taskTitle: 'Implement authentication', + complexityScore: 8, + recommendedSubtasks: 5, + complexityReasoning: 'High complexity due to security concerns', + expansionPrompt: 'Break down auth into smaller tasks' + }, + { + taskId: '2', + taskTitle: 'Create API endpoints', + complexityScore: 6, + recommendedSubtasks: 4, + complexityReasoning: 'Moderate complexity', + expansionPrompt: 'Split by endpoint functionality' + } + ] + }; + + const reportPath = path.join(reportsDir, `task-complexity-report_${tag}.json`); + await fs.writeFile(reportPath, JSON.stringify(complexityReport, null, 2), 'utf-8'); + + // Step 3: Load tasks with the tag - complexity should be enriched + const loadedTasks = await storage.loadTasks(tag); + + // Verify tasks were loaded + expect(loadedTasks).toHaveLength(2); + + // Step 4: Verify complexity data was enriched + const task1 = loadedTasks.find(t => t.id === '1'); + const task2 = loadedTasks.find(t => t.id === '2'); + + expect(task1).toBeDefined(); + expect(task2).toBeDefined(); + + // BUG: These assertions should pass but currently fail + expect(task1!.complexity).toBe(8); + expect(task1!.recommendedSubtasks).toBe(5); + expect(task1!.expansionPrompt).toBe('Break down auth into smaller tasks'); + + expect(task2!.complexity).toBe(6); + expect(task2!.recommendedSubtasks).toBe(4); + expect(task2!.expansionPrompt).toBe('Split by endpoint functionality'); + }); + + it('should handle master tag complexity enrichment', async () => { + // Test with default 'master' tag (no suffix in report filename) + const tasks: Task[] = [ + { + id: '1', + title: 'Setup project', + description: 'Initialize project structure', + status: 'pending', + priority: 'high', + dependencies: [], + subtasks: [], + tags: [] + } + ]; + + await storage.saveTasks(tasks); + + // Create complexity report for master tag (no suffix) + const reportsDir = path.join(tempDir, '.taskmaster', 'reports'); + await fs.mkdir(reportsDir, { recursive: true }); + + const complexityReport: ComplexityReport = { + meta: { + generatedAt: new Date().toISOString(), + tasksAnalyzed: 1, + thresholdScore: 5, + usedResearch: false + }, + complexityAnalysis: [ + { + taskId: '1', + taskTitle: 'Setup project', + complexityScore: 3, + recommendedSubtasks: 2, + complexityReasoning: 'Low complexity', + expansionPrompt: 'Simple setup tasks' + } + ] + }; + + const reportPath = path.join(reportsDir, 'task-complexity-report.json'); + await fs.writeFile(reportPath, JSON.stringify(complexityReport, null, 2), 'utf-8'); + + // Load tasks without specifying tag (defaults to master) + const loadedTasks = await storage.loadTasks(); + + expect(loadedTasks).toHaveLength(1); + expect(loadedTasks[0].complexity).toBe(3); + expect(loadedTasks[0].recommendedSubtasks).toBe(2); + }); + + it('should return tasks without complexity when no report exists', async () => { + const tag = 'no-report'; + const tasks: Task[] = [ + { + id: '1', + title: 'Test task', + description: 'A test', + status: 'pending', + priority: 'medium', + dependencies: [], + subtasks: [], + tags: [tag] + } + ]; + + await storage.saveTasks(tasks, tag); + const loadedTasks = await storage.loadTasks(tag); + + expect(loadedTasks).toHaveLength(1); + expect(loadedTasks[0].complexity).toBeUndefined(); + expect(loadedTasks[0].recommendedSubtasks).toBeUndefined(); + }); +}); diff --git a/taskmaster.mcpb b/taskmaster.mcpb index dadd0c70da..ab290dbfa0 100644 Binary files a/taskmaster.mcpb and b/taskmaster.mcpb differ