Compare commits
11 Commits
v0.16.1
...
v0.16.2-rc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cba86510d3 | ||
|
|
86ea6d1dbc | ||
|
|
a22d2a45b5 | ||
|
|
d73c8e17ec | ||
|
|
4f23751d25 | ||
|
|
7d5c028ca0 | ||
|
|
f18df6da19 | ||
|
|
1754a31372 | ||
|
|
3096ccdfb3 | ||
|
|
6464bb11e5 | ||
|
|
edaa5fe0d5 |
7
.changeset/pink-houses-lay.md
Normal file
7
.changeset/pink-houses-lay.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"task-master-ai": patch
|
||||
---
|
||||
|
||||
Fix double .taskmaster directory paths in file resolution utilities
|
||||
|
||||
- Closes #636
|
||||
5
.changeset/polite-areas-shave.md
Normal file
5
.changeset/polite-areas-shave.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"task-master-ai": patch
|
||||
---
|
||||
|
||||
Add one-click MCP server installation for Cursor
|
||||
@@ -395,7 +395,7 @@ Environment variables are used **only** for sensitive API keys related to AI pro
|
||||
* `AZURE_OPENAI_API_KEY` (Requires `AZURE_OPENAI_ENDPOINT` too)
|
||||
* `OPENROUTER_API_KEY`
|
||||
* `XAI_API_KEY`
|
||||
* `OLLANA_API_KEY` (Requires `OLLAMA_BASE_URL` too)
|
||||
* `OLLAMA_API_KEY` (Requires `OLLAMA_BASE_URL` too)
|
||||
* **Endpoints (Optional/Provider Specific inside .taskmaster/config.json):**
|
||||
* `AZURE_OPENAI_ENDPOINT`
|
||||
* `OLLAMA_BASE_URL` (Default: `http://localhost:11434/api`)
|
||||
|
||||
30
README.md
30
README.md
@@ -39,9 +39,17 @@ MCP (Model Control Protocol) lets you run Task Master directly from your editor.
|
||||
| **Cursor** | Global | `~/.cursor/mcp.json` | `%USERPROFILE%\.cursor\mcp.json` | `mcpServers` |
|
||||
| | Project | `<project_folder>/.cursor/mcp.json` | `<project_folder>\.cursor\mcp.json` | `mcpServers` |
|
||||
| **Windsurf** | Global | `~/.codeium/windsurf/mcp_config.json` | `%USERPROFILE%\.codeium\windsurf\mcp_config.json` | `mcpServers` |
|
||||
| **VS Code** | Project | `<project_folder>/.vscode/mcp.json` | `<project_folder>\.vscode\mcp.json` | `servers` |
|
||||
| **VS Code** | Project | `<project_folder>/.vscode/mcp.json` | `<project_folder>\.vscode\mcp.json` | `servers` |
|
||||
|
||||
##### Cursor & Windsurf (`mcpServers`)
|
||||
##### Quick Install for Cursor (One-Click)
|
||||
|
||||
[<img src="https://cursor.com/deeplink/mcp-install-dark.png" alt="Add Task Master MCP server to Cursor" style="max-height: 32px;">](cursor://anysphere.cursor-deeplink/mcp/install?name=taskmaster-ai&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIi0tcGFja2FnZT10YXNrLW1hc3Rlci1haSIsInRhc2stbWFzdGVyLWFpIl0sImVudiI6eyJBTlRIUk9QSUNfQVBJX0tFWSI6IllPVVJfQU5USFJPUElDX0FQSV9LRVlfSEVSRSIsIlBFUlBMRVhJVFlfQVBJX0tFWSI6IllPVVJfUEVSUExFWElUWV9BUElfS0VZX0hFUkUiLCJPUEVOQUlfQVBJX0tFWSI6IllPVVJfT1BFTkFJX0tFWV9IRVJFIiwiR09PR0xFX0FQSV9LRVkiOiJZT1VSX0dPT0dMRV9LRVlfSEVSRSIsIk1JU1RSQUxfQVBJX0tFWSI6IllPVVJfTUlTVFJBTF9LRVlfSEVSRSIsIk9QRU5ST1VURVJfQVBJX0tFWSI6IllPVVJfT1BFTlJPVVRFUl9LRVlfSEVSRSIsIlhBSV9BUElfS0VZIjoiWU9VUl9YQUlfS0VZX0hFUkUiLCJBWlVSRV9PUEVOQUJFX0FQSV9LRVkiOiJZT1VSX0FaVVJFX0tFWV9IRVJFIiwiT0xMQU1BX0FQSV9LRVkiOiJZT1VSX09MTEFNQV9BUElfS0VZX0hFUkUifX0%3D)
|
||||
|
||||
> **Note:** After clicking the install button, you'll still need to add your API keys to the configuration. The button installs the MCP server with placeholder keys that you'll need to replace with your actual API keys.
|
||||
|
||||
##### Manual Configuration
|
||||
|
||||
###### Cursor & Windsurf (`mcpServers`)
|
||||
|
||||
```jsonc
|
||||
{
|
||||
@@ -58,16 +66,16 @@ MCP (Model Control Protocol) lets you run Task Master directly from your editor.
|
||||
"OPENROUTER_API_KEY": "YOUR_OPENROUTER_KEY_HERE",
|
||||
"XAI_API_KEY": "YOUR_XAI_KEY_HERE",
|
||||
"AZURE_OPENAI_API_KEY": "YOUR_AZURE_KEY_HERE",
|
||||
"OLLAMA_API_KEY": "YOUR_OLLAMA_API_KEY_HERE",
|
||||
},
|
||||
},
|
||||
},
|
||||
"OLLAMA_API_KEY": "YOUR_OLLAMA_API_KEY_HERE"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 🔑 Replace `YOUR_…_KEY_HERE` with your real API keys. You can remove keys you don't use.
|
||||
|
||||
##### VS Code (`servers` + `type`)
|
||||
###### VS Code (`servers` + `type`)
|
||||
|
||||
```jsonc
|
||||
{
|
||||
@@ -83,11 +91,11 @@ MCP (Model Control Protocol) lets you run Task Master directly from your editor.
|
||||
"MISTRAL_API_KEY": "YOUR_MISTRAL_KEY_HERE",
|
||||
"OPENROUTER_API_KEY": "YOUR_OPENROUTER_KEY_HERE",
|
||||
"XAI_API_KEY": "YOUR_XAI_KEY_HERE",
|
||||
"AZURE_OPENAI_API_KEY": "YOUR_AZURE_KEY_HERE",
|
||||
},
|
||||
"type": "stdio",
|
||||
},
|
||||
"AZURE_OPENAI_API_KEY": "YOUR_AZURE_KEY_HERE"
|
||||
},
|
||||
"type": "stdio"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
``# Taskmaster AI Installation Guide
|
||||
# Taskmaster AI Installation Guide
|
||||
|
||||
This guide helps AI assistants install and configure Taskmaster for users in their development projects.
|
||||
|
||||
|
||||
@@ -3,7 +3,8 @@ import {
|
||||
findTasksPath as coreFindTasksPath,
|
||||
findPRDPath as coreFindPrdPath,
|
||||
findComplexityReportPath as coreFindComplexityReportPath,
|
||||
findProjectRoot as coreFindProjectRoot
|
||||
findProjectRoot as coreFindProjectRoot,
|
||||
normalizeProjectRoot
|
||||
} from '../../../../src/utils/path-utils.js';
|
||||
import { PROJECT_MARKERS } from '../../../../src/constants/paths.js';
|
||||
|
||||
@@ -49,19 +50,24 @@ export function findPrdPath(explicitPath, args = null, log = silentLogger) {
|
||||
export function resolveTasksPath(args, log = silentLogger) {
|
||||
// Get explicit path from args.file if provided
|
||||
const explicitPath = args?.file;
|
||||
const projectRoot = args?.projectRoot;
|
||||
const rawProjectRoot = args?.projectRoot;
|
||||
|
||||
// If explicit path is provided and absolute, use it directly
|
||||
if (explicitPath && path.isAbsolute(explicitPath)) {
|
||||
return explicitPath;
|
||||
}
|
||||
|
||||
// If explicit path is relative, resolve it relative to projectRoot
|
||||
// Normalize project root if provided
|
||||
const projectRoot = rawProjectRoot
|
||||
? normalizeProjectRoot(rawProjectRoot)
|
||||
: null;
|
||||
|
||||
// If explicit path is relative, resolve it relative to normalized projectRoot
|
||||
if (explicitPath && projectRoot) {
|
||||
return path.resolve(projectRoot, explicitPath);
|
||||
}
|
||||
|
||||
// Use core findTasksPath with explicit path and projectRoot context
|
||||
// Use core findTasksPath with explicit path and normalized projectRoot context
|
||||
if (projectRoot) {
|
||||
return coreFindTasksPath(explicitPath, { projectRoot }, log);
|
||||
}
|
||||
@@ -79,19 +85,24 @@ export function resolveTasksPath(args, log = silentLogger) {
|
||||
export function resolvePrdPath(args, log = silentLogger) {
|
||||
// Get explicit path from args.input if provided
|
||||
const explicitPath = args?.input;
|
||||
const projectRoot = args?.projectRoot;
|
||||
const rawProjectRoot = args?.projectRoot;
|
||||
|
||||
// If explicit path is provided and absolute, use it directly
|
||||
if (explicitPath && path.isAbsolute(explicitPath)) {
|
||||
return explicitPath;
|
||||
}
|
||||
|
||||
// If explicit path is relative, resolve it relative to projectRoot
|
||||
// Normalize project root if provided
|
||||
const projectRoot = rawProjectRoot
|
||||
? normalizeProjectRoot(rawProjectRoot)
|
||||
: null;
|
||||
|
||||
// If explicit path is relative, resolve it relative to normalized projectRoot
|
||||
if (explicitPath && projectRoot) {
|
||||
return path.resolve(projectRoot, explicitPath);
|
||||
}
|
||||
|
||||
// Use core findPRDPath with explicit path and projectRoot context
|
||||
// Use core findPRDPath with explicit path and normalized projectRoot context
|
||||
if (projectRoot) {
|
||||
return coreFindPrdPath(explicitPath, { projectRoot }, log);
|
||||
}
|
||||
@@ -109,19 +120,24 @@ export function resolvePrdPath(args, log = silentLogger) {
|
||||
export function resolveComplexityReportPath(args, log = silentLogger) {
|
||||
// Get explicit path from args.complexityReport if provided
|
||||
const explicitPath = args?.complexityReport;
|
||||
const projectRoot = args?.projectRoot;
|
||||
const rawProjectRoot = args?.projectRoot;
|
||||
|
||||
// If explicit path is provided and absolute, use it directly
|
||||
if (explicitPath && path.isAbsolute(explicitPath)) {
|
||||
return explicitPath;
|
||||
}
|
||||
|
||||
// If explicit path is relative, resolve it relative to projectRoot
|
||||
// Normalize project root if provided
|
||||
const projectRoot = rawProjectRoot
|
||||
? normalizeProjectRoot(rawProjectRoot)
|
||||
: null;
|
||||
|
||||
// If explicit path is relative, resolve it relative to normalized projectRoot
|
||||
if (explicitPath && projectRoot) {
|
||||
return path.resolve(projectRoot, explicitPath);
|
||||
}
|
||||
|
||||
// Use core findComplexityReportPath with explicit path and projectRoot context
|
||||
// Use core findComplexityReportPath with explicit path and normalized projectRoot context
|
||||
if (projectRoot) {
|
||||
return coreFindComplexityReportPath(explicitPath, { projectRoot }, log);
|
||||
}
|
||||
@@ -142,13 +158,16 @@ export function resolveProjectPath(relativePath, args) {
|
||||
throw new Error('projectRoot is required in args to resolve project paths');
|
||||
}
|
||||
|
||||
// Normalize the project root to prevent double .taskmaster paths
|
||||
const projectRoot = normalizeProjectRoot(args.projectRoot);
|
||||
|
||||
// If already absolute, return as-is
|
||||
if (path.isAbsolute(relativePath)) {
|
||||
return relativePath;
|
||||
}
|
||||
|
||||
// Resolve relative to projectRoot
|
||||
return path.resolve(args.projectRoot, relativePath);
|
||||
// Resolve relative to normalized projectRoot
|
||||
return path.resolve(projectRoot, relativePath);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
} from './utils.js';
|
||||
import { complexityReportDirect } from '../core/task-master-core.js';
|
||||
import { COMPLEXITY_REPORT_FILE } from '../../../src/constants/paths.js';
|
||||
import path from 'path';
|
||||
import { findComplexityReportPath } from '../core/utils/path-utils.js';
|
||||
|
||||
/**
|
||||
* Register the complexityReport tool with the MCP server
|
||||
@@ -38,10 +38,18 @@ export function registerComplexityReportTool(server) {
|
||||
`Getting complexity report with args: ${JSON.stringify(args)}`
|
||||
);
|
||||
|
||||
// Use args.projectRoot directly (guaranteed by withNormalizedProjectRoot)
|
||||
const reportPath = args.file
|
||||
? path.resolve(args.projectRoot, args.file)
|
||||
: path.resolve(args.projectRoot, COMPLEXITY_REPORT_FILE);
|
||||
const pathArgs = {
|
||||
projectRoot: args.projectRoot,
|
||||
complexityReport: args.file
|
||||
};
|
||||
|
||||
const reportPath = findComplexityReportPath(pathArgs, log);
|
||||
|
||||
if (!reportPath) {
|
||||
return createErrorResponse(
|
||||
'No complexity report found. Run task-master analyze-complexity first.'
|
||||
);
|
||||
}
|
||||
|
||||
const result = await complexityReportDirect(
|
||||
{
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "task-master-ai",
|
||||
"version": "0.16.0",
|
||||
"version": "0.16.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "task-master-ai",
|
||||
"version": "0.16.0",
|
||||
"version": "0.16.1",
|
||||
"license": "MIT WITH Commons-Clause",
|
||||
"dependencies": {
|
||||
"@ai-sdk/amazon-bedrock": "^2.2.9",
|
||||
|
||||
@@ -16,6 +16,32 @@ import {
|
||||
} from '../constants/paths.js';
|
||||
import { getLoggerOrDefault } from './logger-utils.js';
|
||||
|
||||
/**
|
||||
* Normalize project root to ensure it doesn't end with .taskmaster
|
||||
* This prevents double .taskmaster paths when using constants that include .taskmaster
|
||||
* @param {string} projectRoot - The project root path to normalize
|
||||
* @returns {string} - Normalized project root path
|
||||
*/
|
||||
export function normalizeProjectRoot(projectRoot) {
|
||||
if (!projectRoot) return projectRoot;
|
||||
|
||||
// Split the path into segments
|
||||
const segments = projectRoot.split(path.sep);
|
||||
|
||||
// Find the index of .taskmaster segment
|
||||
const taskmasterIndex = segments.findIndex(
|
||||
(segment) => segment === '.taskmaster'
|
||||
);
|
||||
|
||||
if (taskmasterIndex !== -1) {
|
||||
// If .taskmaster is found, return everything up to but not including .taskmaster
|
||||
const normalizedSegments = segments.slice(0, taskmasterIndex);
|
||||
return normalizedSegments.join(path.sep) || path.sep;
|
||||
}
|
||||
|
||||
return projectRoot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the project root directory by looking for project markers
|
||||
* @param {string} startDir - Directory to start searching from
|
||||
@@ -80,14 +106,17 @@ export function findTasksPath(explicitPath = null, args = null, log = null) {
|
||||
}
|
||||
|
||||
// 2. Try to get project root from args (MCP) or find it
|
||||
const projectRoot = args?.projectRoot || findProjectRoot();
|
||||
const rawProjectRoot = args?.projectRoot || findProjectRoot();
|
||||
|
||||
if (!projectRoot) {
|
||||
if (!rawProjectRoot) {
|
||||
logger.warn?.('Could not determine project root directory');
|
||||
return null;
|
||||
}
|
||||
|
||||
// 3. Check possible locations in order of preference
|
||||
// 3. Normalize project root to prevent double .taskmaster paths
|
||||
const projectRoot = normalizeProjectRoot(rawProjectRoot);
|
||||
|
||||
// 4. Check possible locations in order of preference
|
||||
const possiblePaths = [
|
||||
path.join(projectRoot, TASKMASTER_TASKS_FILE), // .taskmaster/tasks/tasks.json (NEW)
|
||||
path.join(projectRoot, 'tasks.json'), // tasks.json in root (LEGACY)
|
||||
@@ -151,14 +180,17 @@ export function findPRDPath(explicitPath = null, args = null, log = null) {
|
||||
}
|
||||
|
||||
// 2. Try to get project root from args (MCP) or find it
|
||||
const projectRoot = args?.projectRoot || findProjectRoot();
|
||||
const rawProjectRoot = args?.projectRoot || findProjectRoot();
|
||||
|
||||
if (!projectRoot) {
|
||||
if (!rawProjectRoot) {
|
||||
logger.warn?.('Could not determine project root directory');
|
||||
return null;
|
||||
}
|
||||
|
||||
// 3. Check possible locations in order of preference
|
||||
// 3. Normalize project root to prevent double .taskmaster paths
|
||||
const projectRoot = normalizeProjectRoot(rawProjectRoot);
|
||||
|
||||
// 4. Check possible locations in order of preference
|
||||
const locations = [
|
||||
TASKMASTER_DOCS_DIR, // .taskmaster/docs/ (NEW)
|
||||
'scripts/', // Legacy location
|
||||
@@ -220,14 +252,17 @@ export function findComplexityReportPath(
|
||||
}
|
||||
|
||||
// 2. Try to get project root from args (MCP) or find it
|
||||
const projectRoot = args?.projectRoot || findProjectRoot();
|
||||
const rawProjectRoot = args?.projectRoot || findProjectRoot();
|
||||
|
||||
if (!projectRoot) {
|
||||
if (!rawProjectRoot) {
|
||||
logger.warn?.('Could not determine project root directory');
|
||||
return null;
|
||||
}
|
||||
|
||||
// 3. Check possible locations in order of preference
|
||||
// 3. Normalize project root to prevent double .taskmaster paths
|
||||
const projectRoot = normalizeProjectRoot(rawProjectRoot);
|
||||
|
||||
// 4. Check possible locations in order of preference
|
||||
const locations = [
|
||||
TASKMASTER_REPORTS_DIR, // .taskmaster/reports/ (NEW)
|
||||
'scripts/', // Legacy location
|
||||
@@ -283,9 +318,13 @@ export function resolveTasksOutputPath(
|
||||
}
|
||||
|
||||
// 2. Try to get project root from args (MCP) or find it
|
||||
const projectRoot = args?.projectRoot || findProjectRoot() || process.cwd();
|
||||
const rawProjectRoot =
|
||||
args?.projectRoot || findProjectRoot() || process.cwd();
|
||||
|
||||
// 3. Use new .taskmaster structure by default
|
||||
// 3. Normalize project root to prevent double .taskmaster paths
|
||||
const projectRoot = normalizeProjectRoot(rawProjectRoot);
|
||||
|
||||
// 4. Use new .taskmaster structure by default
|
||||
const defaultPath = path.join(projectRoot, TASKMASTER_TASKS_FILE);
|
||||
logger.info?.(`Using default output path: ${defaultPath}`);
|
||||
|
||||
@@ -326,9 +365,13 @@ export function resolveComplexityReportOutputPath(
|
||||
}
|
||||
|
||||
// 2. Try to get project root from args (MCP) or find it
|
||||
const projectRoot = args?.projectRoot || findProjectRoot() || process.cwd();
|
||||
const rawProjectRoot =
|
||||
args?.projectRoot || findProjectRoot() || process.cwd();
|
||||
|
||||
// 3. Use new .taskmaster structure by default
|
||||
// 3. Normalize project root to prevent double .taskmaster paths
|
||||
const projectRoot = normalizeProjectRoot(rawProjectRoot);
|
||||
|
||||
// 4. Use new .taskmaster structure by default
|
||||
const defaultPath = path.join(projectRoot, COMPLEXITY_REPORT_FILE);
|
||||
logger.info?.(`Using default complexity report output path: ${defaultPath}`);
|
||||
|
||||
@@ -369,14 +412,17 @@ export function findConfigPath(explicitPath = null, args = null, log = null) {
|
||||
}
|
||||
|
||||
// 2. Try to get project root from args (MCP) or find it
|
||||
const projectRoot = args?.projectRoot || findProjectRoot();
|
||||
const rawProjectRoot = args?.projectRoot || findProjectRoot();
|
||||
|
||||
if (!projectRoot) {
|
||||
if (!rawProjectRoot) {
|
||||
logger.warn?.('Could not determine project root directory');
|
||||
return null;
|
||||
}
|
||||
|
||||
// 3. Check possible locations in order of preference
|
||||
// 3. Normalize project root to prevent double .taskmaster paths
|
||||
const projectRoot = normalizeProjectRoot(rawProjectRoot);
|
||||
|
||||
// 4. Check possible locations in order of preference
|
||||
const possiblePaths = [
|
||||
path.join(projectRoot, TASKMASTER_CONFIG_FILE), // NEW location
|
||||
path.join(projectRoot, LEGACY_CONFIG_FILE) // LEGACY location
|
||||
|
||||
Reference in New Issue
Block a user