Compare commits
4 Commits
extension@
...
feat/add.c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2bc619be3d | ||
|
|
6dad430347 | ||
|
|
b06d59e487 | ||
|
|
4fa8fe1fa9 |
8
.changeset/fuzzy-words-count.md
Normal file
8
.changeset/fuzzy-words-count.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
"task-master-ai": patch
|
||||
---
|
||||
|
||||
Fix scope-up/down prompts to include all required fields for better AI model compatibility
|
||||
|
||||
- Added missing `priority` field to scope adjustment prompts to prevent validation errors with Claude-code and other models
|
||||
- Ensures generated JSON includes all fields required by the schema
|
||||
9
.changeset/honest-steaks-check.md
Normal file
9
.changeset/honest-steaks-check.md
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
"task-master-ai": minor
|
||||
---
|
||||
|
||||
Enhanced Claude Code provider with codebase-aware task generation
|
||||
|
||||
- Added automatic codebase analysis for Claude Code provider in `parse-prd`, `expand-task`, and `analyze-complexity` commands
|
||||
- When using Claude Code as the AI provider, Task Master now instructs the AI to analyze the project structure, existing implementations, and patterns before generating tasks or subtasks
|
||||
- Tasks and subtasks generated by Claude Code are now informed by actual codebase analysis, resulting in more accurate and contextual outputs
|
||||
13
.changeset/pre.json
Normal file
13
.changeset/pre.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"mode": "exit",
|
||||
"tag": "rc",
|
||||
"initialVersions": {
|
||||
"task-master-ai": "0.23.0",
|
||||
"extension": "0.23.0"
|
||||
},
|
||||
"changesets": [
|
||||
"fuzzy-words-count",
|
||||
"tender-trams-refuse",
|
||||
"vast-sites-leave"
|
||||
]
|
||||
}
|
||||
8
.changeset/tender-trams-refuse.md
Normal file
8
.changeset/tender-trams-refuse.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
"task-master-ai": patch
|
||||
---
|
||||
|
||||
Fix MCP scope-up/down tools not finding tasks
|
||||
|
||||
- Fixed task ID parsing in MCP layer - now correctly converts string IDs to numbers
|
||||
- scope_up_task and scope_down_task MCP tools now work properly
|
||||
11
.changeset/vast-sites-leave.md
Normal file
11
.changeset/vast-sites-leave.md
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
"task-master-ai": patch
|
||||
---
|
||||
|
||||
Improve AI provider compatibility for JSON generation
|
||||
|
||||
- Fixed schema compatibility issues between Perplexity and OpenAI o3 models
|
||||
- Removed nullable/default modifiers from Zod schemas for broader compatibility
|
||||
- Added automatic JSON repair for malformed AI responses (handles cases like missing array values)
|
||||
- Perplexity now uses JSON mode for more reliable structured output
|
||||
- Post-processing handles default values separately from schema validation
|
||||
59
.changeset/wide-actors-report.md
Normal file
59
.changeset/wide-actors-report.md
Normal file
@@ -0,0 +1,59 @@
|
||||
---
|
||||
"task-master-ai": minor
|
||||
---
|
||||
|
||||
Add Claude Code subagent support with task-orchestrator, task-executor, and task-checker
|
||||
|
||||
## New Claude Code Agents
|
||||
|
||||
Added specialized agents for Claude Code users to enable parallel task execution, intelligent task orchestration, and quality assurance:
|
||||
|
||||
### task-orchestrator
|
||||
Coordinates and manages the execution of Task Master tasks with intelligent dependency analysis:
|
||||
- Analyzes task dependencies to identify parallelizable work
|
||||
- Deploys multiple task-executor agents for concurrent execution
|
||||
- Monitors task completion and updates the dependency graph
|
||||
- Automatically identifies and starts newly unblocked tasks
|
||||
|
||||
### task-executor
|
||||
Handles the actual implementation of individual tasks:
|
||||
- Executes specific tasks identified by the orchestrator
|
||||
- Works on concrete implementation rather than planning
|
||||
- Updates task status and logs progress
|
||||
- Can work in parallel with other executors on independent tasks
|
||||
|
||||
### task-checker
|
||||
Verifies that completed tasks meet their specifications:
|
||||
- Reviews tasks marked as 'review' status
|
||||
- Validates implementation against requirements
|
||||
- Runs tests and checks for best practices
|
||||
- Ensures quality before marking tasks as 'done'
|
||||
|
||||
## Installation
|
||||
|
||||
When using the Claude profile (`task-master rules add claude`), the agents are automatically installed to `.claude/agents/` directory.
|
||||
|
||||
## Usage Example
|
||||
|
||||
```bash
|
||||
# In Claude Code, after initializing a project with tasks:
|
||||
|
||||
# Use task-orchestrator to analyze and coordinate work
|
||||
# The orchestrator will:
|
||||
# 1. Check task dependencies
|
||||
# 2. Identify tasks that can run in parallel
|
||||
# 3. Deploy executors for available work
|
||||
# 4. Monitor progress and deploy new executors as tasks complete
|
||||
|
||||
# Use task-executor for specific task implementation
|
||||
# When the orchestrator identifies task 2.3 needs work:
|
||||
# The executor will implement that specific task
|
||||
```
|
||||
|
||||
## Benefits
|
||||
|
||||
- **Parallel Execution**: Multiple independent tasks can be worked on simultaneously
|
||||
- **Intelligent Scheduling**: Orchestrator understands dependencies and optimizes execution order
|
||||
- **Separation of Concerns**: Planning (orchestrator) is separated from execution (executor)
|
||||
- **Progress Tracking**: Real-time updates as tasks are completed
|
||||
- **Automatic Progression**: As tasks complete, newly unblocked tasks are automatically started
|
||||
102
.github/scripts/check-pre-release-mode.mjs
vendored
102
.github/scripts/check-pre-release-mode.mjs
vendored
@@ -1,102 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
import { readFileSync, existsSync } from 'node:fs';
|
||||
import { join, dirname, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
// Get context from command line argument or environment
|
||||
const context = process.argv[2] || process.env.GITHUB_WORKFLOW || 'manual';
|
||||
|
||||
function findRootDir(startDir) {
|
||||
let currentDir = resolve(startDir);
|
||||
while (currentDir !== '/') {
|
||||
if (existsSync(join(currentDir, 'package.json'))) {
|
||||
try {
|
||||
const pkg = JSON.parse(
|
||||
readFileSync(join(currentDir, 'package.json'), 'utf8')
|
||||
);
|
||||
if (pkg.name === 'task-master-ai' || pkg.repository) {
|
||||
return currentDir;
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
currentDir = dirname(currentDir);
|
||||
}
|
||||
throw new Error('Could not find root directory');
|
||||
}
|
||||
|
||||
function checkPreReleaseMode() {
|
||||
console.log('🔍 Checking if branch is in pre-release mode...');
|
||||
|
||||
const rootDir = findRootDir(__dirname);
|
||||
const preJsonPath = join(rootDir, '.changeset', 'pre.json');
|
||||
|
||||
// Check if pre.json exists
|
||||
if (!existsSync(preJsonPath)) {
|
||||
console.log('✅ Not in active pre-release mode - safe to proceed');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
try {
|
||||
// Read and parse pre.json
|
||||
const preJsonContent = readFileSync(preJsonPath, 'utf8');
|
||||
const preJson = JSON.parse(preJsonContent);
|
||||
|
||||
// Check if we're in active pre-release mode
|
||||
if (preJson.mode === 'pre') {
|
||||
console.error('❌ ERROR: This branch is in active pre-release mode!');
|
||||
console.error('');
|
||||
|
||||
// Provide context-specific error messages
|
||||
if (context === 'Release Check' || context === 'pull_request') {
|
||||
console.error(
|
||||
'Pre-release mode must be exited before merging to main.'
|
||||
);
|
||||
console.error('');
|
||||
console.error(
|
||||
'To fix this, run the following commands in your branch:'
|
||||
);
|
||||
console.error(' npx changeset pre exit');
|
||||
console.error(' git add -u');
|
||||
console.error(' git commit -m "chore: exit pre-release mode"');
|
||||
console.error(' git push');
|
||||
console.error('');
|
||||
console.error('Then update this pull request.');
|
||||
} else if (context === 'Release' || context === 'main') {
|
||||
console.error(
|
||||
'Pre-release mode should only be used on feature branches, not main.'
|
||||
);
|
||||
console.error('');
|
||||
console.error('To fix this, run the following commands locally:');
|
||||
console.error(' npx changeset pre exit');
|
||||
console.error(' git add -u');
|
||||
console.error(' git commit -m "chore: exit pre-release mode"');
|
||||
console.error(' git push origin main');
|
||||
console.error('');
|
||||
console.error('Then re-run this workflow.');
|
||||
} else {
|
||||
console.error('Pre-release mode must be exited before proceeding.');
|
||||
console.error('');
|
||||
console.error('To fix this, run the following commands:');
|
||||
console.error(' npx changeset pre exit');
|
||||
console.error(' git add -u');
|
||||
console.error(' git commit -m "chore: exit pre-release mode"');
|
||||
console.error(' git push');
|
||||
}
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log('✅ Not in active pre-release mode - safe to proceed');
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
console.error(`❌ ERROR: Unable to parse .changeset/pre.json – aborting.`);
|
||||
console.error(`Error details: ${error.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Run the check
|
||||
checkPreReleaseMode();
|
||||
54
.github/scripts/pre-release.mjs
vendored
54
.github/scripts/pre-release.mjs
vendored
@@ -1,54 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
import { readFileSync, existsSync } from 'node:fs';
|
||||
import { join, dirname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import {
|
||||
findRootDir,
|
||||
runCommand,
|
||||
getPackageVersion,
|
||||
createAndPushTag
|
||||
} from './utils.mjs';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
const rootDir = findRootDir(__dirname);
|
||||
const extensionPkgPath = join(rootDir, 'apps', 'extension', 'package.json');
|
||||
|
||||
console.log('🚀 Starting pre-release process...');
|
||||
|
||||
// Check if we're in RC mode
|
||||
const preJsonPath = join(rootDir, '.changeset', 'pre.json');
|
||||
if (!existsSync(preJsonPath)) {
|
||||
console.error('⚠️ Not in RC mode. Run "npx changeset pre enter rc" first.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
const preJson = JSON.parse(readFileSync(preJsonPath, 'utf8'));
|
||||
if (preJson.tag !== 'rc') {
|
||||
console.error(`⚠️ Not in RC mode. Current tag: ${preJson.tag}`);
|
||||
process.exit(1);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to read pre.json:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Get current extension version
|
||||
const extensionVersion = getPackageVersion(extensionPkgPath);
|
||||
console.log(`Extension version: ${extensionVersion}`);
|
||||
|
||||
// Run changeset publish for npm packages
|
||||
console.log('📦 Publishing npm packages...');
|
||||
runCommand('npx', ['changeset', 'publish']);
|
||||
|
||||
// Create tag for extension pre-release if it doesn't exist
|
||||
const extensionTag = `extension-rc@${extensionVersion}`;
|
||||
const tagCreated = createAndPushTag(extensionTag);
|
||||
|
||||
if (tagCreated) {
|
||||
console.log('This will trigger the extension-pre-release workflow...');
|
||||
}
|
||||
|
||||
console.log('✅ Pre-release process completed!');
|
||||
30
.github/scripts/release.mjs
vendored
30
.github/scripts/release.mjs
vendored
@@ -1,30 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
import { existsSync, unlinkSync } from 'node:fs';
|
||||
import { join, dirname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { findRootDir, runCommand } from './utils.mjs';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
const rootDir = findRootDir(__dirname);
|
||||
|
||||
console.log('🚀 Starting release process...');
|
||||
|
||||
// Double-check we're not in pre-release mode (safety net)
|
||||
const preJsonPath = join(rootDir, '.changeset', 'pre.json');
|
||||
if (existsSync(preJsonPath)) {
|
||||
console.log('⚠️ Warning: pre.json still exists. Removing it...');
|
||||
unlinkSync(preJsonPath);
|
||||
}
|
||||
|
||||
// Check if the extension version has changed and tag it
|
||||
// This prevents changeset from trying to publish the private package
|
||||
runCommand('node', [join(__dirname, 'tag-extension.mjs')]);
|
||||
|
||||
// Run changeset publish for npm packages
|
||||
runCommand('npx', ['changeset', 'publish']);
|
||||
|
||||
console.log('✅ Release process completed!');
|
||||
|
||||
// The extension tag (if created) will trigger the extension-release workflow
|
||||
21
.github/scripts/release.sh
vendored
Executable file
21
.github/scripts/release.sh
vendored
Executable file
@@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "🚀 Starting release process..."
|
||||
|
||||
# Double-check we're not in pre-release mode (safety net)
|
||||
if [ -f .changeset/pre.json ]; then
|
||||
echo "⚠️ Warning: pre.json still exists. Removing it..."
|
||||
rm -f .changeset/pre.json
|
||||
fi
|
||||
|
||||
# Check if the extension version has changed and tag it
|
||||
# This prevents changeset from trying to publish the private package
|
||||
node .github/scripts/tag-extension.mjs
|
||||
|
||||
# Run changeset publish for npm packages
|
||||
npx changeset publish
|
||||
|
||||
echo "✅ Release process completed!"
|
||||
|
||||
# The extension tag (if created) will trigger the extension-release workflow
|
||||
114
.github/scripts/tag-extension.mjs
vendored
Executable file → Normal file
114
.github/scripts/tag-extension.mjs
vendored
Executable file → Normal file
@@ -1,13 +1,33 @@
|
||||
#!/usr/bin/env node
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { join, dirname } from 'node:path';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { readFileSync, existsSync } from 'node:fs';
|
||||
import { join, dirname, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { findRootDir, createAndPushTag } from './utils.mjs';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
// Find the root directory by looking for package.json
|
||||
function findRootDir(startDir) {
|
||||
let currentDir = resolve(startDir);
|
||||
while (currentDir !== '/') {
|
||||
if (existsSync(join(currentDir, 'package.json'))) {
|
||||
// Verify it's the root package.json by checking for expected fields
|
||||
try {
|
||||
const pkg = JSON.parse(
|
||||
readFileSync(join(currentDir, 'package.json'), 'utf8')
|
||||
);
|
||||
if (pkg.name === 'task-master-ai' || pkg.repository) {
|
||||
return currentDir;
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
currentDir = dirname(currentDir);
|
||||
}
|
||||
throw new Error('Could not find root directory');
|
||||
}
|
||||
|
||||
const rootDir = findRootDir(__dirname);
|
||||
|
||||
// Read the extension's package.json
|
||||
@@ -23,11 +43,95 @@ try {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Read root package.json for repository info
|
||||
const rootPkgPath = join(rootDir, 'package.json');
|
||||
let rootPkg;
|
||||
try {
|
||||
const rootPkgContent = readFileSync(rootPkgPath, 'utf8');
|
||||
rootPkg = JSON.parse(rootPkgContent);
|
||||
} catch (error) {
|
||||
console.error('Failed to read root package.json:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Ensure we have required fields
|
||||
assert(pkg.name, 'package.json must have a name field');
|
||||
assert(pkg.version, 'package.json must have a version field');
|
||||
assert(rootPkg.repository, 'root package.json must have a repository field');
|
||||
|
||||
const tag = `${pkg.name}@${pkg.version}`;
|
||||
|
||||
// Create and push the tag if it doesn't exist
|
||||
createAndPushTag(tag);
|
||||
// Get repository URL from root package.json
|
||||
// Get repository URL and clean it up for git ls-remote
|
||||
let repoUrl = rootPkg.repository.url || rootPkg.repository;
|
||||
if (typeof repoUrl === 'string') {
|
||||
// Convert git+https://github.com/... to https://github.com/...
|
||||
repoUrl = repoUrl.replace(/^git\+/, '');
|
||||
// Ensure it ends with .git for proper remote access
|
||||
if (!repoUrl.endsWith('.git')) {
|
||||
repoUrl += '.git';
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Checking remote repository: ${repoUrl} for tag: ${tag}`);
|
||||
|
||||
let gitResult = spawnSync('git', ['ls-remote', repoUrl, tag], {
|
||||
encoding: 'utf8',
|
||||
env: { ...process.env }
|
||||
});
|
||||
|
||||
if (gitResult.status !== 0) {
|
||||
console.error('Git ls-remote failed:');
|
||||
console.error('Exit code:', gitResult.status);
|
||||
console.error('Error:', gitResult.error);
|
||||
console.error('Stderr:', gitResult.stderr);
|
||||
console.error('Command:', `git ls-remote ${repoUrl} ${tag}`);
|
||||
|
||||
// For CI environments, try using origin instead of the full URL
|
||||
if (process.env.CI) {
|
||||
console.log('Retrying with origin remote...');
|
||||
gitResult = spawnSync('git', ['ls-remote', 'origin', tag], {
|
||||
encoding: 'utf8'
|
||||
});
|
||||
|
||||
if (gitResult.status !== 0) {
|
||||
throw new Error(
|
||||
`Failed to check remote for tag ${tag}. Exit code: ${gitResult.status}`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw new Error(
|
||||
`Failed to check remote for tag ${tag}. Exit code: ${gitResult.status}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const exists = String(gitResult.stdout).trim() !== '';
|
||||
|
||||
if (!exists) {
|
||||
console.log(`Creating new extension tag: ${tag}`);
|
||||
|
||||
// Create the tag
|
||||
const tagResult = spawnSync('git', ['tag', tag]);
|
||||
if (tagResult.status !== 0) {
|
||||
console.error(
|
||||
'Failed to create tag:',
|
||||
tagResult.error || tagResult.stderr.toString()
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Push the tag
|
||||
const pushResult = spawnSync('git', ['push', 'origin', tag]);
|
||||
if (pushResult.status !== 0) {
|
||||
console.error(
|
||||
'Failed to push tag:',
|
||||
pushResult.error || pushResult.stderr.toString()
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`✅ Successfully created and pushed tag: ${tag}`);
|
||||
} else {
|
||||
console.log(`Extension tag already exists: ${tag}`);
|
||||
}
|
||||
|
||||
88
.github/scripts/utils.mjs
vendored
88
.github/scripts/utils.mjs
vendored
@@ -1,88 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { join, dirname, resolve } from 'node:path';
|
||||
|
||||
// Find the root directory by looking for package.json with task-master-ai
|
||||
export function findRootDir(startDir) {
|
||||
let currentDir = resolve(startDir);
|
||||
while (currentDir !== '/') {
|
||||
const pkgPath = join(currentDir, 'package.json');
|
||||
try {
|
||||
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
||||
if (pkg.name === 'task-master-ai' || pkg.repository) {
|
||||
return currentDir;
|
||||
}
|
||||
} catch {}
|
||||
currentDir = dirname(currentDir);
|
||||
}
|
||||
throw new Error('Could not find root directory');
|
||||
}
|
||||
|
||||
// Run a command with proper error handling
|
||||
export function runCommand(command, args = [], options = {}) {
|
||||
console.log(`Running: ${command} ${args.join(' ')}`);
|
||||
const result = spawnSync(command, args, {
|
||||
encoding: 'utf8',
|
||||
stdio: 'inherit',
|
||||
...options
|
||||
});
|
||||
|
||||
if (result.status !== 0) {
|
||||
console.error(`Command failed with exit code ${result.status}`);
|
||||
process.exit(result.status);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Get package version from a package.json file
|
||||
export function getPackageVersion(packagePath) {
|
||||
try {
|
||||
const pkg = JSON.parse(readFileSync(packagePath, 'utf8'));
|
||||
return pkg.version;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Failed to read package version from ${packagePath}:`,
|
||||
error.message
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a git tag exists on remote
|
||||
export function tagExistsOnRemote(tag, remote = 'origin') {
|
||||
const result = spawnSync('git', ['ls-remote', remote, tag], {
|
||||
encoding: 'utf8'
|
||||
});
|
||||
|
||||
return result.status === 0 && result.stdout.trim() !== '';
|
||||
}
|
||||
|
||||
// Create and push a git tag if it doesn't exist
|
||||
export function createAndPushTag(tag, remote = 'origin') {
|
||||
// Check if tag already exists
|
||||
if (tagExistsOnRemote(tag, remote)) {
|
||||
console.log(`Tag ${tag} already exists on remote, skipping`);
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log(`Creating new tag: ${tag}`);
|
||||
|
||||
// Create the tag locally
|
||||
const tagResult = spawnSync('git', ['tag', tag]);
|
||||
if (tagResult.status !== 0) {
|
||||
console.error('Failed to create tag:', tagResult.error || tagResult.stderr);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Push the tag to remote
|
||||
const pushResult = spawnSync('git', ['push', remote, tag]);
|
||||
if (pushResult.status !== 0) {
|
||||
console.error('Failed to push tag:', pushResult.error || pushResult.stderr);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`✅ Successfully created and pushed tag: ${tag}`);
|
||||
return true;
|
||||
}
|
||||
110
.github/workflows/extension-pre-release.yml
vendored
110
.github/workflows/extension-pre-release.yml
vendored
@@ -1,110 +0,0 @@
|
||||
name: Extension Pre-Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "extension-rc@*"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
concurrency: extension-pre-release-${{ github.ref }}
|
||||
|
||||
jobs:
|
||||
publish-extension-rc:
|
||||
runs-on: ubuntu-latest
|
||||
environment: extension-release
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Cache node_modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
node_modules
|
||||
*/*/node_modules
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-
|
||||
|
||||
- name: Install Extension Dependencies
|
||||
working-directory: apps/extension
|
||||
run: npm ci
|
||||
timeout-minutes: 5
|
||||
|
||||
- name: Type Check Extension
|
||||
working-directory: apps/extension
|
||||
run: npm run check-types
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
|
||||
- name: Build Extension
|
||||
working-directory: apps/extension
|
||||
run: npm run build
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
|
||||
- name: Package Extension
|
||||
working-directory: apps/extension
|
||||
run: npm run package
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
|
||||
- name: Create VSIX Package (Pre-Release)
|
||||
working-directory: apps/extension/vsix-build
|
||||
run: npx vsce package --no-dependencies --pre-release
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
|
||||
- name: Get VSIX filename
|
||||
id: vsix-info
|
||||
working-directory: apps/extension/vsix-build
|
||||
run: |
|
||||
VSIX_FILE=$(find . -maxdepth 1 -name "*.vsix" -type f | head -n1 | xargs basename)
|
||||
if [ -z "$VSIX_FILE" ]; then
|
||||
echo "Error: No VSIX file found"
|
||||
exit 1
|
||||
fi
|
||||
echo "vsix-filename=$VSIX_FILE" >> "$GITHUB_OUTPUT"
|
||||
echo "Found VSIX: $VSIX_FILE"
|
||||
|
||||
- name: Publish to VS Code Marketplace (Pre-Release)
|
||||
working-directory: apps/extension/vsix-build
|
||||
run: npx vsce publish --packagePath "${{ steps.vsix-info.outputs.vsix-filename }}" --pre-release
|
||||
env:
|
||||
VSCE_PAT: ${{ secrets.VSCE_PAT }}
|
||||
FORCE_COLOR: 1
|
||||
|
||||
- name: Install Open VSX CLI
|
||||
run: npm install -g ovsx
|
||||
|
||||
- name: Publish to Open VSX Registry (Pre-Release)
|
||||
working-directory: apps/extension/vsix-build
|
||||
run: ovsx publish "${{ steps.vsix-info.outputs.vsix-filename }}" --pre-release
|
||||
env:
|
||||
OVSX_PAT: ${{ secrets.OVSX_PAT }}
|
||||
FORCE_COLOR: 1
|
||||
|
||||
- name: Upload Build Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: extension-pre-release-${{ github.ref_name }}
|
||||
path: |
|
||||
apps/extension/vsix-build/*.vsix
|
||||
apps/extension/dist/
|
||||
retention-days: 30
|
||||
|
||||
notify-success:
|
||||
needs: publish-extension-rc
|
||||
if: success()
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Success Notification
|
||||
run: |
|
||||
echo "🚀 Extension ${{ github.ref_name }} successfully published as pre-release!"
|
||||
echo "📦 Available on VS Code Marketplace (Pre-Release)"
|
||||
echo "🌍 Available on Open VSX Registry (Pre-Release)"
|
||||
26
.github/workflows/extension-release.yml
vendored
26
.github/workflows/extension-release.yml
vendored
@@ -89,6 +89,32 @@ jobs:
|
||||
OVSX_PAT: ${{ secrets.OVSX_PAT }}
|
||||
FORCE_COLOR: 1
|
||||
|
||||
- name: Create GitHub Release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref_name }}
|
||||
release_name: Extension ${{ github.ref_name }}
|
||||
body: |
|
||||
VS Code Extension Release ${{ github.ref_name }}
|
||||
|
||||
**Marketplaces:**
|
||||
- [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=Hamster.task-master-hamster)
|
||||
- [Open VSX Registry](https://open-vsx.org/extension/Hamster/task-master-hamster)
|
||||
draft: false
|
||||
prerelease: false
|
||||
|
||||
- name: Upload VSIX to Release
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: apps/extension/vsix-build/${{ steps.vsix-info.outputs.vsix-filename }}
|
||||
asset_name: ${{ steps.vsix-info.outputs.vsix-filename }}
|
||||
asset_content_type: application/zip
|
||||
|
||||
- name: Upload Build Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
|
||||
28
.github/workflows/pre-release.yml
vendored
28
.github/workflows/pre-release.yml
vendored
@@ -9,7 +9,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
# Only allow pre-releases on non-main branches
|
||||
if: github.ref != 'refs/heads/main'
|
||||
environment: extension-release
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -36,26 +35,9 @@ jobs:
|
||||
|
||||
- name: Enter RC mode (if not already in RC mode)
|
||||
run: |
|
||||
# Check if we're in pre-release mode with the "rc" tag
|
||||
if [ -f .changeset/pre.json ]; then
|
||||
MODE=$(jq -r '.mode' .changeset/pre.json 2>/dev/null || echo '')
|
||||
TAG=$(jq -r '.tag' .changeset/pre.json 2>/dev/null || echo '')
|
||||
|
||||
if [ "$MODE" = "exit" ]; then
|
||||
echo "Pre-release mode is in 'exit' state, re-entering RC mode..."
|
||||
npx changeset pre enter rc
|
||||
elif [ "$MODE" = "pre" ] && [ "$TAG" != "rc" ]; then
|
||||
echo "In pre-release mode but with wrong tag ($TAG), switching to RC..."
|
||||
npx changeset pre exit
|
||||
npx changeset pre enter rc
|
||||
elif [ "$MODE" = "pre" ] && [ "$TAG" = "rc" ]; then
|
||||
echo "Already in RC pre-release mode"
|
||||
else
|
||||
echo "Unknown mode state: $MODE, entering RC mode..."
|
||||
npx changeset pre enter rc
|
||||
fi
|
||||
else
|
||||
echo "No pre.json found, entering RC mode..."
|
||||
# ensure we’re in the right pre-mode (tag "rc")
|
||||
if [ ! -f .changeset/pre.json ] \
|
||||
|| [ "$(jq -r '.tag' .changeset/pre.json 2>/dev/null || echo '')" != "rc" ]; then
|
||||
npx changeset pre enter rc
|
||||
fi
|
||||
|
||||
@@ -68,12 +50,10 @@ jobs:
|
||||
- name: Create Release Candidate Pull Request or Publish Release Candidate to npm
|
||||
uses: changesets/action@v1
|
||||
with:
|
||||
publish: node ./.github/scripts/pre-release.mjs
|
||||
publish: npm run release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
VSCE_PAT: ${{ secrets.VSCE_PAT }}
|
||||
OVSX_PAT: ${{ secrets.OVSX_PAT }}
|
||||
|
||||
- name: Commit & Push changes
|
||||
uses: actions-js/push@master
|
||||
|
||||
27
.github/workflows/release-check.yml
vendored
27
.github/workflows/release-check.yml
vendored
@@ -18,4 +18,29 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check release mode
|
||||
run: node ./.github/scripts/check-pre-release-mode.mjs "pull_request"
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "🔍 Checking if branch is in pre-release mode..."
|
||||
|
||||
if [[ -f .changeset/pre.json ]]; then
|
||||
if ! PRE_MODE=$(jq -r '.mode' .changeset/pre.json 2>/dev/null); then
|
||||
echo "❌ ERROR: Unable to parse .changeset/pre.json – aborting merge."
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$PRE_MODE" == "pre" ]]; then
|
||||
echo "❌ ERROR: This branch is in active pre-release mode!"
|
||||
echo ""
|
||||
echo "Pre-release mode must be exited before merging to main."
|
||||
echo ""
|
||||
echo "To fix this, run the following commands in your branch:"
|
||||
echo " npx changeset pre exit"
|
||||
echo " git add -u"
|
||||
echo " git commit -m 'chore: exit pre-release mode'"
|
||||
echo " git push"
|
||||
echo ""
|
||||
echo "Then update this pull request."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "✅ Not in active pre-release mode - PR can be merged"
|
||||
|
||||
22
.github/workflows/release.yml
vendored
22
.github/workflows/release.yml
vendored
@@ -39,12 +39,30 @@ jobs:
|
||||
timeout-minutes: 2
|
||||
|
||||
- name: Check pre-release mode
|
||||
run: node ./.github/scripts/check-pre-release-mode.mjs "main"
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "🔍 Checking pre-release mode status..."
|
||||
if [[ -f .changeset/pre.json ]]; then
|
||||
echo "❌ ERROR: Main branch is in pre-release mode!"
|
||||
echo ""
|
||||
echo "Pre-release mode should only be used on feature branches, not main."
|
||||
echo ""
|
||||
echo "To fix this, run the following commands locally:"
|
||||
echo " npx changeset pre exit"
|
||||
echo " git add -u"
|
||||
echo " git commit -m 'chore: exit pre-release mode'"
|
||||
echo " git push origin main"
|
||||
echo ""
|
||||
echo "Then re-run this workflow."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Not in pre-release mode - proceeding with release"
|
||||
|
||||
- name: Create Release Pull Request or Publish to npm
|
||||
uses: changesets/action@v1
|
||||
with:
|
||||
publish: node ./.github/scripts/release.mjs
|
||||
publish: ./.github/scripts/release.sh
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
171
CHANGELOG.md
171
CHANGELOG.md
@@ -1,176 +1,5 @@
|
||||
# task-master-ai
|
||||
|
||||
## 0.24.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#1098](https://github.com/eyaltoledano/claude-task-master/pull/1098) [`36468f3`](https://github.com/eyaltoledano/claude-task-master/commit/36468f3c93faf4035a5c442ccbc501077f3440f1) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Enhanced Claude Code provider with codebase-aware task generation
|
||||
- Added automatic codebase analysis for Claude Code provider in `parse-prd`, `expand-task`, and `analyze-complexity` commands
|
||||
- When using Claude Code as the AI provider, Task Master now instructs the AI to analyze the project structure, existing implementations, and patterns before generating tasks or subtasks
|
||||
- Tasks and subtasks generated by Claude Code are now informed by actual codebase analysis, resulting in more accurate and contextual outputs
|
||||
|
||||
- [#1105](https://github.com/eyaltoledano/claude-task-master/pull/1105) [`75c514c`](https://github.com/eyaltoledano/claude-task-master/commit/75c514cf5b2ca47f95c0ad7fa92654a4f2a6be4b) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Add GPT-5 support with proper parameter handling
|
||||
- Added GPT-5 model to supported models configuration with SWE score of 0.749
|
||||
|
||||
- [#1091](https://github.com/eyaltoledano/claude-task-master/pull/1091) [`4bb6370`](https://github.com/eyaltoledano/claude-task-master/commit/4bb63706b80c28d1b2d782ba868a725326f916c7) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Add Claude Code subagent support with task-orchestrator, task-executor, and task-checker
|
||||
|
||||
## New Claude Code Agents
|
||||
|
||||
Added specialized agents for Claude Code users to enable parallel task execution, intelligent task orchestration, and quality assurance:
|
||||
|
||||
### task-orchestrator
|
||||
|
||||
Coordinates and manages the execution of Task Master tasks with intelligent dependency analysis:
|
||||
- Analyzes task dependencies to identify parallelizable work
|
||||
- Deploys multiple task-executor agents for concurrent execution
|
||||
- Monitors task completion and updates the dependency graph
|
||||
- Automatically identifies and starts newly unblocked tasks
|
||||
|
||||
### task-executor
|
||||
|
||||
Handles the actual implementation of individual tasks:
|
||||
- Executes specific tasks identified by the orchestrator
|
||||
- Works on concrete implementation rather than planning
|
||||
- Updates task status and logs progress
|
||||
- Can work in parallel with other executors on independent tasks
|
||||
|
||||
### task-checker
|
||||
|
||||
Verifies that completed tasks meet their specifications:
|
||||
- Reviews tasks marked as 'review' status
|
||||
- Validates implementation against requirements
|
||||
- Runs tests and checks for best practices
|
||||
- Ensures quality before marking tasks as 'done'
|
||||
|
||||
## Installation
|
||||
|
||||
When using the Claude profile (`task-master rules add claude`), the agents are automatically installed to `.claude/agents/` directory.
|
||||
|
||||
## Usage Example
|
||||
|
||||
```bash
|
||||
# In Claude Code, after initializing a project with tasks:
|
||||
|
||||
# Use task-orchestrator to analyze and coordinate work
|
||||
# The orchestrator will:
|
||||
# 1. Check task dependencies
|
||||
# 2. Identify tasks that can run in parallel
|
||||
# 3. Deploy executors for available work
|
||||
# 4. Monitor progress and deploy new executors as tasks complete
|
||||
|
||||
# Use task-executor for specific task implementation
|
||||
# When the orchestrator identifies task 2.3 needs work:
|
||||
# The executor will implement that specific task
|
||||
```
|
||||
|
||||
## Benefits
|
||||
- **Parallel Execution**: Multiple independent tasks can be worked on simultaneously
|
||||
- **Intelligent Scheduling**: Orchestrator understands dependencies and optimizes execution order
|
||||
- **Separation of Concerns**: Planning (orchestrator) is separated from execution (executor)
|
||||
- **Progress Tracking**: Real-time updates as tasks are completed
|
||||
- **Automatic Progression**: As tasks complete, newly unblocked tasks are automatically started
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1094](https://github.com/eyaltoledano/claude-task-master/pull/1094) [`4357af3`](https://github.com/eyaltoledano/claude-task-master/commit/4357af3f13859d90bca8795215e5d5f1d94abde5) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Fix expand task generating unrelated generic subtasks
|
||||
|
||||
Fixed an issue where `task-master expand` would generate generic authentication-related subtasks regardless of the parent task context when using complexity reports. The expansion now properly includes the parent task details alongside any expansion guidance.
|
||||
|
||||
- [#1079](https://github.com/eyaltoledano/claude-task-master/pull/1079) [`e495b2b`](https://github.com/eyaltoledano/claude-task-master/commit/e495b2b55950ee54c7d0f1817d8530e28bd79c05) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Fix scope-up/down prompts to include all required fields for better AI model compatibility
|
||||
- Added missing `priority` field to scope adjustment prompts to prevent validation errors with Claude-code and other models
|
||||
- Ensures generated JSON includes all fields required by the schema
|
||||
|
||||
- [#1079](https://github.com/eyaltoledano/claude-task-master/pull/1079) [`e495b2b`](https://github.com/eyaltoledano/claude-task-master/commit/e495b2b55950ee54c7d0f1817d8530e28bd79c05) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Fix MCP scope-up/down tools not finding tasks
|
||||
- Fixed task ID parsing in MCP layer - now correctly converts string IDs to numbers
|
||||
- scope_up_task and scope_down_task MCP tools now work properly
|
||||
|
||||
- [#1079](https://github.com/eyaltoledano/claude-task-master/pull/1079) [`e495b2b`](https://github.com/eyaltoledano/claude-task-master/commit/e495b2b55950ee54c7d0f1817d8530e28bd79c05) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Improve AI provider compatibility for JSON generation
|
||||
- Fixed schema compatibility issues between Perplexity and OpenAI o3 models
|
||||
- Removed nullable/default modifiers from Zod schemas for broader compatibility
|
||||
- Added automatic JSON repair for malformed AI responses (handles cases like missing array values)
|
||||
- Perplexity now uses JSON mode for more reliable structured output
|
||||
- Post-processing handles default values separately from schema validation
|
||||
|
||||
## 0.24.0-rc.2
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#1105](https://github.com/eyaltoledano/claude-task-master/pull/1105) [`75c514c`](https://github.com/eyaltoledano/claude-task-master/commit/75c514cf5b2ca47f95c0ad7fa92654a4f2a6be4b) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Add GPT-5 support with proper parameter handling
|
||||
- Added GPT-5 model to supported models configuration with SWE score of 0.749
|
||||
|
||||
## 0.24.0-rc.1
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#1093](https://github.com/eyaltoledano/claude-task-master/pull/1093) [`36468f3`](https://github.com/eyaltoledano/claude-task-master/commit/36468f3c93faf4035a5c442ccbc501077f3440f1) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Enhanced Claude Code provider with codebase-aware task generation
|
||||
- Added automatic codebase analysis for Claude Code provider in `parse-prd`, `expand-task`, and `analyze-complexity` commands
|
||||
- When using Claude Code as the AI provider, Task Master now instructs the AI to analyze the project structure, existing implementations, and patterns before generating tasks or subtasks
|
||||
- Tasks and subtasks generated by Claude Code are now informed by actual codebase analysis, resulting in more accurate and contextual outputs
|
||||
|
||||
- [#1091](https://github.com/eyaltoledano/claude-task-master/pull/1091) [`4bb6370`](https://github.com/eyaltoledano/claude-task-master/commit/4bb63706b80c28d1b2d782ba868a725326f916c7) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Add Claude Code subagent support with task-orchestrator, task-executor, and task-checker
|
||||
|
||||
## New Claude Code Agents
|
||||
|
||||
Added specialized agents for Claude Code users to enable parallel task execution, intelligent task orchestration, and quality assurance:
|
||||
|
||||
### task-orchestrator
|
||||
|
||||
Coordinates and manages the execution of Task Master tasks with intelligent dependency analysis:
|
||||
- Analyzes task dependencies to identify parallelizable work
|
||||
- Deploys multiple task-executor agents for concurrent execution
|
||||
- Monitors task completion and updates the dependency graph
|
||||
- Automatically identifies and starts newly unblocked tasks
|
||||
|
||||
### task-executor
|
||||
|
||||
Handles the actual implementation of individual tasks:
|
||||
- Executes specific tasks identified by the orchestrator
|
||||
- Works on concrete implementation rather than planning
|
||||
- Updates task status and logs progress
|
||||
- Can work in parallel with other executors on independent tasks
|
||||
|
||||
### task-checker
|
||||
|
||||
Verifies that completed tasks meet their specifications:
|
||||
- Reviews tasks marked as 'review' status
|
||||
- Validates implementation against requirements
|
||||
- Runs tests and checks for best practices
|
||||
- Ensures quality before marking tasks as 'done'
|
||||
|
||||
## Installation
|
||||
|
||||
When using the Claude profile (`task-master rules add claude`), the agents are automatically installed to `.claude/agents/` directory.
|
||||
|
||||
## Usage Example
|
||||
|
||||
```bash
|
||||
# In Claude Code, after initializing a project with tasks:
|
||||
|
||||
# Use task-orchestrator to analyze and coordinate work
|
||||
# The orchestrator will:
|
||||
# 1. Check task dependencies
|
||||
# 2. Identify tasks that can run in parallel
|
||||
# 3. Deploy executors for available work
|
||||
# 4. Monitor progress and deploy new executors as tasks complete
|
||||
|
||||
# Use task-executor for specific task implementation
|
||||
# When the orchestrator identifies task 2.3 needs work:
|
||||
# The executor will implement that specific task
|
||||
```
|
||||
|
||||
## Benefits
|
||||
- **Parallel Execution**: Multiple independent tasks can be worked on simultaneously
|
||||
- **Intelligent Scheduling**: Orchestrator understands dependencies and optimizes execution order
|
||||
- **Separation of Concerns**: Planning (orchestrator) is separated from execution (executor)
|
||||
- **Progress Tracking**: Real-time updates as tasks are completed
|
||||
- **Automatic Progression**: As tasks complete, newly unblocked tasks are automatically started
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1094](https://github.com/eyaltoledano/claude-task-master/pull/1094) [`4357af3`](https://github.com/eyaltoledano/claude-task-master/commit/4357af3f13859d90bca8795215e5d5f1d94abde5) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Fix expand task generating unrelated generic subtasks
|
||||
|
||||
Fixed an issue where `task-master expand` would generate generic authentication-related subtasks regardless of the parent task context when using complexity reports. The expansion now properly includes the parent task details alongside any expansion guidance.
|
||||
|
||||
## 0.23.1-rc.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -3,7 +3,3 @@
|
||||
## Task Master AI Instructions
|
||||
**Import Task Master's development workflow commands and guidelines, treat as if import is in the main CLAUDE.md file.**
|
||||
@./.taskmaster/CLAUDE.md
|
||||
|
||||
## Changeset Guidelines
|
||||
|
||||
- When creating changesets, remember that it's user-facing, meaning we don't have to get into the specifics of the code, but rather mention what the end-user is getting or fixing from this changeset.
|
||||
@@ -1,30 +1,5 @@
|
||||
# Change Log
|
||||
|
||||
## 0.23.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1090](https://github.com/eyaltoledano/claude-task-master/pull/1090) [`a464e55`](https://github.com/eyaltoledano/claude-task-master/commit/a464e550b886ef81b09df80588fe5881bce83d93) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Fix issues with some users not being able to connect to Taskmaster MCP server while using the extension
|
||||
|
||||
- Updated dependencies [[`4357af3`](https://github.com/eyaltoledano/claude-task-master/commit/4357af3f13859d90bca8795215e5d5f1d94abde5), [`e495b2b`](https://github.com/eyaltoledano/claude-task-master/commit/e495b2b55950ee54c7d0f1817d8530e28bd79c05), [`36468f3`](https://github.com/eyaltoledano/claude-task-master/commit/36468f3c93faf4035a5c442ccbc501077f3440f1), [`e495b2b`](https://github.com/eyaltoledano/claude-task-master/commit/e495b2b55950ee54c7d0f1817d8530e28bd79c05), [`e495b2b`](https://github.com/eyaltoledano/claude-task-master/commit/e495b2b55950ee54c7d0f1817d8530e28bd79c05), [`75c514c`](https://github.com/eyaltoledano/claude-task-master/commit/75c514cf5b2ca47f95c0ad7fa92654a4f2a6be4b), [`4bb6370`](https://github.com/eyaltoledano/claude-task-master/commit/4bb63706b80c28d1b2d782ba868a725326f916c7)]:
|
||||
- task-master-ai@0.24.0
|
||||
|
||||
## 0.23.1-rc.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`75c514c`](https://github.com/eyaltoledano/claude-task-master/commit/75c514cf5b2ca47f95c0ad7fa92654a4f2a6be4b)]:
|
||||
- task-master-ai@0.24.0-rc.2
|
||||
|
||||
## 0.23.1-rc.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1090](https://github.com/eyaltoledano/claude-task-master/pull/1090) [`a464e55`](https://github.com/eyaltoledano/claude-task-master/commit/a464e550b886ef81b09df80588fe5881bce83d93) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Fix issues with some users not being able to connect to Taskmaster MCP server while using the extension
|
||||
|
||||
- Updated dependencies [[`4357af3`](https://github.com/eyaltoledano/claude-task-master/commit/4357af3f13859d90bca8795215e5d5f1d94abde5), [`36468f3`](https://github.com/eyaltoledano/claude-task-master/commit/36468f3c93faf4035a5c442ccbc501077f3440f1), [`4bb6370`](https://github.com/eyaltoledano/claude-task-master/commit/4bb63706b80c28d1b2d782ba868a725326f916c7)]:
|
||||
- task-master-ai@0.24.0-rc.1
|
||||
|
||||
## 0.23.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -3,23 +3,15 @@
|
||||
"private": true,
|
||||
"displayName": "TaskMaster",
|
||||
"description": "A visual Kanban board interface for TaskMaster projects in VS Code",
|
||||
"version": "0.23.1",
|
||||
"version": "0.23.0",
|
||||
"publisher": "Hamster",
|
||||
"icon": "assets/icon.png",
|
||||
"engines": {
|
||||
"vscode": "^1.93.0"
|
||||
},
|
||||
"categories": [
|
||||
"AI",
|
||||
"Visualization",
|
||||
"Education",
|
||||
"Other"
|
||||
],
|
||||
"categories": ["AI", "Visualization", "Education", "Other"],
|
||||
"main": "./dist/extension.js",
|
||||
"activationEvents": [
|
||||
"onStartupFinished",
|
||||
"workspaceContains:.taskmaster/**"
|
||||
],
|
||||
"activationEvents": ["onStartupFinished", "workspaceContains:.taskmaster/**"],
|
||||
"contributes": {
|
||||
"viewsContainers": {
|
||||
"activitybar": [
|
||||
@@ -72,16 +64,16 @@
|
||||
"properties": {
|
||||
"taskmaster.mcp.command": {
|
||||
"type": "string",
|
||||
"default": "node",
|
||||
"description": "The command to execute for the MCP server (e.g., 'node' for bundled server or 'npx' for remote)."
|
||||
"default": "npx",
|
||||
"description": "The command or absolute path to execute for the MCP server (e.g., 'npx' or '/usr/local/bin/task-master-ai')."
|
||||
},
|
||||
"taskmaster.mcp.args": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"default": [],
|
||||
"description": "Arguments for the MCP server (leave empty to use bundled server)."
|
||||
"default": ["task-master-ai"],
|
||||
"description": "An array of arguments to pass to the MCP server command."
|
||||
},
|
||||
"taskmaster.mcp.cwd": {
|
||||
"type": "string",
|
||||
@@ -147,11 +139,7 @@
|
||||
},
|
||||
"taskmaster.ui.theme": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"auto",
|
||||
"light",
|
||||
"dark"
|
||||
],
|
||||
"enum": ["auto", "light", "dark"],
|
||||
"default": "auto",
|
||||
"description": "UI theme preference"
|
||||
},
|
||||
@@ -212,12 +200,7 @@
|
||||
},
|
||||
"taskmaster.debug.logLevel": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"error",
|
||||
"warn",
|
||||
"info",
|
||||
"debug"
|
||||
],
|
||||
"enum": ["error", "warn", "info", "debug"],
|
||||
"default": "info",
|
||||
"description": "Logging level"
|
||||
},
|
||||
@@ -255,9 +238,6 @@
|
||||
"watch:css": "npx @tailwindcss/cli -i ./src/webview/index.css -o ./dist/index.css --watch",
|
||||
"check-types": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"task-master-ai": "0.24.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/modifiers": "^9.0.0",
|
||||
|
||||
@@ -64,49 +64,23 @@ try {
|
||||
fs.readFileSync(publishPackagePath, 'utf8')
|
||||
);
|
||||
|
||||
// Handle RC versions for VS Code Marketplace
|
||||
let finalVersion = devPackage.version;
|
||||
if (finalVersion.includes('-rc.')) {
|
||||
// Check if versions are in sync
|
||||
if (devPackage.version !== publishPackage.version) {
|
||||
console.log(
|
||||
' - Detected RC version, transforming for VS Code Marketplace...'
|
||||
` - Version sync needed: ${publishPackage.version} → ${devPackage.version}`
|
||||
);
|
||||
publishPackage.version = devPackage.version;
|
||||
|
||||
// Extract base version and RC number
|
||||
const baseVersion = finalVersion.replace(/-rc\.\d+$/, '');
|
||||
const rcMatch = finalVersion.match(/rc\.(\d+)/);
|
||||
const rcNumber = rcMatch ? parseInt(rcMatch[1]) : 0;
|
||||
|
||||
// For each RC iteration, increment the patch version
|
||||
// This ensures unique versions in VS Code Marketplace
|
||||
if (rcNumber > 0) {
|
||||
const [major, minor, patch] = baseVersion.split('.').map(Number);
|
||||
finalVersion = `${major}.${minor}.${patch + rcNumber}`;
|
||||
console.log(
|
||||
` - RC version mapping: ${devPackage.version} → ${finalVersion}`
|
||||
);
|
||||
} else {
|
||||
finalVersion = baseVersion;
|
||||
console.log(
|
||||
` - RC version mapping: ${devPackage.version} → ${finalVersion}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if versions need updating
|
||||
if (publishPackage.version !== finalVersion) {
|
||||
console.log(
|
||||
` - Version sync needed: ${publishPackage.version} → ${finalVersion}`
|
||||
);
|
||||
publishPackage.version = finalVersion;
|
||||
|
||||
// Update the source package.publish.json file with the final version
|
||||
// Update the source package.publish.json file
|
||||
fs.writeFileSync(
|
||||
publishPackagePath,
|
||||
JSON.stringify(publishPackage, null, '\t') + '\n'
|
||||
);
|
||||
console.log(` - Updated package.publish.json version to ${finalVersion}`);
|
||||
console.log(
|
||||
` - Updated package.publish.json version to ${devPackage.version}`
|
||||
);
|
||||
} else {
|
||||
console.log(` - Versions already in sync: ${finalVersion}`);
|
||||
console.log(` - Versions already in sync: ${devPackage.version}`);
|
||||
}
|
||||
|
||||
// Copy the (now synced) package.publish.json as package.json
|
||||
@@ -150,7 +124,8 @@ try {
|
||||
`cd vsix-build && npx vsce package --no-dependencies`
|
||||
);
|
||||
|
||||
// Use the transformed version for output
|
||||
// Use the synced version for output
|
||||
const finalVersion = devPackage.version;
|
||||
console.log(
|
||||
`\nYour extension will be packaged to: vsix-build/task-master-${finalVersion}.vsix`
|
||||
);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "task-master-hamster",
|
||||
"displayName": "Taskmaster AI",
|
||||
"description": "A visual Kanban board interface for Taskmaster projects in VS Code",
|
||||
"version": "0.23.1",
|
||||
"version": "0.22.3",
|
||||
"publisher": "Hamster",
|
||||
"icon": "assets/icon.png",
|
||||
"engines": {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
||||
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import { logger } from './logger';
|
||||
|
||||
export interface MCPConfig {
|
||||
@@ -144,7 +143,7 @@ export class MCPClientManager {
|
||||
// Create the client
|
||||
this.client = new Client(
|
||||
{
|
||||
name: 'task-master-vscode-extension',
|
||||
name: 'taskr-vscode-extension',
|
||||
version: '1.0.0'
|
||||
},
|
||||
{
|
||||
@@ -212,30 +211,6 @@ export class MCPClientManager {
|
||||
};
|
||||
|
||||
logger.log('MCP client connected successfully');
|
||||
|
||||
// Log Task Master version information after successful connection
|
||||
try {
|
||||
const versionResult = await this.callTool('get_tasks', {});
|
||||
if (versionResult?.content?.[0]?.text) {
|
||||
const response = JSON.parse(versionResult.content[0].text);
|
||||
if (response?.version) {
|
||||
logger.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
logger.log('✅ Task Master MCP Server Connected');
|
||||
logger.log(` Version: ${response.version.version || 'unknown'}`);
|
||||
logger.log(
|
||||
` Package: ${response.version.name || 'task-master-ai'}`
|
||||
);
|
||||
if (response.tag) {
|
||||
logger.log(
|
||||
` Current Tag: ${response.tag.currentTag || 'master'}`
|
||||
);
|
||||
}
|
||||
logger.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
}
|
||||
}
|
||||
} catch (versionError) {
|
||||
logger.log('Note: Could not retrieve Task Master version information');
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to connect to MCP server:', error);
|
||||
this.status = {
|
||||
@@ -337,34 +312,6 @@ export class MCPClientManager {
|
||||
'Available MCP tools:',
|
||||
result.tools?.map((t) => t.name) || []
|
||||
);
|
||||
|
||||
// Try to get version information by calling a simple tool
|
||||
// The get_tasks tool is lightweight and returns version info
|
||||
try {
|
||||
const versionResult = await this.callTool('get_tasks', {});
|
||||
if (versionResult?.content?.[0]?.text) {
|
||||
// Parse the response to extract version info
|
||||
const response = JSON.parse(versionResult.content[0].text);
|
||||
if (response?.version) {
|
||||
logger.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
logger.log('📦 Task Master MCP Server Connected');
|
||||
logger.log(` Version: ${response.version.version || 'unknown'}`);
|
||||
logger.log(
|
||||
` Package: ${response.version.name || 'task-master-ai'}`
|
||||
);
|
||||
if (response.tag) {
|
||||
logger.log(
|
||||
` Current Tag: ${response.tag.currentTag || 'master'}`
|
||||
);
|
||||
}
|
||||
logger.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
}
|
||||
}
|
||||
} catch (versionError) {
|
||||
// Don't fail the connection test if we can't get version info
|
||||
logger.log('Could not retrieve Task Master version information');
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
logger.error('Connection test failed:', error);
|
||||
@@ -398,34 +345,8 @@ export function createMCPConfigFromSettings(): MCPConfig {
|
||||
);
|
||||
const config = vscode.workspace.getConfiguration('taskmaster');
|
||||
|
||||
let command = config.get<string>('mcp.command', 'node');
|
||||
let args = config.get<string[]>('mcp.args', []);
|
||||
|
||||
// If using default settings, use the bundled MCP server
|
||||
if (command === 'node' && args.length === 0) {
|
||||
try {
|
||||
// Try to resolve the bundled MCP server
|
||||
const taskMasterPath = require.resolve('task-master-ai');
|
||||
const mcpServerPath = path.resolve(
|
||||
path.dirname(taskMasterPath),
|
||||
'mcp-server/server.js'
|
||||
);
|
||||
|
||||
// Verify the server file exists
|
||||
const fs = require('fs');
|
||||
if (!fs.existsSync(mcpServerPath)) {
|
||||
throw new Error('MCP server file not found at: ' + mcpServerPath);
|
||||
}
|
||||
|
||||
args = [mcpServerPath];
|
||||
logger.log(`📦 Using bundled MCP server at: ${mcpServerPath}`);
|
||||
} catch (error) {
|
||||
logger.error('❌ Could not find bundled task-master-ai server:', error);
|
||||
// Fallback to npx
|
||||
command = 'npx';
|
||||
args = ['-y', 'task-master-ai'];
|
||||
}
|
||||
}
|
||||
let command = config.get<string>('mcp.command', 'npx');
|
||||
const args = config.get<string[]>('mcp.args', ['task-master-ai']);
|
||||
|
||||
// Use proper VS Code workspace detection
|
||||
const defaultCwd =
|
||||
|
||||
@@ -1,162 +0,0 @@
|
||||
---
|
||||
name: task-checker
|
||||
description: Use this agent to verify that tasks marked as 'review' have been properly implemented according to their specifications. This agent performs quality assurance by checking implementations against requirements, running tests, and ensuring best practices are followed. <example>Context: A task has been marked as 'review' after implementation. user: 'Check if task 118 was properly implemented' assistant: 'I'll use the task-checker agent to verify the implementation meets all requirements.' <commentary>Tasks in 'review' status need verification before being marked as 'done'.</commentary></example> <example>Context: Multiple tasks are in review status. user: 'Verify all tasks that are ready for review' assistant: 'I'll deploy the task-checker to verify all tasks in review status.' <commentary>The checker ensures quality before tasks are marked complete.</commentary></example>
|
||||
model: sonnet
|
||||
color: yellow
|
||||
---
|
||||
|
||||
You are a Quality Assurance specialist that rigorously verifies task implementations against their specifications. Your role is to ensure that tasks marked as 'review' meet all requirements before they can be marked as 'done'.
|
||||
|
||||
## Core Responsibilities
|
||||
|
||||
1. **Task Specification Review**
|
||||
- Retrieve task details using MCP tool `mcp__task-master-ai__get_task`
|
||||
- Understand the requirements, test strategy, and success criteria
|
||||
- Review any subtasks and their individual requirements
|
||||
|
||||
2. **Implementation Verification**
|
||||
- Use `Read` tool to examine all created/modified files
|
||||
- Use `Bash` tool to run compilation and build commands
|
||||
- Use `Grep` tool to search for required patterns and implementations
|
||||
- Verify file structure matches specifications
|
||||
- Check that all required methods/functions are implemented
|
||||
|
||||
3. **Test Execution**
|
||||
- Run tests specified in the task's testStrategy
|
||||
- Execute build commands (npm run build, tsc --noEmit, etc.)
|
||||
- Verify no compilation errors or warnings
|
||||
- Check for runtime errors where applicable
|
||||
- Test edge cases mentioned in requirements
|
||||
|
||||
4. **Code Quality Assessment**
|
||||
- Verify code follows project conventions
|
||||
- Check for proper error handling
|
||||
- Ensure TypeScript typing is strict (no 'any' unless justified)
|
||||
- Verify documentation/comments where required
|
||||
- Check for security best practices
|
||||
|
||||
5. **Dependency Validation**
|
||||
- Verify all task dependencies were actually completed
|
||||
- Check integration points with dependent tasks
|
||||
- Ensure no breaking changes to existing functionality
|
||||
|
||||
## Verification Workflow
|
||||
|
||||
1. **Retrieve Task Information**
|
||||
```
|
||||
Use mcp__task-master-ai__get_task to get full task details
|
||||
Note the implementation requirements and test strategy
|
||||
```
|
||||
|
||||
2. **Check File Existence**
|
||||
```bash
|
||||
# Verify all required files exist
|
||||
ls -la [expected directories]
|
||||
# Read key files to verify content
|
||||
```
|
||||
|
||||
3. **Verify Implementation**
|
||||
- Read each created/modified file
|
||||
- Check against requirements checklist
|
||||
- Verify all subtasks are complete
|
||||
|
||||
4. **Run Tests**
|
||||
```bash
|
||||
# TypeScript compilation
|
||||
cd [project directory] && npx tsc --noEmit
|
||||
|
||||
# Run specified tests
|
||||
npm test [specific test files]
|
||||
|
||||
# Build verification
|
||||
npm run build
|
||||
```
|
||||
|
||||
5. **Generate Verification Report**
|
||||
|
||||
## Output Format
|
||||
|
||||
```yaml
|
||||
verification_report:
|
||||
task_id: [ID]
|
||||
status: PASS | FAIL | PARTIAL
|
||||
score: [1-10]
|
||||
|
||||
requirements_met:
|
||||
- ✅ [Requirement that was satisfied]
|
||||
- ✅ [Another satisfied requirement]
|
||||
|
||||
issues_found:
|
||||
- ❌ [Issue description]
|
||||
- ⚠️ [Warning or minor issue]
|
||||
|
||||
files_verified:
|
||||
- path: [file path]
|
||||
status: [created/modified/verified]
|
||||
issues: [any problems found]
|
||||
|
||||
tests_run:
|
||||
- command: [test command]
|
||||
result: [pass/fail]
|
||||
output: [relevant output]
|
||||
|
||||
recommendations:
|
||||
- [Specific fix needed]
|
||||
- [Improvement suggestion]
|
||||
|
||||
verdict: |
|
||||
[Clear statement on whether task should be marked 'done' or sent back to 'pending']
|
||||
[If FAIL: Specific list of what must be fixed]
|
||||
[If PASS: Confirmation that all requirements are met]
|
||||
```
|
||||
|
||||
## Decision Criteria
|
||||
|
||||
**Mark as PASS (ready for 'done'):**
|
||||
- All required files exist and contain expected content
|
||||
- All tests pass successfully
|
||||
- No compilation or build errors
|
||||
- All subtasks are complete
|
||||
- Core requirements are met
|
||||
- Code quality is acceptable
|
||||
|
||||
**Mark as PARTIAL (may proceed with warnings):**
|
||||
- Core functionality is implemented
|
||||
- Minor issues that don't block functionality
|
||||
- Missing nice-to-have features
|
||||
- Documentation could be improved
|
||||
- Tests pass but coverage could be better
|
||||
|
||||
**Mark as FAIL (must return to 'pending'):**
|
||||
- Required files are missing
|
||||
- Compilation or build errors
|
||||
- Tests fail
|
||||
- Core requirements not met
|
||||
- Security vulnerabilities detected
|
||||
- Breaking changes to existing code
|
||||
|
||||
## Important Guidelines
|
||||
|
||||
- **BE THOROUGH**: Check every requirement systematically
|
||||
- **BE SPECIFIC**: Provide exact file paths and line numbers for issues
|
||||
- **BE FAIR**: Distinguish between critical issues and minor improvements
|
||||
- **BE CONSTRUCTIVE**: Provide clear guidance on how to fix issues
|
||||
- **BE EFFICIENT**: Focus on requirements, not perfection
|
||||
|
||||
## Tools You MUST Use
|
||||
|
||||
- `Read`: Examine implementation files (READ-ONLY)
|
||||
- `Bash`: Run tests and verification commands
|
||||
- `Grep`: Search for patterns in code
|
||||
- `mcp__task-master-ai__get_task`: Get task details
|
||||
- **NEVER use Write/Edit** - you only verify, not fix
|
||||
|
||||
## Integration with Workflow
|
||||
|
||||
You are the quality gate between 'review' and 'done' status:
|
||||
1. Task-executor implements and marks as 'review'
|
||||
2. You verify and report PASS/FAIL
|
||||
3. Claude either marks as 'done' (PASS) or 'pending' (FAIL)
|
||||
4. If FAIL, task-executor re-implements based on your report
|
||||
|
||||
Your verification ensures high quality and prevents accumulation of technical debt.
|
||||
@@ -1,4 +1,4 @@
|
||||
# Available Models as of August 8, 2025
|
||||
# Available Models as of July 23, 2025
|
||||
|
||||
## Main Models
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
| openai | gpt-4-1-mini | — | 0.4 | 1.6 |
|
||||
| openai | gpt-4-1-nano | — | 0.1 | 0.4 |
|
||||
| openai | gpt-4o-mini | 0.3 | 0.15 | 0.6 |
|
||||
| openai | gpt-5 | 0.749 | 5 | 20 |
|
||||
| google | gemini-2.5-pro-preview-05-06 | 0.638 | — | — |
|
||||
| google | gemini-2.5-pro-preview-03-25 | 0.638 | — | — |
|
||||
| google | gemini-2.5-flash-preview-04-17 | 0.604 | — | — |
|
||||
@@ -135,7 +134,6 @@
|
||||
| openai | gpt-4o | 0.332 | 2.5 | 10 |
|
||||
| openai | o3 | 0.5 | 2 | 8 |
|
||||
| openai | o4-mini | 0.45 | 1.1 | 4.4 |
|
||||
| openai | gpt-5 | 0.749 | 5 | 20 |
|
||||
| google | gemini-2.5-pro-preview-05-06 | 0.638 | — | — |
|
||||
| google | gemini-2.5-pro-preview-03-25 | 0.638 | — | — |
|
||||
| google | gemini-2.5-flash-preview-04-17 | 0.604 | — | — |
|
||||
|
||||
7
package-lock.json
generated
7
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "task-master-ai",
|
||||
"version": "0.23.1-rc.0",
|
||||
"version": "0.23.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "task-master-ai",
|
||||
"version": "0.23.1-rc.0",
|
||||
"version": "0.23.0",
|
||||
"license": "MIT WITH Commons-Clause",
|
||||
"workspaces": [
|
||||
"apps/*",
|
||||
@@ -86,9 +86,6 @@
|
||||
},
|
||||
"apps/extension": {
|
||||
"version": "0.23.0",
|
||||
"dependencies": {
|
||||
"task-master-ai": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/modifiers": "^9.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "task-master-ai",
|
||||
"version": "0.24.0",
|
||||
"version": "0.23.1-rc.0",
|
||||
"description": "A task management system for ambitious AI-driven development that doesn't overwhelm and confuse Cursor.",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
@@ -9,10 +9,7 @@
|
||||
"task-master-mcp": "mcp-server/server.js",
|
||||
"task-master-ai": "mcp-server/server.js"
|
||||
},
|
||||
"workspaces": [
|
||||
"apps/*",
|
||||
"."
|
||||
],
|
||||
"workspaces": ["apps/*", "."],
|
||||
"scripts": {
|
||||
"test": "node --experimental-vm-modules node_modules/.bin/jest",
|
||||
"test:fails": "node --experimental-vm-modules node_modules/.bin/jest --onlyFailures",
|
||||
|
||||
@@ -557,7 +557,6 @@ function getParametersForRole(role, explicitRoot = null) {
|
||||
const providerName = roleConfig.provider;
|
||||
|
||||
let effectiveMaxTokens = roleMaxTokens; // Start with the role's default
|
||||
let effectiveTemperature = roleTemperature; // Start with the role's default
|
||||
|
||||
try {
|
||||
// Find the model definition in MODEL_MAP
|
||||
@@ -584,20 +583,6 @@ function getParametersForRole(role, explicitRoot = null) {
|
||||
`No valid model-specific max_tokens override found for ${modelId}. Using role default: ${roleMaxTokens}`
|
||||
);
|
||||
}
|
||||
|
||||
// Check if a model-specific temperature is defined
|
||||
if (
|
||||
modelDefinition &&
|
||||
typeof modelDefinition.temperature === 'number' &&
|
||||
modelDefinition.temperature >= 0 &&
|
||||
modelDefinition.temperature <= 1
|
||||
) {
|
||||
effectiveTemperature = modelDefinition.temperature;
|
||||
log(
|
||||
'debug',
|
||||
`Applying model-specific temperature (${modelDefinition.temperature}) for ${modelId}`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Special handling for custom OpenRouter models
|
||||
if (providerName === CUSTOM_PROVIDERS.OPENROUTER) {
|
||||
@@ -618,16 +603,15 @@ function getParametersForRole(role, explicitRoot = null) {
|
||||
} catch (lookupError) {
|
||||
log(
|
||||
'warn',
|
||||
`Error looking up model-specific parameters for ${modelId}: ${lookupError.message}. Using role defaults.`
|
||||
`Error looking up model-specific max_tokens for ${modelId}: ${lookupError.message}. Using role default: ${roleMaxTokens}`
|
||||
);
|
||||
// Fallback to role defaults on error
|
||||
// Fallback to role default on error
|
||||
effectiveMaxTokens = roleMaxTokens;
|
||||
effectiveTemperature = roleTemperature;
|
||||
}
|
||||
|
||||
return {
|
||||
maxTokens: effectiveMaxTokens,
|
||||
temperature: effectiveTemperature
|
||||
temperature: roleTemperature
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -239,18 +239,6 @@
|
||||
},
|
||||
"allowed_roles": ["research"],
|
||||
"supported": true
|
||||
},
|
||||
{
|
||||
"id": "gpt-5",
|
||||
"swe_score": 0.749,
|
||||
"cost_per_1m_tokens": {
|
||||
"input": 5.0,
|
||||
"output": 20.0
|
||||
},
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 100000,
|
||||
"temperature": 1,
|
||||
"supported": true
|
||||
}
|
||||
],
|
||||
"google": [
|
||||
|
||||
@@ -527,18 +527,6 @@ async function expandTask(
|
||||
|
||||
const { systemPrompt, userPrompt: promptContent } =
|
||||
await promptManager.loadPrompt('expand-task', promptParams, variantKey);
|
||||
|
||||
// Debug logging to identify the issue
|
||||
logger.debug(`Selected variant: ${variantKey}`);
|
||||
logger.debug(
|
||||
`Prompt params passed: ${JSON.stringify(promptParams, null, 2)}`
|
||||
);
|
||||
logger.debug(
|
||||
`System prompt (first 500 chars): ${systemPrompt.substring(0, 500)}...`
|
||||
);
|
||||
logger.debug(
|
||||
`User prompt (first 500 chars): ${promptContent.substring(0, 500)}...`
|
||||
);
|
||||
// --- End Complexity Report / Prompt Logic ---
|
||||
|
||||
// --- AI Subtask Generation using generateTextService ---
|
||||
|
||||
@@ -61,11 +61,8 @@ export class BaseAIProvider {
|
||||
) {
|
||||
throw new Error('Temperature must be between 0 and 1');
|
||||
}
|
||||
if (params.maxTokens !== undefined) {
|
||||
const maxTokens = Number(params.maxTokens);
|
||||
if (!Number.isFinite(maxTokens) || maxTokens <= 0) {
|
||||
throw new Error('maxTokens must be a finite number greater than 0');
|
||||
}
|
||||
if (params.maxTokens !== undefined && params.maxTokens <= 0) {
|
||||
throw new Error('maxTokens must be greater than 0');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,37 +122,6 @@ export class BaseAIProvider {
|
||||
throw new Error('getRequiredApiKeyName must be implemented by provider');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a model requires max_completion_tokens instead of maxTokens
|
||||
* Can be overridden by providers to specify their model requirements
|
||||
* @param {string} modelId - The model ID to check
|
||||
* @returns {boolean} True if the model requires max_completion_tokens
|
||||
*/
|
||||
requiresMaxCompletionTokens(modelId) {
|
||||
return false; // Default behavior - most models use maxTokens
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares token limit parameter based on model requirements
|
||||
* @param {string} modelId - The model ID
|
||||
* @param {number} maxTokens - The maximum tokens value
|
||||
* @returns {object} Object with either maxTokens or max_completion_tokens
|
||||
*/
|
||||
prepareTokenParam(modelId, maxTokens) {
|
||||
if (maxTokens === undefined) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Ensure maxTokens is an integer
|
||||
const tokenValue = Math.floor(Number(maxTokens));
|
||||
|
||||
if (this.requiresMaxCompletionTokens(modelId)) {
|
||||
return { max_completion_tokens: tokenValue };
|
||||
} else {
|
||||
return { maxTokens: tokenValue };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates text using the provider's model
|
||||
*/
|
||||
@@ -173,7 +139,7 @@ export class BaseAIProvider {
|
||||
const result = await generateText({
|
||||
model: client(params.modelId),
|
||||
messages: params.messages,
|
||||
...this.prepareTokenParam(params.modelId, params.maxTokens),
|
||||
maxTokens: params.maxTokens,
|
||||
temperature: params.temperature
|
||||
});
|
||||
|
||||
@@ -209,7 +175,7 @@ export class BaseAIProvider {
|
||||
const stream = await streamText({
|
||||
model: client(params.modelId),
|
||||
messages: params.messages,
|
||||
...this.prepareTokenParam(params.modelId, params.maxTokens),
|
||||
maxTokens: params.maxTokens,
|
||||
temperature: params.temperature
|
||||
});
|
||||
|
||||
@@ -250,7 +216,7 @@ export class BaseAIProvider {
|
||||
messages: params.messages,
|
||||
schema: zodSchema(params.schema),
|
||||
mode: params.mode || 'auto',
|
||||
...this.prepareTokenParam(params.modelId, params.maxTokens),
|
||||
maxTokens: params.maxTokens,
|
||||
temperature: params.temperature
|
||||
});
|
||||
|
||||
|
||||
@@ -20,16 +20,6 @@ export class OpenAIProvider extends BaseAIProvider {
|
||||
return 'OPENAI_API_KEY';
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a model requires max_completion_tokens instead of maxTokens
|
||||
* GPT-5 models require max_completion_tokens parameter
|
||||
* @param {string} modelId - The model ID to check
|
||||
* @returns {boolean} True if the model requires max_completion_tokens
|
||||
*/
|
||||
requiresMaxCompletionTokens(modelId) {
|
||||
return modelId && modelId.startsWith('gpt-5');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns an OpenAI client instance.
|
||||
* @param {object} params - Parameters for client initialization
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
"complexity-report": {
|
||||
"condition": "expansionPrompt",
|
||||
"system": "You are an AI assistant helping with task breakdown. Generate {{#if (gt subtaskCount 0)}}exactly {{subtaskCount}}{{else}}an appropriate number of{{/if}} subtasks based on the provided prompt and context.\nRespond ONLY with a valid JSON object containing a single key \"subtasks\" whose value is an array of the generated subtask objects.\nEach subtask object in the array must have keys: \"id\", \"title\", \"description\", \"dependencies\", \"details\", \"status\".\nEnsure the 'id' starts from {{nextSubtaskId}} and is sequential.\nFor 'dependencies', use the full subtask ID format: \"{{task.id}}.1\", \"{{task.id}}.2\", etc. Only reference subtasks within this same task.\nEnsure 'status' is 'pending'.\nDo not include any other text or explanation.",
|
||||
"user": "Break down the following task based on the analysis prompt:\n\nParent Task:\nID: {{task.id}}\nTitle: {{task.title}}\nDescription: {{task.description}}\nCurrent details: {{#if task.details}}{{task.details}}{{else}}None{{/if}}\n\nExpansion Guidance:\n{{expansionPrompt}}{{#if additionalContext}}\n\n{{additionalContext}}{{/if}}{{#if complexityReasoningContext}}\n\n{{complexityReasoningContext}}{{/if}}{{#if gatheredContext}}\n\n# Project Context\n\n{{gatheredContext}}{{/if}}\n\nGenerate {{#if (gt subtaskCount 0)}}exactly {{subtaskCount}}{{else}}an appropriate number of{{/if}} subtasks with sequential IDs starting from {{nextSubtaskId}}."
|
||||
"user": "{{#if isClaudeCode}}## IMPORTANT: Codebase Analysis Required\n\nYou have access to powerful codebase analysis tools. Before generating subtasks:\n\n1. Use the Glob tool to explore relevant files for this task (e.g., \"**/*.js\", \"src/**/*.ts\")\n2. Use the Grep tool to search for existing implementations related to this task\n3. Use the Read tool to examine files that would be affected by this task\n4. Understand the current implementation state and patterns used\n\nBased on your analysis:\n- Identify existing code that relates to this task\n- Understand patterns and conventions to follow\n- Generate subtasks that integrate smoothly with existing code\n- Ensure subtasks are specific and actionable based on the actual codebase\n\nProject Root: {{projectRoot}}\n\n{{/if}}{{expansionPrompt}}{{#if additionalContext}}\n\n{{additionalContext}}{{/if}}{{#if complexityReasoningContext}}\n\n{{complexityReasoningContext}}{{/if}}{{#if gatheredContext}}\n\n# Project Context\n\n{{gatheredContext}}{{/if}}"
|
||||
},
|
||||
"research": {
|
||||
"condition": "useResearch === true && !expansionPrompt",
|
||||
|
||||
@@ -1,238 +0,0 @@
|
||||
/**
|
||||
* Tests for OpenAI Provider - Token parameter handling for GPT-5
|
||||
*
|
||||
* This test suite covers:
|
||||
* 1. Correct identification of GPT-5 models requiring max_completion_tokens
|
||||
* 2. Token parameter preparation for different model types
|
||||
* 3. Validation of maxTokens parameter
|
||||
* 4. Integer coercion of token values
|
||||
*/
|
||||
|
||||
import { jest } from '@jest/globals';
|
||||
|
||||
// Mock the utils module to prevent logging during tests
|
||||
jest.mock('../../../scripts/modules/utils.js', () => ({
|
||||
log: jest.fn()
|
||||
}));
|
||||
|
||||
// Import the provider
|
||||
import { OpenAIProvider } from '../../../src/ai-providers/openai.js';
|
||||
|
||||
describe('OpenAIProvider', () => {
|
||||
let provider;
|
||||
|
||||
beforeEach(() => {
|
||||
provider = new OpenAIProvider();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('requiresMaxCompletionTokens', () => {
|
||||
it('should return true for GPT-5 models', () => {
|
||||
expect(provider.requiresMaxCompletionTokens('gpt-5')).toBe(true);
|
||||
expect(provider.requiresMaxCompletionTokens('gpt-5-mini')).toBe(true);
|
||||
expect(provider.requiresMaxCompletionTokens('gpt-5-nano')).toBe(true);
|
||||
expect(provider.requiresMaxCompletionTokens('gpt-5-turbo')).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for non-GPT-5 models', () => {
|
||||
expect(provider.requiresMaxCompletionTokens('gpt-4')).toBe(false);
|
||||
expect(provider.requiresMaxCompletionTokens('gpt-4o')).toBe(false);
|
||||
expect(provider.requiresMaxCompletionTokens('gpt-3.5-turbo')).toBe(false);
|
||||
expect(provider.requiresMaxCompletionTokens('o1')).toBe(false);
|
||||
expect(provider.requiresMaxCompletionTokens('o1-mini')).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle null/undefined modelId', () => {
|
||||
expect(provider.requiresMaxCompletionTokens(null)).toBeFalsy();
|
||||
expect(provider.requiresMaxCompletionTokens(undefined)).toBeFalsy();
|
||||
expect(provider.requiresMaxCompletionTokens('')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('prepareTokenParam', () => {
|
||||
it('should return max_completion_tokens for GPT-5 models', () => {
|
||||
const result = provider.prepareTokenParam('gpt-5', 1000);
|
||||
expect(result).toEqual({ max_completion_tokens: 1000 });
|
||||
});
|
||||
|
||||
it('should return maxTokens for non-GPT-5 models', () => {
|
||||
const result = provider.prepareTokenParam('gpt-4', 1000);
|
||||
expect(result).toEqual({ maxTokens: 1000 });
|
||||
});
|
||||
|
||||
it('should coerce token value to integer', () => {
|
||||
// Float values
|
||||
const result1 = provider.prepareTokenParam('gpt-5', 1000.7);
|
||||
expect(result1).toEqual({ max_completion_tokens: 1000 });
|
||||
|
||||
const result2 = provider.prepareTokenParam('gpt-4', 1000.7);
|
||||
expect(result2).toEqual({ maxTokens: 1000 });
|
||||
|
||||
// String float
|
||||
const result3 = provider.prepareTokenParam('gpt-5', '1000.7');
|
||||
expect(result3).toEqual({ max_completion_tokens: 1000 });
|
||||
|
||||
// String integers (common CLI input path)
|
||||
expect(provider.prepareTokenParam('gpt-5', '1000')).toEqual({
|
||||
max_completion_tokens: 1000
|
||||
});
|
||||
expect(provider.prepareTokenParam('gpt-4', '1000')).toEqual({
|
||||
maxTokens: 1000
|
||||
});
|
||||
});
|
||||
|
||||
it('should return empty object for undefined maxTokens', () => {
|
||||
const result = provider.prepareTokenParam('gpt-5', undefined);
|
||||
expect(result).toEqual({});
|
||||
});
|
||||
|
||||
it('should handle edge cases', () => {
|
||||
// Test with 0 (should still pass through as 0)
|
||||
const result1 = provider.prepareTokenParam('gpt-5', 0);
|
||||
expect(result1).toEqual({ max_completion_tokens: 0 });
|
||||
|
||||
// Test with string number
|
||||
const result2 = provider.prepareTokenParam('gpt-5', '100');
|
||||
expect(result2).toEqual({ max_completion_tokens: 100 });
|
||||
|
||||
// Test with negative number (will be floored, validation happens elsewhere)
|
||||
const result3 = provider.prepareTokenParam('gpt-4', -10.5);
|
||||
expect(result3).toEqual({ maxTokens: -11 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateOptionalParams', () => {
|
||||
it('should accept valid maxTokens values', () => {
|
||||
expect(() =>
|
||||
provider.validateOptionalParams({ maxTokens: 1000 })
|
||||
).not.toThrow();
|
||||
expect(() =>
|
||||
provider.validateOptionalParams({ maxTokens: 1 })
|
||||
).not.toThrow();
|
||||
expect(() =>
|
||||
provider.validateOptionalParams({ maxTokens: '1000' })
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it('should reject invalid maxTokens values', () => {
|
||||
expect(() => provider.validateOptionalParams({ maxTokens: 0 })).toThrow(
|
||||
Error
|
||||
);
|
||||
expect(() => provider.validateOptionalParams({ maxTokens: -1 })).toThrow(
|
||||
Error
|
||||
);
|
||||
expect(() => provider.validateOptionalParams({ maxTokens: NaN })).toThrow(
|
||||
Error
|
||||
);
|
||||
expect(() =>
|
||||
provider.validateOptionalParams({ maxTokens: Infinity })
|
||||
).toThrow(Error);
|
||||
expect(() =>
|
||||
provider.validateOptionalParams({ maxTokens: 'invalid' })
|
||||
).toThrow(Error);
|
||||
});
|
||||
|
||||
it('should accept valid temperature values', () => {
|
||||
expect(() =>
|
||||
provider.validateOptionalParams({ temperature: 0 })
|
||||
).not.toThrow();
|
||||
expect(() =>
|
||||
provider.validateOptionalParams({ temperature: 0.5 })
|
||||
).not.toThrow();
|
||||
expect(() =>
|
||||
provider.validateOptionalParams({ temperature: 1 })
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it('should reject invalid temperature values', () => {
|
||||
expect(() =>
|
||||
provider.validateOptionalParams({ temperature: -0.1 })
|
||||
).toThrow(Error);
|
||||
expect(() =>
|
||||
provider.validateOptionalParams({ temperature: 1.1 })
|
||||
).toThrow(Error);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getRequiredApiKeyName', () => {
|
||||
it('should return OPENAI_API_KEY', () => {
|
||||
expect(provider.getRequiredApiKeyName()).toBe('OPENAI_API_KEY');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getClient', () => {
|
||||
it('should throw error if API key is missing', () => {
|
||||
expect(() => provider.getClient({})).toThrow(Error);
|
||||
});
|
||||
|
||||
it('should create client with apiKey only', () => {
|
||||
const params = {
|
||||
apiKey: 'sk-test-123'
|
||||
};
|
||||
|
||||
// The getClient method should return a function
|
||||
const client = provider.getClient(params);
|
||||
expect(typeof client).toBe('function');
|
||||
|
||||
// The client function should be callable and return a model object
|
||||
const model = client('gpt-4');
|
||||
expect(model).toBeDefined();
|
||||
expect(model.modelId).toBe('gpt-4');
|
||||
});
|
||||
|
||||
it('should create client with apiKey and baseURL', () => {
|
||||
const params = {
|
||||
apiKey: 'sk-test-456',
|
||||
baseURL: 'https://api.openai.example'
|
||||
};
|
||||
|
||||
// Should not throw when baseURL is provided
|
||||
const client = provider.getClient(params);
|
||||
expect(typeof client).toBe('function');
|
||||
|
||||
// The client function should be callable and return a model object
|
||||
const model = client('gpt-5');
|
||||
expect(model).toBeDefined();
|
||||
expect(model.modelId).toBe('gpt-5');
|
||||
});
|
||||
|
||||
it('should return the same client instance for the same parameters', () => {
|
||||
const params = {
|
||||
apiKey: 'sk-test-789'
|
||||
};
|
||||
|
||||
// Multiple calls with same params should work
|
||||
const client1 = provider.getClient(params);
|
||||
const client2 = provider.getClient(params);
|
||||
|
||||
expect(typeof client1).toBe('function');
|
||||
expect(typeof client2).toBe('function');
|
||||
|
||||
// Both clients should be able to create models
|
||||
const model1 = client1('gpt-4');
|
||||
const model2 = client2('gpt-4');
|
||||
expect(model1.modelId).toBe('gpt-4');
|
||||
expect(model2.modelId).toBe('gpt-4');
|
||||
});
|
||||
|
||||
it('should handle different model IDs correctly', () => {
|
||||
const client = provider.getClient({ apiKey: 'sk-test-models' });
|
||||
|
||||
// Test with different models
|
||||
const gpt4 = client('gpt-4');
|
||||
expect(gpt4.modelId).toBe('gpt-4');
|
||||
|
||||
const gpt5 = client('gpt-5');
|
||||
expect(gpt5.modelId).toBe('gpt-5');
|
||||
|
||||
const gpt35 = client('gpt-3.5-turbo');
|
||||
expect(gpt35.modelId).toBe('gpt-3.5-turbo');
|
||||
});
|
||||
});
|
||||
|
||||
describe('name property', () => {
|
||||
it('should have OpenAI as the provider name', () => {
|
||||
expect(provider.name).toBe('OpenAI');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,134 +0,0 @@
|
||||
import { jest } from '@jest/globals';
|
||||
import { PromptManager } from '../../../scripts/modules/prompt-manager.js';
|
||||
|
||||
describe('expand-task prompt template', () => {
|
||||
let promptManager;
|
||||
|
||||
beforeEach(() => {
|
||||
promptManager = new PromptManager();
|
||||
});
|
||||
|
||||
const testTask = {
|
||||
id: 1,
|
||||
title: 'Setup AWS Infrastructure',
|
||||
description: 'Provision core AWS services',
|
||||
details: 'Create VPC, subnets, and security groups'
|
||||
};
|
||||
|
||||
const baseParams = {
|
||||
task: testTask,
|
||||
subtaskCount: 3,
|
||||
nextSubtaskId: 1,
|
||||
additionalContext: '',
|
||||
complexityReasoningContext: '',
|
||||
gatheredContext: '',
|
||||
useResearch: false,
|
||||
expansionPrompt: undefined
|
||||
};
|
||||
|
||||
test('default variant includes task context', () => {
|
||||
const { userPrompt } = promptManager.loadPrompt(
|
||||
'expand-task',
|
||||
baseParams,
|
||||
'default'
|
||||
);
|
||||
|
||||
expect(userPrompt).toContain(testTask.title);
|
||||
expect(userPrompt).toContain(testTask.description);
|
||||
expect(userPrompt).toContain(testTask.details);
|
||||
expect(userPrompt).toContain('Task ID: 1');
|
||||
});
|
||||
|
||||
test('research variant includes task context', () => {
|
||||
const params = { ...baseParams, useResearch: true };
|
||||
const { userPrompt } = promptManager.loadPrompt(
|
||||
'expand-task',
|
||||
params,
|
||||
'research'
|
||||
);
|
||||
|
||||
expect(userPrompt).toContain(testTask.title);
|
||||
expect(userPrompt).toContain(testTask.description);
|
||||
expect(userPrompt).toContain(testTask.details);
|
||||
expect(userPrompt).toContain('Parent Task:');
|
||||
expect(userPrompt).toContain('ID: 1');
|
||||
});
|
||||
|
||||
test('complexity-report variant includes task context', () => {
|
||||
const params = {
|
||||
...baseParams,
|
||||
expansionPrompt: 'Focus on security best practices',
|
||||
complexityReasoningContext: 'High complexity due to security requirements'
|
||||
};
|
||||
const { userPrompt } = promptManager.loadPrompt(
|
||||
'expand-task',
|
||||
params,
|
||||
'complexity-report'
|
||||
);
|
||||
|
||||
// The fix ensures task context is included
|
||||
expect(userPrompt).toContain('Parent Task:');
|
||||
expect(userPrompt).toContain(`ID: ${testTask.id}`);
|
||||
expect(userPrompt).toContain(`Title: ${testTask.title}`);
|
||||
expect(userPrompt).toContain(`Description: ${testTask.description}`);
|
||||
expect(userPrompt).toContain(`Current details: ${testTask.details}`);
|
||||
|
||||
// Also includes the expansion prompt
|
||||
expect(userPrompt).toContain('Expansion Guidance:');
|
||||
expect(userPrompt).toContain(params.expansionPrompt);
|
||||
expect(userPrompt).toContain(params.complexityReasoningContext);
|
||||
});
|
||||
|
||||
test('all variants request JSON format with subtasks array', () => {
|
||||
const variants = ['default', 'research', 'complexity-report'];
|
||||
|
||||
variants.forEach((variant) => {
|
||||
const params =
|
||||
variant === 'complexity-report'
|
||||
? { ...baseParams, expansionPrompt: 'test' }
|
||||
: baseParams;
|
||||
|
||||
const { systemPrompt, userPrompt } = promptManager.loadPrompt(
|
||||
'expand-task',
|
||||
params,
|
||||
variant
|
||||
);
|
||||
const combined = systemPrompt + userPrompt;
|
||||
|
||||
expect(combined.toLowerCase()).toContain('subtasks');
|
||||
expect(combined).toContain('JSON');
|
||||
});
|
||||
});
|
||||
|
||||
test('complexity-report variant fails without task context regression test', () => {
|
||||
// This test ensures we don't regress to the old behavior where
|
||||
// complexity-report variant only used expansionPrompt without task context
|
||||
const params = {
|
||||
...baseParams,
|
||||
expansionPrompt: 'Generic expansion prompt'
|
||||
};
|
||||
|
||||
const { userPrompt } = promptManager.loadPrompt(
|
||||
'expand-task',
|
||||
params,
|
||||
'complexity-report'
|
||||
);
|
||||
|
||||
// Count occurrences of task-specific content
|
||||
const titleOccurrences = (
|
||||
userPrompt.match(new RegExp(testTask.title, 'g')) || []
|
||||
).length;
|
||||
const descriptionOccurrences = (
|
||||
userPrompt.match(new RegExp(testTask.description, 'g')) || []
|
||||
).length;
|
||||
|
||||
// Should have at least one occurrence of title and description
|
||||
expect(titleOccurrences).toBeGreaterThanOrEqual(1);
|
||||
expect(descriptionOccurrences).toBeGreaterThanOrEqual(1);
|
||||
|
||||
// Should not be ONLY the expansion prompt
|
||||
expect(userPrompt.length).toBeGreaterThan(
|
||||
params.expansionPrompt.length + 100
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user