Skip to content

Commit 99dbd08

Browse files
Release 0.43.0
2 parents 7a52bd4 + 712a078 commit 99dbd08

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+4588
-236
lines changed

.changeset/add-mcpb-bundle.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
"task-master-ai": minor
3+
---
4+
5+
Add MCPB bundle for single-click Claude Desktop installation
6+
7+
- Added `manifest.json` for MCP Bundle (MCPB) specification v0.3
8+
- Added `.mcpbignore` to exclude development files from bundle
9+
- Added `icon.png` (512x512) for Claude Desktop display
10+
- Enables users to install Task Master MCP server directly in Claude Desktop without manual configuration

.changeset/add-modifyjson-utils.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"task-master-ai": patch
3+
---
4+
5+
Add modifyJSON function for safer file updates

.changeset/cuddly-wings-drop.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
"task-master-ai": minor
3+
---
4+
5+
Add verbose output mode to loop command with `--verbose` flag
6+
7+
- New `-v, --verbose` flag shows Claude's work in real-time (thinking, tool calls) rather than waiting until the iteration completes
8+
- New `--no-output` flag excludes full Claude output from iteration results to save memory
9+
- Improved error handling with proper validation for incompatible options (verbose + sandbox)

.changeset/fair-heads-report.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"task-master-ai": patch
3+
---
4+
5+
Add --no-banner to suppress the startup banner.

.changeset/task-metadata-field.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
"task-master-ai": minor
3+
---
4+
5+
Add optional `metadata` field to tasks for storing user-defined custom data
6+
7+
Tasks and subtasks now support an optional `metadata` field that allows storing arbitrary JSON data such as:
8+
- External IDs (GitHub issues, Jira tickets, Linear issues)
9+
- Workflow data (sprints, story points, custom statuses)
10+
- Integration data (sync timestamps, external system references)
11+
- Custom tracking (UUIDs, version numbers, audit information)
12+
13+
Key features:
14+
- **AI-Safe**: Metadata is preserved through all AI operations (update-task, expand, etc.) because AI schemas intentionally exclude this field
15+
- **Flexible Schema**: Store any JSON-serializable data without schema changes
16+
- **Backward Compatible**: The field is optional; existing tasks work without modification
17+
- **Subtask Support**: Both tasks and subtasks can have their own metadata
18+
- **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)
19+
20+
Example usage:
21+
```json
22+
{
23+
"id": 1,
24+
"title": "Implement authentication",
25+
"metadata": {
26+
"githubIssue": 42,
27+
"sprint": "Q1-S3",
28+
"storyPoints": 5
29+
}
30+
}
31+
```
32+
33+
MCP metadata update example:
34+
```javascript
35+
// With TASK_MASTER_ALLOW_METADATA_UPDATES=true set in MCP env
36+
update_task({
37+
id: "1",
38+
metadata: '{"githubIssue": 42, "sprint": "Q1-S3"}'
39+
})
40+
```
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
#!/usr/bin/env node
2+
import { spawnSync } from 'node:child_process';
3+
import {
4+
existsSync,
5+
readFileSync,
6+
readdirSync,
7+
unlinkSync,
8+
writeFileSync
9+
} from 'node:fs';
10+
import { dirname, join } from 'node:path';
11+
import { fileURLToPath } from 'node:url';
12+
import { findRootDir } from './utils.mjs';
13+
14+
const __filename = fileURLToPath(import.meta.url);
15+
const __dirname = dirname(__filename);
16+
17+
const rootDir = findRootDir(__dirname);
18+
19+
// Read the root package.json version
20+
const pkgPath = join(rootDir, 'package.json');
21+
const manifestPath = join(rootDir, 'manifest.json');
22+
23+
let pkg;
24+
try {
25+
pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
26+
} catch (error) {
27+
console.error('Failed to read package.json:', error.message);
28+
process.exit(1);
29+
}
30+
31+
let manifest;
32+
try {
33+
manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
34+
} catch (error) {
35+
console.error('Failed to read manifest.json:', error.message);
36+
process.exit(1);
37+
}
38+
39+
// Sync manifest version if different
40+
if (manifest.version !== pkg.version) {
41+
console.log(
42+
`Syncing manifest.json version: ${manifest.version}${pkg.version}`
43+
);
44+
manifest.version = pkg.version;
45+
46+
try {
47+
writeFileSync(
48+
manifestPath,
49+
JSON.stringify(manifest, null, '\t') + '\n',
50+
'utf8'
51+
);
52+
console.log(`✅ Updated manifest.json version to ${pkg.version}`);
53+
} catch (error) {
54+
console.error('Failed to write manifest.json:', error.message);
55+
process.exit(1);
56+
}
57+
} else {
58+
console.log(
59+
`✓ manifest.json version already matches package.json (${pkg.version})`
60+
);
61+
}
62+
63+
// Remove old .mcpb files
64+
const files = readdirSync(rootDir);
65+
for (const file of files) {
66+
if (file.endsWith('.mcpb')) {
67+
const filePath = join(rootDir, file);
68+
console.log(`Removing old bundle: ${file}`);
69+
unlinkSync(filePath);
70+
}
71+
}
72+
73+
// Generate new .mcpb bundle
74+
const bundleName = 'taskmaster.mcpb';
75+
console.log(`Generating ${bundleName} for version ${pkg.version}...`);
76+
const result = spawnSync('npx', ['mcpb', 'pack', '.', bundleName], {
77+
cwd: rootDir,
78+
encoding: 'utf8',
79+
stdio: 'inherit'
80+
});
81+
82+
if (result.status !== 0) {
83+
console.error('Failed to generate MCPB bundle');
84+
process.exit(1);
85+
}
86+
87+
// Verify the new bundle was created
88+
if (existsSync(join(rootDir, bundleName))) {
89+
console.log(`✅ Generated ${bundleName}`);
90+
} else {
91+
console.error(`Expected bundle ${bundleName} was not created`);
92+
process.exit(1);
93+
}

.github/workflows/forward-port.yml

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
name: Forward Port to Next
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
8+
concurrency:
9+
group: forward-port
10+
cancel-in-progress: false
11+
12+
permissions:
13+
contents: write
14+
pull-requests: write
15+
16+
jobs:
17+
forward-port:
18+
runs-on: ubuntu-latest
19+
env:
20+
DISCORD_WEBHOOK: ${{ secrets.DISCORD_METRICS_WEBHOOK }}
21+
steps:
22+
- name: Checkout
23+
uses: actions/checkout@v4
24+
with:
25+
fetch-depth: 0
26+
27+
- name: Check for existing PR
28+
id: check-pr
29+
env:
30+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
31+
run: |
32+
EXISTING_PR=$(gh pr list --base next --head main --state open --json number --jq '.[0].number // empty')
33+
if [ -n "$EXISTING_PR" ]; then
34+
echo "existing_pr=$EXISTING_PR" >> $GITHUB_OUTPUT
35+
echo "PR #$EXISTING_PR already exists for main → next"
36+
else
37+
echo "existing_pr=" >> $GITHUB_OUTPUT
38+
echo "No existing PR found"
39+
fi
40+
41+
- name: Check if main has changes not in next
42+
id: check-diff
43+
if: steps.check-pr.outputs.existing_pr == ''
44+
run: |
45+
git fetch origin next
46+
DIFF_COUNT=$(git rev-list --count origin/next..origin/main)
47+
echo "diff_count=$DIFF_COUNT" >> $GITHUB_OUTPUT
48+
if [ "$DIFF_COUNT" -gt 0 ]; then
49+
echo "Found $DIFF_COUNT commit(s) in main not in next"
50+
else
51+
echo "No new commits to forward port"
52+
fi
53+
54+
- name: Check for merge conflicts
55+
id: check-conflicts
56+
if: steps.check-pr.outputs.existing_pr == '' && steps.check-diff.outputs.diff_count != '0'
57+
run: |
58+
git config user.name "github-actions[bot]"
59+
git config user.email "github-actions[bot]@users.noreply.github.com"
60+
61+
# Try a test merge to detect conflicts
62+
git checkout origin/next
63+
if git merge --no-commit --no-ff origin/main 2>/dev/null; then
64+
echo "has_conflicts=false" >> $GITHUB_OUTPUT
65+
echo "No merge conflicts detected"
66+
else
67+
echo "has_conflicts=true" >> $GITHUB_OUTPUT
68+
# Get list of conflicting files
69+
CONFLICTING_FILES=$(git diff --name-only --diff-filter=U | head -10)
70+
echo "conflicting_files<<EOF" >> $GITHUB_OUTPUT
71+
echo "$CONFLICTING_FILES" >> $GITHUB_OUTPUT
72+
echo "EOF" >> $GITHUB_OUTPUT
73+
echo "Merge conflicts detected in: $CONFLICTING_FILES"
74+
fi
75+
git merge --abort 2>/dev/null || true
76+
77+
- name: Create forward-port PR
78+
id: create-pr
79+
if: steps.check-pr.outputs.existing_pr == '' && steps.check-diff.outputs.diff_count != '0'
80+
env:
81+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
82+
run: |
83+
# Get the commits being forward-ported for the PR body
84+
COMMITS=$(git log origin/next..origin/main --oneline --no-decorate | head -20)
85+
COMMIT_COUNT=$(git rev-list --count origin/next..origin/main)
86+
87+
# Build conflict warning if needed
88+
CONFLICT_WARNING=""
89+
if [ "${{ steps.check-conflicts.outputs.has_conflicts }}" = "true" ]; then
90+
CONFLICT_WARNING="
91+
> [!WARNING]
92+
> **Merge conflicts detected.** Manual resolution required.
93+
>
94+
> Conflicting files:
95+
> \`\`\`
96+
> ${{ steps.check-conflicts.outputs.conflicting_files }}
97+
> \`\`\`
98+
99+
### How to resolve
100+
101+
\`\`\`bash
102+
# Option 1: Resolve in a temporary branch (recommended)
103+
git fetch origin
104+
git checkout -b resolve-forward-port origin/next
105+
git merge origin/main
106+
# Fix conflicts in your editor, then:
107+
git add .
108+
git commit
109+
git push origin resolve-forward-port
110+
# Create PR from resolve-forward-port → next, then close this PR
111+
112+
# Option 2: Resolve directly on next
113+
git checkout next
114+
git pull origin next
115+
git merge origin/main
116+
# Fix conflicts, commit, and push
117+
\`\`\`
118+
"
119+
fi
120+
121+
# Create PR body
122+
BODY="## Forward Port: main → next
123+
124+
This PR forward-ports changes from \`main\` to \`next\` to ensure hotfixes and releases are included in the next development branch.
125+
$CONFLICT_WARNING
126+
### Commits ($COMMIT_COUNT total)
127+
\`\`\`
128+
$COMMITS
129+
\`\`\`
130+
$([ "$COMMIT_COUNT" -gt 20 ] && echo "... and $((COMMIT_COUNT - 20)) more")
131+
132+
---
133+
*Auto-generated by forward-port workflow*"
134+
135+
# Create the PR
136+
PR_URL=$(gh pr create \
137+
--base next \
138+
--head main \
139+
--title "chore: forward port main to next" \
140+
--label "forward-port" \
141+
--label "automated" \
142+
--body "$BODY")
143+
144+
PR_NUMBER=$(echo "$PR_URL" | grep -oE '[0-9]+$')
145+
echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
146+
echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT
147+
148+
# Add conflict label if needed
149+
if [ "${{ steps.check-conflicts.outputs.has_conflicts }}" = "true" ]; then
150+
gh pr edit "$PR_NUMBER" --add-label "has-conflicts"
151+
fi
152+
153+
- name: Send Discord notification
154+
if: steps.create-pr.outputs.pr_url != '' && env.DISCORD_WEBHOOK != ''
155+
uses: sarisia/actions-status-discord@v1
156+
with:
157+
webhook: ${{ env.DISCORD_WEBHOOK }}
158+
status: ${{ steps.check-conflicts.outputs.has_conflicts == 'true' && 'Warning' || 'Success' }}
159+
title: "🔄 Forward Port PR Created"
160+
description: |
161+
**main → next**
162+
163+
${{ steps.check-conflicts.outputs.has_conflicts == 'true' && '⚠️ **Merge conflicts detected** - manual resolution required' || '✅ No conflicts - ready for review' }}
164+
165+
**Commits:** ${{ steps.check-diff.outputs.diff_count }}
166+
**PR:** ${{ steps.create-pr.outputs.pr_url }}
167+
color: ${{ steps.check-conflicts.outputs.has_conflicts == 'true' && '0xFFA500' || '0x58AFFF' }}
168+
username: Task Master Bot
169+
avatar_url: https://raw.githubusercontent.com/eyaltoledano/claude-task-master/main/images/logo.png

.mcpbignore

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Exclude everything except manifest and icon
2+
# This is an npx-based bundle - no source code needed
3+
4+
# All source code
5+
*.js
6+
*.ts
7+
*.mjs
8+
*.cjs
9+
*.jsx
10+
*.tsx
11+
*.json
12+
!manifest.json
13+
14+
# All directories
15+
*/
16+
17+
# All other files
18+
*.md
19+
*.txt
20+
*.yml
21+
*.yaml
22+
*.lock
23+
*.log
24+
.git*
25+
.env*
26+
.eslint*
27+
.prettier*
28+
.editorconfig
29+
LICENSE*
30+
Makefile
31+
Dockerfile

0 commit comments

Comments
 (0)