Compare commits
3 Commits
esm-module
...
fix/normal
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c9aa9faf59 | ||
|
|
9b4168bb4e | ||
|
|
ad612763ff |
5
.changeset/curly-months-go.md
Normal file
5
.changeset/curly-months-go.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"task-master-ai": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix bedrock issues
|
||||||
5
.changeset/dirty-webs-jam.md
Normal file
5
.changeset/dirty-webs-jam.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"task-master-ai": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix MCP tool calls logging errors
|
||||||
5
.changeset/loud-dolls-yell.md
Normal file
5
.changeset/loud-dolls-yell.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"task-master-ai": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Update rules for new directory structure
|
||||||
5
.changeset/odd-banks-fly.md
Normal file
5
.changeset/odd-banks-fly.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"task-master-ai": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix bug in expand_all mcp tool
|
||||||
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/tall-symbols-call.md
Normal file
5
.changeset/tall-symbols-call.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"task-master-ai": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix MCP crashing after certain commands due to console logs
|
||||||
@@ -45,7 +45,7 @@ This document provides a detailed reference for interacting with Taskmaster, cov
|
|||||||
* **Description:** `Parse a Product Requirements Document, PRD, or text file with Taskmaster to automatically generate an initial set of tasks in tasks.json.`
|
* **Description:** `Parse a Product Requirements Document, PRD, or text file with Taskmaster to automatically generate an initial set of tasks in tasks.json.`
|
||||||
* **Key Parameters/Options:**
|
* **Key Parameters/Options:**
|
||||||
* `input`: `Path to your PRD or requirements text file that Taskmaster should parse for tasks.` (CLI: `[file]` positional or `-i, --input <file>`)
|
* `input`: `Path to your PRD or requirements text file that Taskmaster should parse for tasks.` (CLI: `[file]` positional or `-i, --input <file>`)
|
||||||
* `output`: `Specify where Taskmaster should save the generated 'tasks.json' file. Defaults to 'tasks/tasks.json'.` (CLI: `-o, --output <file>`)
|
* `output`: `Specify where Taskmaster should save the generated 'tasks.json' file. Defaults to '.taskmaster/tasks/tasks.json'.` (CLI: `-o, --output <file>`)
|
||||||
* `numTasks`: `Approximate number of top-level tasks Taskmaster should aim to generate from the document.` (CLI: `-n, --num-tasks <number>`)
|
* `numTasks`: `Approximate number of top-level tasks Taskmaster should aim to generate from the document.` (CLI: `-n, --num-tasks <number>`)
|
||||||
* `force`: `Use this to allow Taskmaster to overwrite an existing 'tasks.json' without asking for confirmation.` (CLI: `-f, --force`)
|
* `force`: `Use this to allow Taskmaster to overwrite an existing 'tasks.json' without asking for confirmation.` (CLI: `-f, --force`)
|
||||||
* **Usage:** Useful for bootstrapping a project from an existing requirements document.
|
* **Usage:** Useful for bootstrapping a project from an existing requirements document.
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
"defaultPriority": "medium",
|
"defaultPriority": "medium",
|
||||||
"projectName": "Taskmaster",
|
"projectName": "Taskmaster",
|
||||||
"ollamaBaseURL": "http://localhost:11434/api",
|
"ollamaBaseURL": "http://localhost:11434/api",
|
||||||
|
"bedrockBaseURL": "https://bedrock.us-east-1.amazonaws.com",
|
||||||
"userId": "1234567890",
|
"userId": "1234567890",
|
||||||
"azureBaseURL": "https://your-endpoint.azure.com/"
|
"azureBaseURL": "https://your-endpoint.azure.com/"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
{
|
|
||||||
"models": {
|
|
||||||
"main": {
|
|
||||||
"provider": "anthropic",
|
|
||||||
"modelId": "claude-sonnet-4-20250514",
|
|
||||||
"maxTokens": 50000,
|
|
||||||
"temperature": 0.2
|
|
||||||
},
|
|
||||||
"research": {
|
|
||||||
"provider": "perplexity",
|
|
||||||
"modelId": "sonar-pro",
|
|
||||||
"maxTokens": 8700,
|
|
||||||
"temperature": 0.1
|
|
||||||
},
|
|
||||||
"fallback": {
|
|
||||||
"provider": "anthropic",
|
|
||||||
"modelId": "claude-3-7-sonnet-20250219",
|
|
||||||
"maxTokens": 128000,
|
|
||||||
"temperature": 0.2
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"global": {
|
|
||||||
"logLevel": "info",
|
|
||||||
"debug": false,
|
|
||||||
"defaultSubtasks": 5,
|
|
||||||
"defaultPriority": "medium",
|
|
||||||
"projectName": "Taskmaster",
|
|
||||||
"ollamaBaseURL": "http://localhost:11434/api",
|
|
||||||
"userId": "1234567890",
|
|
||||||
"azureBaseURL": "https://your-endpoint.azure.com/"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -56,20 +56,24 @@ task-master generate # Update task markd
|
|||||||
|
|
||||||
```
|
```
|
||||||
project/
|
project/
|
||||||
├── tasks/
|
├── .taskmaster/
|
||||||
│ ├── tasks.json # Main task database
|
│ ├── tasks/ # Task files directory
|
||||||
│ ├── task-1.md # Individual task files
|
│ │ ├── tasks.json # Main task database
|
||||||
│ └── task-2.md
|
│ │ ├── task-1.md # Individual task files
|
||||||
├── scripts/
|
│ │ └── task-2.md
|
||||||
│ ├── prd.txt # Product requirements
|
│ ├── docs/ # Documentation directory
|
||||||
│ └── task-complexity-report.json
|
│ │ ├── prd.txt # Product requirements
|
||||||
|
│ ├── reports/ # Analysis reports directory
|
||||||
|
│ │ └── task-complexity-report.json
|
||||||
|
│ ├── templates/ # Template files
|
||||||
|
│ │ └── example_prd.txt # Example PRD template
|
||||||
|
│ └── config.json # AI models & settings
|
||||||
├── .claude/
|
├── .claude/
|
||||||
│ ├── settings.json # Claude Code configuration
|
│ ├── settings.json # Claude Code configuration
|
||||||
│ └── commands/ # Custom slash commands
|
│ └── commands/ # Custom slash commands
|
||||||
├── .taskmasterconfig # AI models & settings
|
├── .env # API keys
|
||||||
├── .env # API keys
|
├── .mcp.json # MCP configuration
|
||||||
├── .mcp.json # MCP configuration
|
└── CLAUDE.md # This file - auto-loaded by Claude Code
|
||||||
└── CLAUDE.md # This file - auto-loaded by Claude Code
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## MCP Integration
|
## MCP Integration
|
||||||
@@ -384,7 +388,7 @@ These commands make AI calls and may take up to a minute:
|
|||||||
### File Management
|
### File Management
|
||||||
|
|
||||||
- Never manually edit `tasks.json` - use commands instead
|
- Never manually edit `tasks.json` - use commands instead
|
||||||
- Never manually edit `.taskmasterconfig` - use `task-master models`
|
- Never manually edit `.taskmaster/config.json` - use `task-master models`
|
||||||
- Task markdown files in `tasks/` are auto-generated
|
- Task markdown files in `tasks/` are auto-generated
|
||||||
- Run `task-master generate` after manual changes to tasks.json
|
- Run `task-master generate` after manual changes to tasks.json
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
"defaultPriority": "medium",
|
"defaultPriority": "medium",
|
||||||
"projectName": "Taskmaster",
|
"projectName": "Taskmaster",
|
||||||
"ollamaBaseURL": "http://localhost:11434/api",
|
"ollamaBaseURL": "http://localhost:11434/api",
|
||||||
"azureOpenaiBaseURL": "https://your-endpoint.openai.azure.com/"
|
"azureOpenaiBaseURL": "https://your-endpoint.openai.azure.com/",
|
||||||
|
"bedrockBaseURL": "https://bedrock.us-east-1.amazonaws.com"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,5 +5,5 @@ OPENAI_API_KEY="your_openai_api_key_here" # Optional, for OpenAI/Ope
|
|||||||
GOOGLE_API_KEY="your_google_api_key_here" # Optional, for Google Gemini models.
|
GOOGLE_API_KEY="your_google_api_key_here" # Optional, for Google Gemini models.
|
||||||
MISTRAL_API_KEY="your_mistral_key_here" # Optional, for Mistral AI models.
|
MISTRAL_API_KEY="your_mistral_key_here" # Optional, for Mistral AI models.
|
||||||
XAI_API_KEY="YOUR_XAI_KEY_HERE" # Optional, for xAI AI models.
|
XAI_API_KEY="YOUR_XAI_KEY_HERE" # Optional, for xAI AI models.
|
||||||
AZURE_OPENAI_API_KEY="your_azure_key_here" # Optional, for Azure OpenAI models (requires endpoint in .taskmasterconfig).
|
AZURE_OPENAI_API_KEY="your_azure_key_here" # Optional, for Azure OpenAI models (requires endpoint in .taskmaster/config.json).
|
||||||
OLLAMA_API_KEY="your_ollama_api_key_here" # Optional: For remote Ollama servers that require authentication.
|
OLLAMA_API_KEY="your_ollama_api_key_here" # Optional: For remote Ollama servers that require authentication.
|
||||||
@@ -20,7 +20,7 @@ In an AI-driven development process—particularly with tools like [Cursor](http
|
|||||||
|
|
||||||
Task Master configuration is now managed through two primary methods:
|
Task Master configuration is now managed through two primary methods:
|
||||||
|
|
||||||
1. **`.taskmasterconfig` File (Project Root - Primary)**
|
1. **`.taskmaster/config.json` File (Project Root - Primary)**
|
||||||
|
|
||||||
- Stores AI model selections (`main`, `research`, `fallback`), model parameters (`maxTokens`, `temperature`), `logLevel`, `defaultSubtasks`, `defaultPriority`, `projectName`, etc.
|
- Stores AI model selections (`main`, `research`, `fallback`), model parameters (`maxTokens`, `temperature`), `logLevel`, `defaultSubtasks`, `defaultPriority`, `projectName`, etc.
|
||||||
- Managed using the `task-master models --setup` command or the `models` MCP tool.
|
- Managed using the `task-master models --setup` command or the `models` MCP tool.
|
||||||
@@ -192,7 +192,7 @@ Notes:
|
|||||||
## AI Integration (Updated)
|
## AI Integration (Updated)
|
||||||
|
|
||||||
- The script now uses a unified AI service layer (`ai-services-unified.js`).
|
- The script now uses a unified AI service layer (`ai-services-unified.js`).
|
||||||
- Model selection (e.g., Claude vs. Perplexity for `--research`) is determined by the configuration in `.taskmasterconfig` based on the requested `role` (`main` or `research`).
|
- Model selection (e.g., Claude vs. Perplexity for `--research`) is determined by the configuration in `.taskmaster/config.json` based on the requested `role` (`main` or `research`).
|
||||||
- API keys are automatically resolved from your `.env` file (for CLI) or MCP session environment.
|
- API keys are automatically resolved from your `.env` file (for CLI) or MCP session environment.
|
||||||
- To use the research capabilities (e.g., `expand --research`), ensure you have:
|
- To use the research capabilities (e.g., `expand --research`), ensure you have:
|
||||||
1. Configured a model for the `research` role using `task-master models --setup` (Perplexity models are recommended).
|
1. Configured a model for the `research` role using `task-master models --setup` (Perplexity models are recommended).
|
||||||
@@ -357,25 +357,25 @@ The output report structure is:
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"meta": {
|
"meta": {
|
||||||
"generatedAt": "2023-06-15T12:34:56.789Z",
|
"generatedAt": "2023-06-15T12:34:56.789Z",
|
||||||
"tasksAnalyzed": 20,
|
"tasksAnalyzed": 20,
|
||||||
"thresholdScore": 5,
|
"thresholdScore": 5,
|
||||||
"projectName": "Your Project Name",
|
"projectName": "Your Project Name",
|
||||||
"usedResearch": true
|
"usedResearch": true
|
||||||
},
|
},
|
||||||
"complexityAnalysis": [
|
"complexityAnalysis": [
|
||||||
{
|
{
|
||||||
"taskId": 8,
|
"taskId": 8,
|
||||||
"taskTitle": "Develop Implementation Drift Handling",
|
"taskTitle": "Develop Implementation Drift Handling",
|
||||||
"complexityScore": 9.5,
|
"complexityScore": 9.5,
|
||||||
"recommendedSubtasks": 6,
|
"recommendedSubtasks": 6,
|
||||||
"expansionPrompt": "Create subtasks that handle detecting...",
|
"expansionPrompt": "Create subtasks that handle detecting...",
|
||||||
"reasoning": "This task requires sophisticated logic...",
|
"reasoning": "This task requires sophisticated logic...",
|
||||||
"expansionCommand": "task-master expand --id=8 --num=6 --prompt=\"Create subtasks...\" --research"
|
"expansionCommand": "task-master expand --id=8 --num=6 --prompt=\"Create subtasks...\" --research"
|
||||||
}
|
}
|
||||||
// More tasks sorted by complexity score (highest first)
|
// More tasks sorted by complexity score (highest first)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -28,8 +28,7 @@ export async function complexityReportDirect(args, log) {
|
|||||||
log.error('complexityReportDirect called without reportPath');
|
log.error('complexityReportDirect called without reportPath');
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: { code: 'MISSING_ARGUMENT', message: 'reportPath is required' },
|
error: { code: 'MISSING_ARGUMENT', message: 'reportPath is required' }
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,8 +110,7 @@ export async function complexityReportDirect(args, log) {
|
|||||||
error: {
|
error: {
|
||||||
code: 'UNEXPECTED_ERROR',
|
code: 'UNEXPECTED_ERROR',
|
||||||
message: error.message
|
message: error.message
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,8 @@ export async function expandAllTasksDirect(args, log, context = {}) {
|
|||||||
useResearch,
|
useResearch,
|
||||||
additionalContext,
|
additionalContext,
|
||||||
forceFlag,
|
forceFlag,
|
||||||
{ session, mcpLog, projectRoot }
|
{ session, mcpLog, projectRoot },
|
||||||
|
'json'
|
||||||
);
|
);
|
||||||
|
|
||||||
// Core function now returns a summary object including the *aggregated* telemetryData
|
// Core function now returns a summary object including the *aggregated* telemetryData
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import { createLogWrapper } from '../../tools/utils.js';
|
|||||||
* @param {Object} log - Logger object
|
* @param {Object} log - Logger object
|
||||||
* @param {Object} context - Context object containing session
|
* @param {Object} context - Context object containing session
|
||||||
* @param {Object} [context.session] - MCP Session object
|
* @param {Object} [context.session] - MCP Session object
|
||||||
* @returns {Promise<Object>} - Task expansion result { success: boolean, data?: any, error?: { code: string, message: string }, fromCache: boolean }
|
* @returns {Promise<Object>} - Task expansion result { success: boolean, data?: any, error?: { code: string, message: string } }
|
||||||
*/
|
*/
|
||||||
export async function expandTaskDirect(args, log, context = {}) {
|
export async function expandTaskDirect(args, log, context = {}) {
|
||||||
const { session } = context; // Extract session
|
const { session } = context; // Extract session
|
||||||
@@ -54,8 +54,7 @@ export async function expandTaskDirect(args, log, context = {}) {
|
|||||||
error: {
|
error: {
|
||||||
code: 'MISSING_ARGUMENT',
|
code: 'MISSING_ARGUMENT',
|
||||||
message: 'tasksJsonPath is required'
|
message: 'tasksJsonPath is required'
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,8 +72,7 @@ export async function expandTaskDirect(args, log, context = {}) {
|
|||||||
error: {
|
error: {
|
||||||
code: 'INPUT_VALIDATION_ERROR',
|
code: 'INPUT_VALIDATION_ERROR',
|
||||||
message: 'Task ID is required'
|
message: 'Task ID is required'
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,8 +103,7 @@ export async function expandTaskDirect(args, log, context = {}) {
|
|||||||
error: {
|
error: {
|
||||||
code: 'INVALID_TASKS_FILE',
|
code: 'INVALID_TASKS_FILE',
|
||||||
message: `No valid tasks found in ${tasksPath}. readJSON returned: ${JSON.stringify(data)}`
|
message: `No valid tasks found in ${tasksPath}. readJSON returned: ${JSON.stringify(data)}`
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,8 +118,7 @@ export async function expandTaskDirect(args, log, context = {}) {
|
|||||||
error: {
|
error: {
|
||||||
code: 'TASK_NOT_FOUND',
|
code: 'TASK_NOT_FOUND',
|
||||||
message: `Task with ID ${taskId} not found`
|
message: `Task with ID ${taskId} not found`
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,8 +129,7 @@ export async function expandTaskDirect(args, log, context = {}) {
|
|||||||
error: {
|
error: {
|
||||||
code: 'TASK_COMPLETED',
|
code: 'TASK_COMPLETED',
|
||||||
message: `Task ${taskId} is already marked as ${task.status} and cannot be expanded`
|
message: `Task ${taskId} is already marked as ${task.status} and cannot be expanded`
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,8 +146,7 @@ export async function expandTaskDirect(args, log, context = {}) {
|
|||||||
task,
|
task,
|
||||||
subtasksAdded: 0,
|
subtasksAdded: 0,
|
||||||
hasExistingSubtasks
|
hasExistingSubtasks
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,8 +226,7 @@ export async function expandTaskDirect(args, log, context = {}) {
|
|||||||
subtasksAdded,
|
subtasksAdded,
|
||||||
hasExistingSubtasks,
|
hasExistingSubtasks,
|
||||||
telemetryData: coreResult.telemetryData
|
telemetryData: coreResult.telemetryData
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Make sure to restore normal logging even if there's an error
|
// Make sure to restore normal logging even if there's an error
|
||||||
@@ -245,8 +238,7 @@ export async function expandTaskDirect(args, log, context = {}) {
|
|||||||
error: {
|
error: {
|
||||||
code: 'CORE_FUNCTION_ERROR',
|
code: 'CORE_FUNCTION_ERROR',
|
||||||
message: error.message || 'Failed to expand task'
|
message: error.message || 'Failed to expand task'
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -256,8 +248,7 @@ export async function expandTaskDirect(args, log, context = {}) {
|
|||||||
error: {
|
error: {
|
||||||
code: 'CORE_FUNCTION_ERROR',
|
code: 'CORE_FUNCTION_ERROR',
|
||||||
message: error.message || 'Failed to expand task'
|
message: error.message || 'Failed to expand task'
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,8 +28,7 @@ export async function generateTaskFilesDirect(args, log) {
|
|||||||
log.error(errorMessage);
|
log.error(errorMessage);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: { code: 'MISSING_ARGUMENT', message: errorMessage },
|
error: { code: 'MISSING_ARGUMENT', message: errorMessage }
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (!outputDir) {
|
if (!outputDir) {
|
||||||
@@ -37,8 +36,7 @@ export async function generateTaskFilesDirect(args, log) {
|
|||||||
log.error(errorMessage);
|
log.error(errorMessage);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: { code: 'MISSING_ARGUMENT', message: errorMessage },
|
error: { code: 'MISSING_ARGUMENT', message: errorMessage }
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,8 +63,7 @@ export async function generateTaskFilesDirect(args, log) {
|
|||||||
log.error(`Error in generateTaskFiles: ${genError.message}`);
|
log.error(`Error in generateTaskFiles: ${genError.message}`);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: { code: 'GENERATE_FILES_ERROR', message: genError.message },
|
error: { code: 'GENERATE_FILES_ERROR', message: genError.message }
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,8 +76,7 @@ export async function generateTaskFilesDirect(args, log) {
|
|||||||
outputDir: resolvedOutputDir,
|
outputDir: resolvedOutputDir,
|
||||||
taskFiles:
|
taskFiles:
|
||||||
'Individual task files have been generated in the output directory'
|
'Individual task files have been generated in the output directory'
|
||||||
},
|
}
|
||||||
fromCache: false // This operation always modifies state and should never be cached
|
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Make sure to restore normal logging if an outer error occurs
|
// Make sure to restore normal logging if an outer error occurs
|
||||||
@@ -92,8 +88,7 @@ export async function generateTaskFilesDirect(args, log) {
|
|||||||
error: {
|
error: {
|
||||||
code: 'GENERATE_TASKS_ERROR',
|
code: 'GENERATE_TASKS_ERROR',
|
||||||
message: error.message || 'Unknown error generating task files'
|
message: error.message || 'Unknown error generating task files'
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,8 +41,7 @@ export async function initializeProjectDirect(args, log, context = {}) {
|
|||||||
code: 'INVALID_TARGET_DIRECTORY',
|
code: 'INVALID_TARGET_DIRECTORY',
|
||||||
message: `Cannot initialize project: Invalid target directory '${targetDirectory}' received. Please ensure a valid workspace/folder is open or specified.`,
|
message: `Cannot initialize project: Invalid target directory '${targetDirectory}' received. Please ensure a valid workspace/folder is open or specified.`,
|
||||||
details: `Received args.projectRoot: ${args.projectRoot}` // Show what was received
|
details: `Received args.projectRoot: ${args.projectRoot}` // Show what was received
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,8 +96,8 @@ export async function initializeProjectDirect(args, log, context = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
return { success: true, data: resultData, fromCache: false };
|
return { success: true, data: resultData };
|
||||||
} else {
|
} else {
|
||||||
return { success: false, error: errorResult, fromCache: false };
|
return { success: false, error: errorResult };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
*
|
*
|
||||||
* @param {Object} args - Command arguments (now expecting tasksJsonPath explicitly).
|
* @param {Object} args - Command arguments (now expecting tasksJsonPath explicitly).
|
||||||
* @param {Object} log - Logger object.
|
* @param {Object} log - Logger object.
|
||||||
* @returns {Promise<Object>} - Task list result { success: boolean, data?: any, error?: { code: string, message: string }, fromCache: boolean }.
|
* @returns {Promise<Object>} - Task list result { success: boolean, data?: any, error?: { code: string, message: string } }.
|
||||||
*/
|
*/
|
||||||
export async function listTasksDirect(args, log) {
|
export async function listTasksDirect(args, log) {
|
||||||
// Destructure the explicit tasksJsonPath from args
|
// Destructure the explicit tasksJsonPath from args
|
||||||
@@ -27,8 +27,7 @@ export async function listTasksDirect(args, log) {
|
|||||||
error: {
|
error: {
|
||||||
code: 'MISSING_ARGUMENT',
|
code: 'MISSING_ARGUMENT',
|
||||||
message: 'tasksJsonPath is required'
|
message: 'tasksJsonPath is required'
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import {
|
|||||||
* @param {Object} args - Command arguments
|
* @param {Object} args - Command arguments
|
||||||
* @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
|
* @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
|
||||||
* @param {Object} log - Logger object
|
* @param {Object} log - Logger object
|
||||||
* @returns {Promise<Object>} - Next task result { success: boolean, data?: any, error?: { code: string, message: string }, fromCache: boolean }
|
* @returns {Promise<Object>} - Next task result { success: boolean, data?: any, error?: { code: string, message: string } }
|
||||||
*/
|
*/
|
||||||
export async function nextTaskDirect(args, log) {
|
export async function nextTaskDirect(args, log) {
|
||||||
// Destructure expected args
|
// Destructure expected args
|
||||||
@@ -32,8 +32,7 @@ export async function nextTaskDirect(args, log) {
|
|||||||
error: {
|
error: {
|
||||||
code: 'MISSING_ARGUMENT',
|
code: 'MISSING_ARGUMENT',
|
||||||
message: 'tasksJsonPath is required'
|
message: 'tasksJsonPath is required'
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +120,7 @@ export async function nextTaskDirect(args, log) {
|
|||||||
// Use the caching utility
|
// Use the caching utility
|
||||||
try {
|
try {
|
||||||
const result = await coreNextTaskAction();
|
const result = await coreNextTaskAction();
|
||||||
log.info(`nextTaskDirect completed.`);
|
log.info('nextTaskDirect completed.');
|
||||||
return result;
|
return result;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error(`Unexpected error during nextTask: ${error.message}`);
|
log.error(`Unexpected error during nextTask: ${error.message}`);
|
||||||
@@ -130,8 +129,7 @@ export async function nextTaskDirect(args, log) {
|
|||||||
error: {
|
error: {
|
||||||
code: 'UNEXPECTED_ERROR',
|
code: 'UNEXPECTED_ERROR',
|
||||||
message: error.message
|
message: error.message
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ export async function parsePRDDirect(args, log, context = {}) {
|
|||||||
? path.isAbsolute(outputArg)
|
? path.isAbsolute(outputArg)
|
||||||
? outputArg
|
? outputArg
|
||||||
: path.resolve(projectRoot, outputArg)
|
: path.resolve(projectRoot, outputArg)
|
||||||
: resolveProjectPath(TASKMASTER_TASKS_FILE, session) ||
|
: resolveProjectPath(TASKMASTER_TASKS_FILE, args) ||
|
||||||
path.resolve(projectRoot, TASKMASTER_TASKS_FILE);
|
path.resolve(projectRoot, TASKMASTER_TASKS_FILE);
|
||||||
|
|
||||||
// Check if input file exists
|
// Check if input file exists
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import {
|
|||||||
* @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
|
* @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
|
||||||
* @param {string} args.id - The ID(s) of the task(s) or subtask(s) to remove (comma-separated for multiple).
|
* @param {string} args.id - The ID(s) of the task(s) or subtask(s) to remove (comma-separated for multiple).
|
||||||
* @param {Object} log - Logger object
|
* @param {Object} log - Logger object
|
||||||
* @returns {Promise<Object>} - Remove task result { success: boolean, data?: any, error?: { code: string, message: string }, fromCache: false }
|
* @returns {Promise<Object>} - Remove task result { success: boolean, data?: any, error?: { code: string, message: string } }
|
||||||
*/
|
*/
|
||||||
export async function removeTaskDirect(args, log) {
|
export async function removeTaskDirect(args, log) {
|
||||||
// Destructure expected args
|
// Destructure expected args
|
||||||
@@ -35,8 +35,7 @@ export async function removeTaskDirect(args, log) {
|
|||||||
error: {
|
error: {
|
||||||
code: 'MISSING_ARGUMENT',
|
code: 'MISSING_ARGUMENT',
|
||||||
message: 'tasksJsonPath is required'
|
message: 'tasksJsonPath is required'
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,8 +47,7 @@ export async function removeTaskDirect(args, log) {
|
|||||||
error: {
|
error: {
|
||||||
code: 'INPUT_VALIDATION_ERROR',
|
code: 'INPUT_VALIDATION_ERROR',
|
||||||
message: 'Task ID is required'
|
message: 'Task ID is required'
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,8 +66,7 @@ export async function removeTaskDirect(args, log) {
|
|||||||
error: {
|
error: {
|
||||||
code: 'INVALID_TASKS_FILE',
|
code: 'INVALID_TASKS_FILE',
|
||||||
message: `No valid tasks found in ${tasksJsonPath}`
|
message: `No valid tasks found in ${tasksJsonPath}`
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,8 +80,7 @@ export async function removeTaskDirect(args, log) {
|
|||||||
error: {
|
error: {
|
||||||
code: 'INVALID_TASK_ID',
|
code: 'INVALID_TASK_ID',
|
||||||
message: `The following tasks were not found: ${invalidTasks.join(', ')}`
|
message: `The following tasks were not found: ${invalidTasks.join(', ')}`
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,8 +129,7 @@ export async function removeTaskDirect(args, log) {
|
|||||||
details: failedRemovals
|
details: failedRemovals
|
||||||
.map((r) => `${r.taskId}: ${r.error}`)
|
.map((r) => `${r.taskId}: ${r.error}`)
|
||||||
.join('; ')
|
.join('; ')
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,8 +142,7 @@ export async function removeTaskDirect(args, log) {
|
|||||||
failed: failedRemovals.length,
|
failed: failedRemovals.length,
|
||||||
results: results,
|
results: results,
|
||||||
tasksPath: tasksJsonPath
|
tasksPath: tasksJsonPath
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Ensure silent mode is disabled even if an outer error occurs
|
// Ensure silent mode is disabled even if an outer error occurs
|
||||||
@@ -161,8 +155,7 @@ export async function removeTaskDirect(args, log) {
|
|||||||
error: {
|
error: {
|
||||||
code: 'UNEXPECTED_ERROR',
|
code: 'UNEXPECTED_ERROR',
|
||||||
message: error.message
|
message: error.message
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,8 +29,7 @@ export async function setTaskStatusDirect(args, log) {
|
|||||||
log.error(errorMessage);
|
log.error(errorMessage);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: { code: 'MISSING_ARGUMENT', message: errorMessage },
|
error: { code: 'MISSING_ARGUMENT', message: errorMessage }
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,8 +40,7 @@ export async function setTaskStatusDirect(args, log) {
|
|||||||
log.error(errorMessage);
|
log.error(errorMessage);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: { code: 'MISSING_TASK_ID', message: errorMessage },
|
error: { code: 'MISSING_TASK_ID', message: errorMessage }
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,8 +50,7 @@ export async function setTaskStatusDirect(args, log) {
|
|||||||
log.error(errorMessage);
|
log.error(errorMessage);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: { code: 'MISSING_STATUS', message: errorMessage },
|
error: { code: 'MISSING_STATUS', message: errorMessage }
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,8 +79,7 @@ export async function setTaskStatusDirect(args, log) {
|
|||||||
taskId,
|
taskId,
|
||||||
status: newStatus,
|
status: newStatus,
|
||||||
tasksPath: tasksPath // Return the path used
|
tasksPath: tasksPath // Return the path used
|
||||||
},
|
}
|
||||||
fromCache: false // This operation always modifies state and should never be cached
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// If the task was completed, attempt to fetch the next task
|
// If the task was completed, attempt to fetch the next task
|
||||||
@@ -126,8 +122,7 @@ export async function setTaskStatusDirect(args, log) {
|
|||||||
error: {
|
error: {
|
||||||
code: 'SET_STATUS_ERROR',
|
code: 'SET_STATUS_ERROR',
|
||||||
message: error.message || 'Unknown error setting task status'
|
message: error.message || 'Unknown error setting task status'
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
} finally {
|
} finally {
|
||||||
// ALWAYS restore normal logging in finally block
|
// ALWAYS restore normal logging in finally block
|
||||||
@@ -145,8 +140,7 @@ export async function setTaskStatusDirect(args, log) {
|
|||||||
error: {
|
error: {
|
||||||
code: 'SET_STATUS_ERROR',
|
code: 'SET_STATUS_ERROR',
|
||||||
message: error.message || 'Unknown error setting task status'
|
message: error.message || 'Unknown error setting task status'
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,8 +42,7 @@ export async function updateSubtaskByIdDirect(args, log, context = {}) {
|
|||||||
logWrapper.error(errorMessage);
|
logWrapper.error(errorMessage);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: { code: 'MISSING_ARGUMENT', message: errorMessage },
|
error: { code: 'MISSING_ARGUMENT', message: errorMessage }
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,8 +53,7 @@ export async function updateSubtaskByIdDirect(args, log, context = {}) {
|
|||||||
logWrapper.error(errorMessage);
|
logWrapper.error(errorMessage);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: { code: 'INVALID_SUBTASK_ID', message: errorMessage },
|
error: { code: 'INVALID_SUBTASK_ID', message: errorMessage }
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,8 +63,7 @@ export async function updateSubtaskByIdDirect(args, log, context = {}) {
|
|||||||
logWrapper.error(errorMessage);
|
logWrapper.error(errorMessage);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: { code: 'MISSING_PROMPT', message: errorMessage },
|
error: { code: 'MISSING_PROMPT', message: errorMessage }
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,8 +74,7 @@ export async function updateSubtaskByIdDirect(args, log, context = {}) {
|
|||||||
log.error(errorMessage);
|
log.error(errorMessage);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: { code: 'INVALID_SUBTASK_ID_TYPE', message: errorMessage },
|
error: { code: 'INVALID_SUBTASK_ID_TYPE', message: errorMessage }
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,8 +84,7 @@ export async function updateSubtaskByIdDirect(args, log, context = {}) {
|
|||||||
log.error(errorMessage);
|
log.error(errorMessage);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: { code: 'INVALID_SUBTASK_ID_FORMAT', message: errorMessage },
|
error: { code: 'INVALID_SUBTASK_ID_FORMAT', message: errorMessage }
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,8 +123,7 @@ export async function updateSubtaskByIdDirect(args, log, context = {}) {
|
|||||||
logWrapper.error(message);
|
logWrapper.error(message);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: { code: 'SUBTASK_NOT_FOUND', message: message },
|
error: { code: 'SUBTASK_NOT_FOUND', message: message }
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,8 +140,7 @@ export async function updateSubtaskByIdDirect(args, log, context = {}) {
|
|||||||
tasksPath,
|
tasksPath,
|
||||||
useResearch,
|
useResearch,
|
||||||
telemetryData: coreResult.telemetryData
|
telemetryData: coreResult.telemetryData
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logWrapper.error(`Error updating subtask by ID: ${error.message}`);
|
logWrapper.error(`Error updating subtask by ID: ${error.message}`);
|
||||||
@@ -156,8 +149,7 @@ export async function updateSubtaskByIdDirect(args, log, context = {}) {
|
|||||||
error: {
|
error: {
|
||||||
code: 'UPDATE_SUBTASK_CORE_ERROR',
|
code: 'UPDATE_SUBTASK_CORE_ERROR',
|
||||||
message: error.message || 'Unknown error updating subtask'
|
message: error.message || 'Unknown error updating subtask'
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
} finally {
|
} finally {
|
||||||
if (!wasSilent && isSilentMode()) {
|
if (!wasSilent && isSilentMode()) {
|
||||||
@@ -174,8 +166,7 @@ export async function updateSubtaskByIdDirect(args, log, context = {}) {
|
|||||||
error: {
|
error: {
|
||||||
code: 'DIRECT_FUNCTION_SETUP_ERROR',
|
code: 'DIRECT_FUNCTION_SETUP_ERROR',
|
||||||
message: error.message || 'Unknown setup error'
|
message: error.message || 'Unknown setup error'
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,8 +42,7 @@ export async function updateTaskByIdDirect(args, log, context = {}) {
|
|||||||
logWrapper.error(errorMessage);
|
logWrapper.error(errorMessage);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: { code: 'MISSING_ARGUMENT', message: errorMessage },
|
error: { code: 'MISSING_ARGUMENT', message: errorMessage }
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,8 +53,7 @@ export async function updateTaskByIdDirect(args, log, context = {}) {
|
|||||||
logWrapper.error(errorMessage);
|
logWrapper.error(errorMessage);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: { code: 'MISSING_TASK_ID', message: errorMessage },
|
error: { code: 'MISSING_TASK_ID', message: errorMessage }
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,8 +63,7 @@ export async function updateTaskByIdDirect(args, log, context = {}) {
|
|||||||
logWrapper.error(errorMessage);
|
logWrapper.error(errorMessage);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: { code: 'MISSING_PROMPT', message: errorMessage },
|
error: { code: 'MISSING_PROMPT', message: errorMessage }
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,8 +81,7 @@ export async function updateTaskByIdDirect(args, log, context = {}) {
|
|||||||
logWrapper.error(errorMessage);
|
logWrapper.error(errorMessage);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: { code: 'INVALID_TASK_ID', message: errorMessage },
|
error: { code: 'INVALID_TASK_ID', message: errorMessage }
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,8 +133,7 @@ export async function updateTaskByIdDirect(args, log, context = {}) {
|
|||||||
taskId: taskId,
|
taskId: taskId,
|
||||||
updated: false,
|
updated: false,
|
||||||
telemetryData: coreResult?.telemetryData
|
telemetryData: coreResult?.telemetryData
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,8 +150,7 @@ export async function updateTaskByIdDirect(args, log, context = {}) {
|
|||||||
updated: true,
|
updated: true,
|
||||||
updatedTask: coreResult.updatedTask,
|
updatedTask: coreResult.updatedTask,
|
||||||
telemetryData: coreResult.telemetryData
|
telemetryData: coreResult.telemetryData
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logWrapper.error(`Error updating task by ID: ${error.message}`);
|
logWrapper.error(`Error updating task by ID: ${error.message}`);
|
||||||
@@ -165,8 +159,7 @@ export async function updateTaskByIdDirect(args, log, context = {}) {
|
|||||||
error: {
|
error: {
|
||||||
code: 'UPDATE_TASK_CORE_ERROR',
|
code: 'UPDATE_TASK_CORE_ERROR',
|
||||||
message: error.message || 'Unknown error updating task'
|
message: error.message || 'Unknown error updating task'
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
} finally {
|
} finally {
|
||||||
if (!wasSilent && isSilentMode()) {
|
if (!wasSilent && isSilentMode()) {
|
||||||
@@ -181,8 +174,7 @@ export async function updateTaskByIdDirect(args, log, context = {}) {
|
|||||||
error: {
|
error: {
|
||||||
code: 'DIRECT_FUNCTION_SETUP_ERROR',
|
code: 'DIRECT_FUNCTION_SETUP_ERROR',
|
||||||
message: error.message || 'Unknown setup error'
|
message: error.message || 'Unknown setup error'
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs';
|
|
||||||
import {
|
import {
|
||||||
findTasksPath as coreFindTasksPath,
|
findTasksPath as coreFindTasksPath,
|
||||||
findPRDPath as coreFindPrdPath,
|
findPRDPath as coreFindPrdPath,
|
||||||
findComplexityReportPath as coreFindComplexityReportPath,
|
findComplexityReportPath as coreFindComplexityReportPath,
|
||||||
findProjectRoot as coreFindProjectRoot
|
findProjectRoot as coreFindProjectRoot,
|
||||||
|
normalizeProjectRoot
|
||||||
} from '../../../../src/utils/path-utils.js';
|
} from '../../../../src/utils/path-utils.js';
|
||||||
import { PROJECT_MARKERS } from '../../../../src/constants/paths.js';
|
import { PROJECT_MARKERS } from '../../../../src/constants/paths.js';
|
||||||
|
|
||||||
@@ -13,22 +13,22 @@ import { PROJECT_MARKERS } from '../../../../src/constants/paths.js';
|
|||||||
* This module handles session-specific path resolution for the MCP server
|
* This module handles session-specific path resolution for the MCP server
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Silent logger for MCP context to prevent console output
|
||||||
|
*/
|
||||||
|
const silentLogger = {
|
||||||
|
info: () => {},
|
||||||
|
warn: () => {},
|
||||||
|
error: () => {},
|
||||||
|
debug: () => {},
|
||||||
|
success: () => {}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cache for last found project root to improve performance
|
* Cache for last found project root to improve performance
|
||||||
*/
|
*/
|
||||||
export const lastFoundProjectRoot = null;
|
export const lastFoundProjectRoot = null;
|
||||||
|
|
||||||
/**
|
|
||||||
* Find tasks.json file with MCP support
|
|
||||||
* @param {string} [explicitPath] - Explicit path to tasks.json (highest priority)
|
|
||||||
* @param {Object} [args] - Arguments object for context
|
|
||||||
* @param {Object} [log] - Logger object to prevent console logging
|
|
||||||
* @returns {string|null} - Resolved path to tasks.json or null if not found
|
|
||||||
*/
|
|
||||||
export function findTasksPathCore(explicitPath, args = null, log = null) {
|
|
||||||
return coreFindTasksPath(explicitPath, args, log);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find PRD file with MCP support
|
* Find PRD file with MCP support
|
||||||
* @param {string} [explicitPath] - Explicit path to PRD file (highest priority)
|
* @param {string} [explicitPath] - Explicit path to PRD file (highest priority)
|
||||||
@@ -36,25 +36,10 @@ export function findTasksPathCore(explicitPath, args = null, log = null) {
|
|||||||
* @param {Object} [log] - Logger object to prevent console logging
|
* @param {Object} [log] - Logger object to prevent console logging
|
||||||
* @returns {string|null} - Resolved path to PRD file or null if not found
|
* @returns {string|null} - Resolved path to PRD file or null if not found
|
||||||
*/
|
*/
|
||||||
export function findPrdPath(explicitPath, args = null, log = null) {
|
export function findPrdPath(explicitPath, args = null, log = silentLogger) {
|
||||||
return coreFindPrdPath(explicitPath, args, log);
|
return coreFindPrdPath(explicitPath, args, log);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Find complexity report file with MCP support
|
|
||||||
* @param {string} [explicitPath] - Explicit path to complexity report (highest priority)
|
|
||||||
* @param {Object} [args] - Arguments object for context
|
|
||||||
* @param {Object} [log] - Logger object to prevent console logging
|
|
||||||
* @returns {string|null} - Resolved path to complexity report or null if not found
|
|
||||||
*/
|
|
||||||
export function findComplexityReportPathCore(
|
|
||||||
explicitPath,
|
|
||||||
args = null,
|
|
||||||
log = null
|
|
||||||
) {
|
|
||||||
return coreFindComplexityReportPath(explicitPath, args, log);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve tasks.json path from arguments
|
* Resolve tasks.json path from arguments
|
||||||
* Prioritizes explicit path parameter, then uses fallback logic
|
* Prioritizes explicit path parameter, then uses fallback logic
|
||||||
@@ -62,22 +47,27 @@ export function findComplexityReportPathCore(
|
|||||||
* @param {Object} [log] - Logger object to prevent console logging
|
* @param {Object} [log] - Logger object to prevent console logging
|
||||||
* @returns {string|null} - Resolved path to tasks.json or null if not found
|
* @returns {string|null} - Resolved path to tasks.json or null if not found
|
||||||
*/
|
*/
|
||||||
export function resolveTasksPath(args, log = null) {
|
export function resolveTasksPath(args, log = silentLogger) {
|
||||||
// Get explicit path from args.file if provided
|
// Get explicit path from args.file if provided
|
||||||
const explicitPath = args?.file;
|
const explicitPath = args?.file;
|
||||||
const projectRoot = args?.projectRoot;
|
const rawProjectRoot = args?.projectRoot;
|
||||||
|
|
||||||
// If explicit path is provided and absolute, use it directly
|
// If explicit path is provided and absolute, use it directly
|
||||||
if (explicitPath && path.isAbsolute(explicitPath)) {
|
if (explicitPath && path.isAbsolute(explicitPath)) {
|
||||||
return 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) {
|
if (explicitPath && projectRoot) {
|
||||||
return path.resolve(projectRoot, explicitPath);
|
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) {
|
if (projectRoot) {
|
||||||
return coreFindTasksPath(explicitPath, { projectRoot }, log);
|
return coreFindTasksPath(explicitPath, { projectRoot }, log);
|
||||||
}
|
}
|
||||||
@@ -92,22 +82,27 @@ export function resolveTasksPath(args, log = null) {
|
|||||||
* @param {Object} [log] - Logger object to prevent console logging
|
* @param {Object} [log] - Logger object to prevent console logging
|
||||||
* @returns {string|null} - Resolved path to PRD file or null if not found
|
* @returns {string|null} - Resolved path to PRD file or null if not found
|
||||||
*/
|
*/
|
||||||
export function resolvePrdPath(args, log = null) {
|
export function resolvePrdPath(args, log = silentLogger) {
|
||||||
// Get explicit path from args.input if provided
|
// Get explicit path from args.input if provided
|
||||||
const explicitPath = args?.input;
|
const explicitPath = args?.input;
|
||||||
const projectRoot = args?.projectRoot;
|
const rawProjectRoot = args?.projectRoot;
|
||||||
|
|
||||||
// If explicit path is provided and absolute, use it directly
|
// If explicit path is provided and absolute, use it directly
|
||||||
if (explicitPath && path.isAbsolute(explicitPath)) {
|
if (explicitPath && path.isAbsolute(explicitPath)) {
|
||||||
return 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) {
|
if (explicitPath && projectRoot) {
|
||||||
return path.resolve(projectRoot, explicitPath);
|
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) {
|
if (projectRoot) {
|
||||||
return coreFindPrdPath(explicitPath, { projectRoot }, log);
|
return coreFindPrdPath(explicitPath, { projectRoot }, log);
|
||||||
}
|
}
|
||||||
@@ -122,22 +117,27 @@ export function resolvePrdPath(args, log = null) {
|
|||||||
* @param {Object} [log] - Logger object to prevent console logging
|
* @param {Object} [log] - Logger object to prevent console logging
|
||||||
* @returns {string|null} - Resolved path to complexity report or null if not found
|
* @returns {string|null} - Resolved path to complexity report or null if not found
|
||||||
*/
|
*/
|
||||||
export function resolveComplexityReportPath(args, log = null) {
|
export function resolveComplexityReportPath(args, log = silentLogger) {
|
||||||
// Get explicit path from args.complexityReport if provided
|
// Get explicit path from args.complexityReport if provided
|
||||||
const explicitPath = args?.complexityReport;
|
const explicitPath = args?.complexityReport;
|
||||||
const projectRoot = args?.projectRoot;
|
const rawProjectRoot = args?.projectRoot;
|
||||||
|
|
||||||
// If explicit path is provided and absolute, use it directly
|
// If explicit path is provided and absolute, use it directly
|
||||||
if (explicitPath && path.isAbsolute(explicitPath)) {
|
if (explicitPath && path.isAbsolute(explicitPath)) {
|
||||||
return 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) {
|
if (explicitPath && projectRoot) {
|
||||||
return path.resolve(projectRoot, explicitPath);
|
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) {
|
if (projectRoot) {
|
||||||
return coreFindComplexityReportPath(explicitPath, { projectRoot }, log);
|
return coreFindComplexityReportPath(explicitPath, { projectRoot }, log);
|
||||||
}
|
}
|
||||||
@@ -158,13 +158,16 @@ export function resolveProjectPath(relativePath, args) {
|
|||||||
throw new Error('projectRoot is required in args to resolve project paths');
|
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 already absolute, return as-is
|
||||||
if (path.isAbsolute(relativePath)) {
|
if (path.isAbsolute(relativePath)) {
|
||||||
return relativePath;
|
return relativePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve relative to projectRoot
|
// Resolve relative to normalized projectRoot
|
||||||
return path.resolve(args.projectRoot, relativePath);
|
return path.resolve(projectRoot, relativePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -184,7 +187,7 @@ export function findProjectRoot(startDir) {
|
|||||||
* @param {Object} [log] - Log function to prevent console logging
|
* @param {Object} [log] - Log function to prevent console logging
|
||||||
* @returns {string|null} - Resolved path to tasks.json or null if not found
|
* @returns {string|null} - Resolved path to tasks.json or null if not found
|
||||||
*/
|
*/
|
||||||
export function findTasksPath(args, log = null) {
|
export function findTasksPath(args, log = silentLogger) {
|
||||||
return resolveTasksPath(args, log);
|
return resolveTasksPath(args, log);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,7 +197,7 @@ export function findTasksPath(args, log = null) {
|
|||||||
* @param {Object} [log] - Log function to prevent console logging
|
* @param {Object} [log] - Log function to prevent console logging
|
||||||
* @returns {string|null} - Resolved path to complexity report or null if not found
|
* @returns {string|null} - Resolved path to complexity report or null if not found
|
||||||
*/
|
*/
|
||||||
export function findComplexityReportPath(args, log = null) {
|
export function findComplexityReportPath(args, log = silentLogger) {
|
||||||
return resolveComplexityReportPath(args, log);
|
return resolveComplexityReportPath(args, log);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,7 +208,7 @@ export function findComplexityReportPath(args, log = null) {
|
|||||||
* @param {Object} [log] - Logger object to prevent console logging
|
* @param {Object} [log] - Logger object to prevent console logging
|
||||||
* @returns {string|null} - Resolved path to PRD file or null if not found
|
* @returns {string|null} - Resolved path to PRD file or null if not found
|
||||||
*/
|
*/
|
||||||
export function findPRDPath(explicitPath, args = null, log = null) {
|
export function findPRDPath(explicitPath, args = null, log = silentLogger) {
|
||||||
return findPrdPath(explicitPath, args, log);
|
return findPrdPath(explicitPath, args, log);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
import { complexityReportDirect } from '../core/task-master-core.js';
|
import { complexityReportDirect } from '../core/task-master-core.js';
|
||||||
import { COMPLEXITY_REPORT_FILE } from '../../../src/constants/paths.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
|
* Register the complexityReport tool with the MCP server
|
||||||
@@ -38,10 +38,18 @@ export function registerComplexityReportTool(server) {
|
|||||||
`Getting complexity report with args: ${JSON.stringify(args)}`
|
`Getting complexity report with args: ${JSON.stringify(args)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
// Use args.projectRoot directly (guaranteed by withNormalizedProjectRoot)
|
const pathArgs = {
|
||||||
const reportPath = args.file
|
projectRoot: args.projectRoot,
|
||||||
? path.resolve(args.projectRoot, args.file)
|
complexityReport: args.file
|
||||||
: path.resolve(args.projectRoot, COMPLEXITY_REPORT_FILE);
|
};
|
||||||
|
|
||||||
|
const reportPath = findComplexityReportPath(pathArgs, log);
|
||||||
|
|
||||||
|
if (!reportPath) {
|
||||||
|
return createErrorResponse(
|
||||||
|
'No complexity report found. Run task-master analyze-complexity first.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const result = await complexityReportDirect(
|
const result = await complexityReportDirect(
|
||||||
{
|
{
|
||||||
@@ -51,9 +59,7 @@ export function registerComplexityReportTool(server) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
log.info(
|
log.info('Successfully retrieved complexity report');
|
||||||
`Successfully retrieved complexity report${result.fromCache ? ' (from cache)' : ''}`
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
log.error(
|
log.error(
|
||||||
`Failed to retrieve complexity report: ${result.error.message}`
|
`Failed to retrieve complexity report: ${result.error.message}`
|
||||||
|
|||||||
@@ -116,9 +116,7 @@ export function registerShowTaskTool(server) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
log.info(
|
log.info(`Successfully retrieved task details for ID: ${args.id}`);
|
||||||
`Successfully retrieved task details for ID: ${args.id}${result.fromCache ? ' (from cache)' : ''}`
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
log.error(`Failed to get task: ${result.error.message}`);
|
log.error(`Failed to get task: ${result.error.message}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export function registerListTasksTool(server) {
|
|||||||
// Resolve the path to tasks.json using new path utilities
|
// Resolve the path to tasks.json using new path utilities
|
||||||
let tasksJsonPath;
|
let tasksJsonPath;
|
||||||
try {
|
try {
|
||||||
tasksJsonPath = resolveTasksPath(args, session);
|
tasksJsonPath = resolveTasksPath(args, log);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error(`Error finding tasks.json: ${error.message}`);
|
log.error(`Error finding tasks.json: ${error.message}`);
|
||||||
return createErrorResponse(
|
return createErrorResponse(
|
||||||
@@ -87,7 +87,7 @@ export function registerListTasksTool(server) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
log.info(
|
log.info(
|
||||||
`Retrieved ${result.success ? result.data?.tasks?.length || 0 : 0} tasks${result.fromCache ? ' (from cache)' : ''}`
|
`Retrieved ${result.success ? result.data?.tasks?.length || 0 : 0} tasks`
|
||||||
);
|
);
|
||||||
return handleApiResult(result, log, 'Error getting tasks');
|
return handleApiResult(result, log, 'Error getting tasks');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ export function registerModelsTool(server) {
|
|||||||
),
|
),
|
||||||
projectRoot: z
|
projectRoot: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
|
||||||
.describe('The directory of the project. Must be an absolute path.'),
|
.describe('The directory of the project. Must be an absolute path.'),
|
||||||
openrouter: z
|
openrouter: z
|
||||||
.boolean()
|
.boolean()
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ export function registerMoveTaskTool(server) {
|
|||||||
file: z.string().optional().describe('Custom path to tasks.json file'),
|
file: z.string().optional().describe('Custom path to tasks.json file'),
|
||||||
projectRoot: z
|
projectRoot: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
|
||||||
.describe(
|
.describe(
|
||||||
'Root directory of the project (typically derived from session)'
|
'Root directory of the project (typically derived from session)'
|
||||||
)
|
)
|
||||||
@@ -95,13 +94,16 @@ export function registerMoveTaskTool(server) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return handleApiResult(
|
||||||
success: true,
|
{
|
||||||
data: {
|
success: true,
|
||||||
moves: results,
|
data: {
|
||||||
message: `Successfully moved ${results.length} tasks`
|
moves: results,
|
||||||
}
|
message: `Successfully moved ${results.length} tasks`
|
||||||
};
|
}
|
||||||
|
},
|
||||||
|
log
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
// Moving a single task
|
// Moving a single task
|
||||||
return handleApiResult(
|
return handleApiResult(
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ export function registerParsePRDTool(server) {
|
|||||||
.describe('Absolute path to the PRD document file (.txt, .md, etc.)'),
|
.describe('Absolute path to the PRD document file (.txt, .md, etc.)'),
|
||||||
projectRoot: z
|
projectRoot: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
|
||||||
.describe('The directory of the project. Must be an absolute path.'),
|
.describe('The directory of the project. Must be an absolute path.'),
|
||||||
output: z
|
output: z
|
||||||
.string()
|
.string()
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { spawnSync } from 'child_process';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { contextManager } from '../core/context-manager.js'; // Import the singleton
|
import { contextManager } from '../core/context-manager.js'; // Import the singleton
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
// Import path utilities to ensure consistent path resolution
|
// Import path utilities to ensure consistent path resolution
|
||||||
import {
|
import {
|
||||||
@@ -14,6 +15,50 @@ import {
|
|||||||
PROJECT_MARKERS
|
PROJECT_MARKERS
|
||||||
} from '../core/utils/path-utils.js';
|
} from '../core/utils/path-utils.js';
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
|
||||||
|
// Cache for version info to avoid repeated file reads
|
||||||
|
let cachedVersionInfo = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get version information from package.json
|
||||||
|
* @returns {Object} Version information
|
||||||
|
*/
|
||||||
|
function getVersionInfo() {
|
||||||
|
// Return cached version if available
|
||||||
|
if (cachedVersionInfo) {
|
||||||
|
return cachedVersionInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Navigate to the project root from the tools directory
|
||||||
|
const packageJsonPath = path.join(
|
||||||
|
path.dirname(__filename),
|
||||||
|
'../../../package.json'
|
||||||
|
);
|
||||||
|
if (fs.existsSync(packageJsonPath)) {
|
||||||
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
||||||
|
cachedVersionInfo = {
|
||||||
|
version: packageJson.version,
|
||||||
|
name: packageJson.name
|
||||||
|
};
|
||||||
|
return cachedVersionInfo;
|
||||||
|
}
|
||||||
|
cachedVersionInfo = {
|
||||||
|
version: 'unknown',
|
||||||
|
name: 'task-master-ai'
|
||||||
|
};
|
||||||
|
return cachedVersionInfo;
|
||||||
|
} catch (error) {
|
||||||
|
// Fallback version info if package.json can't be read
|
||||||
|
cachedVersionInfo = {
|
||||||
|
version: 'unknown',
|
||||||
|
name: 'task-master-ai'
|
||||||
|
};
|
||||||
|
return cachedVersionInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get normalized project root path
|
* Get normalized project root path
|
||||||
* @param {string|undefined} projectRootRaw - Raw project root from arguments
|
* @param {string|undefined} projectRootRaw - Raw project root from arguments
|
||||||
@@ -199,17 +244,19 @@ function getProjectRootFromSession(session, log) {
|
|||||||
* @param {Function} processFunction - Optional function to process successful result data
|
* @param {Function} processFunction - Optional function to process successful result data
|
||||||
* @returns {Object} - Standardized MCP response object
|
* @returns {Object} - Standardized MCP response object
|
||||||
*/
|
*/
|
||||||
function handleApiResult(
|
async function handleApiResult(
|
||||||
result,
|
result,
|
||||||
log,
|
log,
|
||||||
errorPrefix = 'API error',
|
errorPrefix = 'API error',
|
||||||
processFunction = processMCPResponseData
|
processFunction = processMCPResponseData
|
||||||
) {
|
) {
|
||||||
|
// Get version info for every response
|
||||||
|
const versionInfo = getVersionInfo();
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
const errorMsg = result.error?.message || `Unknown ${errorPrefix}`;
|
const errorMsg = result.error?.message || `Unknown ${errorPrefix}`;
|
||||||
// Include cache status in error logs
|
log.error(`${errorPrefix}: ${errorMsg}`);
|
||||||
log.error(`${errorPrefix}: ${errorMsg}. From cache: ${result.fromCache}`); // Keep logging cache status on error
|
return createErrorResponse(errorMsg, versionInfo);
|
||||||
return createErrorResponse(errorMsg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process the result data if needed
|
// Process the result data if needed
|
||||||
@@ -217,16 +264,14 @@ function handleApiResult(
|
|||||||
? processFunction(result.data)
|
? processFunction(result.data)
|
||||||
: result.data;
|
: result.data;
|
||||||
|
|
||||||
// Log success including cache status
|
log.info('Successfully completed operation');
|
||||||
log.info(`Successfully completed operation. From cache: ${result.fromCache}`); // Add success log with cache status
|
|
||||||
|
|
||||||
// Create the response payload including the fromCache flag
|
// Create the response payload including version info
|
||||||
const responsePayload = {
|
const responsePayload = {
|
||||||
fromCache: result.fromCache, // Get the flag from the original 'result'
|
data: processedData,
|
||||||
data: processedData // Nest the processed data under a 'data' key
|
version: versionInfo
|
||||||
};
|
};
|
||||||
|
|
||||||
// Pass this combined payload to createContentResponse
|
|
||||||
return createContentResponse(responsePayload);
|
return createContentResponse(responsePayload);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,8 +365,8 @@ function executeTaskMasterCommand(
|
|||||||
* @param {Function} options.actionFn - The async function to execute if the cache misses.
|
* @param {Function} options.actionFn - The async function to execute if the cache misses.
|
||||||
* Should return an object like { success: boolean, data?: any, error?: { code: string, message: string } }.
|
* Should return an object like { success: boolean, data?: any, error?: { code: string, message: string } }.
|
||||||
* @param {Object} options.log - The logger instance.
|
* @param {Object} options.log - The logger instance.
|
||||||
* @returns {Promise<Object>} - An object containing the result, indicating if it was from cache.
|
* @returns {Promise<Object>} - An object containing the result.
|
||||||
* Format: { success: boolean, data?: any, error?: { code: string, message: string }, fromCache: boolean }
|
* Format: { success: boolean, data?: any, error?: { code: string, message: string } }
|
||||||
*/
|
*/
|
||||||
async function getCachedOrExecute({ cacheKey, actionFn, log }) {
|
async function getCachedOrExecute({ cacheKey, actionFn, log }) {
|
||||||
// Check cache first
|
// Check cache first
|
||||||
@@ -329,11 +374,7 @@ async function getCachedOrExecute({ cacheKey, actionFn, log }) {
|
|||||||
|
|
||||||
if (cachedResult !== undefined) {
|
if (cachedResult !== undefined) {
|
||||||
log.info(`Cache hit for key: ${cacheKey}`);
|
log.info(`Cache hit for key: ${cacheKey}`);
|
||||||
// Return the cached data in the same structure as a fresh result
|
return cachedResult;
|
||||||
return {
|
|
||||||
...cachedResult, // Spread the cached result to maintain its structure
|
|
||||||
fromCache: true // Just add the fromCache flag
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info(`Cache miss for key: ${cacheKey}. Executing action function.`);
|
log.info(`Cache miss for key: ${cacheKey}. Executing action function.`);
|
||||||
@@ -341,12 +382,10 @@ async function getCachedOrExecute({ cacheKey, actionFn, log }) {
|
|||||||
// Execute the action function if cache missed
|
// Execute the action function if cache missed
|
||||||
const result = await actionFn();
|
const result = await actionFn();
|
||||||
|
|
||||||
// If the action was successful, cache the result (but without fromCache flag)
|
// If the action was successful, cache the result
|
||||||
if (result.success && result.data !== undefined) {
|
if (result.success && result.data !== undefined) {
|
||||||
log.info(`Action successful. Caching result for key: ${cacheKey}`);
|
log.info(`Action successful. Caching result for key: ${cacheKey}`);
|
||||||
// Cache the entire result structure (minus the fromCache flag)
|
contextManager.setCachedData(cacheKey, result);
|
||||||
const { fromCache, ...resultToCache } = result;
|
|
||||||
contextManager.setCachedData(cacheKey, resultToCache);
|
|
||||||
} else if (!result.success) {
|
} else if (!result.success) {
|
||||||
log.warn(
|
log.warn(
|
||||||
`Action failed for cache key ${cacheKey}. Result not cached. Error: ${result.error?.message}`
|
`Action failed for cache key ${cacheKey}. Result not cached. Error: ${result.error?.message}`
|
||||||
@@ -357,11 +396,7 @@ async function getCachedOrExecute({ cacheKey, actionFn, log }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the fresh result, indicating it wasn't from cache
|
return result;
|
||||||
return {
|
|
||||||
...result,
|
|
||||||
fromCache: false
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -460,14 +495,22 @@ function createContentResponse(content) {
|
|||||||
/**
|
/**
|
||||||
* Creates error response for tools
|
* Creates error response for tools
|
||||||
* @param {string} errorMessage - Error message to include in response
|
* @param {string} errorMessage - Error message to include in response
|
||||||
|
* @param {Object} [versionInfo] - Optional version information object
|
||||||
* @returns {Object} - Error content response object in FastMCP format
|
* @returns {Object} - Error content response object in FastMCP format
|
||||||
*/
|
*/
|
||||||
function createErrorResponse(errorMessage) {
|
function createErrorResponse(errorMessage, versionInfo) {
|
||||||
|
// Provide fallback version info if not provided
|
||||||
|
if (!versionInfo) {
|
||||||
|
versionInfo = getVersionInfo();
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [
|
content: [
|
||||||
{
|
{
|
||||||
type: 'text',
|
type: 'text',
|
||||||
text: `Error: ${errorMessage}`
|
text: `Error: ${errorMessage}
|
||||||
|
Version: ${versionInfo.version}
|
||||||
|
Name: ${versionInfo.name}`
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
isError: true
|
isError: true
|
||||||
|
|||||||
@@ -509,9 +509,9 @@ function createProjectStructure(addAliases, dryRun, options) {
|
|||||||
replacements
|
replacements
|
||||||
);
|
);
|
||||||
|
|
||||||
// Copy .taskmasterconfig with project name to NEW location
|
// Copy config.json with project name to NEW location
|
||||||
copyTemplateFile(
|
copyTemplateFile(
|
||||||
'.taskmasterconfig',
|
'config.json',
|
||||||
path.join(targetDir, TASKMASTER_CONFIG_FILE),
|
path.join(targetDir, TASKMASTER_CONFIG_FILE),
|
||||||
{
|
{
|
||||||
...replacements
|
...replacements
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import {
|
|||||||
isApiKeySet,
|
isApiKeySet,
|
||||||
getOllamaBaseURL,
|
getOllamaBaseURL,
|
||||||
getAzureBaseURL,
|
getAzureBaseURL,
|
||||||
|
getBedrockBaseURL,
|
||||||
getVertexProjectId,
|
getVertexProjectId,
|
||||||
getVertexLocation
|
getVertexLocation
|
||||||
} from './config-manager.js';
|
} from './config-manager.js';
|
||||||
@@ -410,6 +411,10 @@ async function _unifiedServiceRunner(serviceType, params) {
|
|||||||
// For Ollama, use the global Ollama base URL if role-specific URL is not configured
|
// For Ollama, use the global Ollama base URL if role-specific URL is not configured
|
||||||
baseURL = getOllamaBaseURL(effectiveProjectRoot);
|
baseURL = getOllamaBaseURL(effectiveProjectRoot);
|
||||||
log('debug', `Using global Ollama base URL: ${baseURL}`);
|
log('debug', `Using global Ollama base URL: ${baseURL}`);
|
||||||
|
} else if (providerName?.toLowerCase() === 'bedrock' && !baseURL) {
|
||||||
|
// For Bedrock, use the global Bedrock base URL if role-specific URL is not configured
|
||||||
|
baseURL = getBedrockBaseURL(effectiveProjectRoot);
|
||||||
|
log('debug', `Using global Bedrock base URL: ${baseURL}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get AI parameters for the current role
|
// Get AI parameters for the current role
|
||||||
|
|||||||
@@ -61,7 +61,8 @@ const DEFAULTS = {
|
|||||||
defaultSubtasks: 5,
|
defaultSubtasks: 5,
|
||||||
defaultPriority: 'medium',
|
defaultPriority: 'medium',
|
||||||
projectName: 'Task Master',
|
projectName: 'Task Master',
|
||||||
ollamaBaseURL: 'http://localhost:11434/api'
|
ollamaBaseURL: 'http://localhost:11434/api',
|
||||||
|
bedrockBaseURL: 'https://bedrock.us-east-1.amazonaws.com'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -382,6 +383,11 @@ function getAzureBaseURL(explicitRoot = null) {
|
|||||||
return getGlobalConfig(explicitRoot).azureBaseURL;
|
return getGlobalConfig(explicitRoot).azureBaseURL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getBedrockBaseURL(explicitRoot = null) {
|
||||||
|
// Directly return value from config
|
||||||
|
return getGlobalConfig(explicitRoot).bedrockBaseURL;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the Google Cloud project ID for Vertex AI from configuration
|
* Gets the Google Cloud project ID for Vertex AI from configuration
|
||||||
* @param {string|null} explicitRoot - Optional explicit path to the project root.
|
* @param {string|null} explicitRoot - Optional explicit path to the project root.
|
||||||
@@ -779,6 +785,7 @@ export {
|
|||||||
getProjectName,
|
getProjectName,
|
||||||
getOllamaBaseURL,
|
getOllamaBaseURL,
|
||||||
getAzureBaseURL,
|
getAzureBaseURL,
|
||||||
|
getBedrockBaseURL,
|
||||||
getParametersForRole,
|
getParametersForRole,
|
||||||
getUserId,
|
getUserId,
|
||||||
// API Key Checkers (still relevant)
|
// API Key Checkers (still relevant)
|
||||||
|
|||||||
@@ -482,6 +482,11 @@ async function setModel(role, modelId, options = {}) {
|
|||||||
`Model ID "${modelId}" not found in the Ollama instance. Please verify the model is pulled and available. You can check available models with: curl ${tagsUrl}`
|
`Model ID "${modelId}" not found in the Ollama instance. Please verify the model is pulled and available. You can check available models with: curl ${tagsUrl}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else if (providerHint === 'bedrock') {
|
||||||
|
// Set provider without model validation since Bedrock models are managed by AWS
|
||||||
|
determinedProvider = 'bedrock';
|
||||||
|
warningMessage = `Warning: Custom Bedrock model '${modelId}' set. Please ensure the model ID is valid and accessible in your AWS account.`;
|
||||||
|
report('warn', warningMessage);
|
||||||
} else {
|
} else {
|
||||||
// Invalid provider hint - should not happen
|
// Invalid provider hint - should not happen
|
||||||
throw new Error(`Invalid provider hint received: ${providerHint}`);
|
throw new Error(`Invalid provider hint received: ${providerHint}`);
|
||||||
|
|||||||
@@ -24,7 +24,10 @@ import {
|
|||||||
} from './task-manager.js';
|
} from './task-manager.js';
|
||||||
import { getProjectName, getDefaultSubtasks } from './config-manager.js';
|
import { getProjectName, getDefaultSubtasks } from './config-manager.js';
|
||||||
import { TASK_STATUS_OPTIONS } from '../../src/constants/task-status.js';
|
import { TASK_STATUS_OPTIONS } from '../../src/constants/task-status.js';
|
||||||
import { TASKMASTER_TASKS_FILE } from '../../src/constants/paths.js';
|
import {
|
||||||
|
TASKMASTER_CONFIG_FILE,
|
||||||
|
TASKMASTER_TASKS_FILE
|
||||||
|
} from '../../src/constants/paths.js';
|
||||||
import { getTaskMasterVersion } from '../../src/utils/getVersion.js';
|
import { getTaskMasterVersion } from '../../src/utils/getVersion.js';
|
||||||
|
|
||||||
// Create a color gradient for the banner
|
// Create a color gradient for the banner
|
||||||
@@ -687,7 +690,7 @@ function displayHelp() {
|
|||||||
|
|
||||||
configTable.push(
|
configTable.push(
|
||||||
[
|
[
|
||||||
`${chalk.yellow('.taskmasterconfig')}${chalk.reset('')}`,
|
`${chalk.yellow(TASKMASTER_CONFIG_FILE)}${chalk.reset('')}`,
|
||||||
`${chalk.white('AI model configuration file (project root)')}${chalk.reset('')}`,
|
`${chalk.white('AI model configuration file (project root)')}${chalk.reset('')}`,
|
||||||
`${chalk.dim('Managed by models cmd')}${chalk.reset('')}`
|
`${chalk.dim('Managed by models cmd')}${chalk.reset('')}`
|
||||||
],
|
],
|
||||||
@@ -1851,7 +1854,7 @@ function displayApiKeyStatus(statusReport) {
|
|||||||
console.log(table.toString());
|
console.log(table.toString());
|
||||||
console.log(
|
console.log(
|
||||||
chalk.gray(
|
chalk.gray(
|
||||||
' Note: Some providers (e.g., Azure, Ollama) may require additional endpoint configuration in .taskmasterconfig.'
|
` Note: Some providers (e.g., Azure, Ollama) may require additional endpoint configuration in ${TASKMASTER_CONFIG_FILE}.`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,8 +155,17 @@ function log(level, ...args) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get log level dynamically from config-manager
|
// GUARD: Prevent circular dependency during config loading
|
||||||
const configLevel = getLogLevel() || 'info'; // Use getter
|
// Use a simple fallback log level instead of calling getLogLevel()
|
||||||
|
let configLevel = 'info'; // Default fallback
|
||||||
|
try {
|
||||||
|
// Only try to get config level if we're not in the middle of config loading
|
||||||
|
configLevel = getLogLevel() || 'info';
|
||||||
|
} catch (error) {
|
||||||
|
// If getLogLevel() fails (likely due to circular dependency),
|
||||||
|
// use default 'info' level and continue
|
||||||
|
configLevel = 'info';
|
||||||
|
}
|
||||||
|
|
||||||
// Use text prefixes instead of emojis
|
// Use text prefixes instead of emojis
|
||||||
const prefixes = {
|
const prefixes = {
|
||||||
@@ -190,8 +199,17 @@ function log(level, ...args) {
|
|||||||
* @returns {Object|null} Parsed JSON data or null if error occurs
|
* @returns {Object|null} Parsed JSON data or null if error occurs
|
||||||
*/
|
*/
|
||||||
function readJSON(filepath) {
|
function readJSON(filepath) {
|
||||||
// Get debug flag dynamically from config-manager
|
// GUARD: Prevent circular dependency during config loading
|
||||||
const isDebug = getDebugFlag();
|
let isDebug = false; // Default fallback
|
||||||
|
try {
|
||||||
|
// Only try to get debug flag if we're not in the middle of config loading
|
||||||
|
isDebug = getDebugFlag();
|
||||||
|
} catch (error) {
|
||||||
|
// If getDebugFlag() fails (likely due to circular dependency),
|
||||||
|
// use default false and continue
|
||||||
|
isDebug = false;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const rawData = fs.readFileSync(filepath, 'utf8');
|
const rawData = fs.readFileSync(filepath, 'utf8');
|
||||||
return JSON.parse(rawData);
|
return JSON.parse(rawData);
|
||||||
@@ -212,8 +230,17 @@ function readJSON(filepath) {
|
|||||||
* @param {Object} data - Data to write
|
* @param {Object} data - Data to write
|
||||||
*/
|
*/
|
||||||
function writeJSON(filepath, data) {
|
function writeJSON(filepath, data) {
|
||||||
// Get debug flag dynamically from config-manager
|
// GUARD: Prevent circular dependency during config loading
|
||||||
const isDebug = getDebugFlag();
|
let isDebug = false; // Default fallback
|
||||||
|
try {
|
||||||
|
// Only try to get debug flag if we're not in the middle of config loading
|
||||||
|
isDebug = getDebugFlag();
|
||||||
|
} catch (error) {
|
||||||
|
// If getDebugFlag() fails (likely due to circular dependency),
|
||||||
|
// use default false and continue
|
||||||
|
isDebug = false;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const dir = path.dirname(filepath);
|
const dir = path.dirname(filepath);
|
||||||
if (!fs.existsSync(dir)) {
|
if (!fs.existsSync(dir)) {
|
||||||
@@ -246,8 +273,17 @@ function sanitizePrompt(prompt) {
|
|||||||
* @returns {Object|null} The parsed complexity report or null if not found
|
* @returns {Object|null} The parsed complexity report or null if not found
|
||||||
*/
|
*/
|
||||||
function readComplexityReport(customPath = null) {
|
function readComplexityReport(customPath = null) {
|
||||||
// Get debug flag dynamically from config-manager
|
// GUARD: Prevent circular dependency during config loading
|
||||||
const isDebug = getDebugFlag();
|
let isDebug = false; // Default fallback
|
||||||
|
try {
|
||||||
|
// Only try to get debug flag if we're not in the middle of config loading
|
||||||
|
isDebug = getDebugFlag();
|
||||||
|
} catch (error) {
|
||||||
|
// If getDebugFlag() fails (likely due to circular dependency),
|
||||||
|
// use default false and continue
|
||||||
|
isDebug = false;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let reportPath;
|
let reportPath;
|
||||||
if (customPath) {
|
if (customPath) {
|
||||||
|
|||||||
31
src/utils/logger-utils.js
Normal file
31
src/utils/logger-utils.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* Logger utility functions for Task Master
|
||||||
|
* Provides standardized logging patterns for both CLI and utility contexts
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { log as utilLog } from '../../scripts/modules/utils.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a standard logger object that wraps the utility log function
|
||||||
|
* This provides a consistent logger interface across different parts of the application
|
||||||
|
* @returns {Object} A logger object with standard logging methods (info, warn, error, debug, success)
|
||||||
|
*/
|
||||||
|
export function createStandardLogger() {
|
||||||
|
return {
|
||||||
|
info: (msg, ...args) => utilLog('info', msg, ...args),
|
||||||
|
warn: (msg, ...args) => utilLog('warn', msg, ...args),
|
||||||
|
error: (msg, ...args) => utilLog('error', msg, ...args),
|
||||||
|
debug: (msg, ...args) => utilLog('debug', msg, ...args),
|
||||||
|
success: (msg, ...args) => utilLog('success', msg, ...args)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a logger using either the provided logger or a default standard logger
|
||||||
|
* This is the recommended pattern for functions that accept an optional logger parameter
|
||||||
|
* @param {Object|null} providedLogger - Optional logger object passed from caller
|
||||||
|
* @returns {Object} A logger object with standard logging methods
|
||||||
|
*/
|
||||||
|
export function getLoggerOrDefault(providedLogger = null) {
|
||||||
|
return providedLogger || createStandardLogger();
|
||||||
|
}
|
||||||
@@ -14,6 +14,33 @@ import {
|
|||||||
TASKMASTER_CONFIG_FILE,
|
TASKMASTER_CONFIG_FILE,
|
||||||
LEGACY_CONFIG_FILE
|
LEGACY_CONFIG_FILE
|
||||||
} from '../constants/paths.js';
|
} 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
|
* Find the project root directory by looking for project markers
|
||||||
@@ -59,7 +86,8 @@ export function findProjectRoot(startDir = process.cwd()) {
|
|||||||
* @returns {string|null} - Resolved tasks.json path or null if not found
|
* @returns {string|null} - Resolved tasks.json path or null if not found
|
||||||
*/
|
*/
|
||||||
export function findTasksPath(explicitPath = null, args = null, log = null) {
|
export function findTasksPath(explicitPath = null, args = null, log = null) {
|
||||||
const logger = log || console;
|
// Use the passed logger if available, otherwise use the default logger
|
||||||
|
const logger = getLoggerOrDefault(log);
|
||||||
|
|
||||||
// 1. If explicit path is provided, use it (highest priority)
|
// 1. If explicit path is provided, use it (highest priority)
|
||||||
if (explicitPath) {
|
if (explicitPath) {
|
||||||
@@ -78,14 +106,17 @@ export function findTasksPath(explicitPath = null, args = null, log = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2. Try to get project root from args (MCP) or find it
|
// 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');
|
logger.warn?.('Could not determine project root directory');
|
||||||
return null;
|
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 = [
|
const possiblePaths = [
|
||||||
path.join(projectRoot, TASKMASTER_TASKS_FILE), // .taskmaster/tasks/tasks.json (NEW)
|
path.join(projectRoot, TASKMASTER_TASKS_FILE), // .taskmaster/tasks/tasks.json (NEW)
|
||||||
path.join(projectRoot, 'tasks.json'), // tasks.json in root (LEGACY)
|
path.join(projectRoot, 'tasks.json'), // tasks.json in root (LEGACY)
|
||||||
@@ -130,7 +161,7 @@ export function findTasksPath(explicitPath = null, args = null, log = null) {
|
|||||||
* @returns {string|null} - Resolved PRD document path or null if not found
|
* @returns {string|null} - Resolved PRD document path or null if not found
|
||||||
*/
|
*/
|
||||||
export function findPRDPath(explicitPath = null, args = null, log = null) {
|
export function findPRDPath(explicitPath = null, args = null, log = null) {
|
||||||
const logger = log || console;
|
const logger = getLoggerOrDefault(log);
|
||||||
|
|
||||||
// 1. If explicit path is provided, use it (highest priority)
|
// 1. If explicit path is provided, use it (highest priority)
|
||||||
if (explicitPath) {
|
if (explicitPath) {
|
||||||
@@ -149,14 +180,17 @@ export function findPRDPath(explicitPath = null, args = null, log = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2. Try to get project root from args (MCP) or find it
|
// 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');
|
logger.warn?.('Could not determine project root directory');
|
||||||
return null;
|
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 = [
|
const locations = [
|
||||||
TASKMASTER_DOCS_DIR, // .taskmaster/docs/ (NEW)
|
TASKMASTER_DOCS_DIR, // .taskmaster/docs/ (NEW)
|
||||||
'scripts/', // Legacy location
|
'scripts/', // Legacy location
|
||||||
@@ -199,7 +233,7 @@ export function findComplexityReportPath(
|
|||||||
args = null,
|
args = null,
|
||||||
log = null
|
log = null
|
||||||
) {
|
) {
|
||||||
const logger = log || console;
|
const logger = getLoggerOrDefault(log);
|
||||||
|
|
||||||
// 1. If explicit path is provided, use it (highest priority)
|
// 1. If explicit path is provided, use it (highest priority)
|
||||||
if (explicitPath) {
|
if (explicitPath) {
|
||||||
@@ -218,14 +252,17 @@ export function findComplexityReportPath(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2. Try to get project root from args (MCP) or find it
|
// 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');
|
logger.warn?.('Could not determine project root directory');
|
||||||
return null;
|
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 = [
|
const locations = [
|
||||||
TASKMASTER_REPORTS_DIR, // .taskmaster/reports/ (NEW)
|
TASKMASTER_REPORTS_DIR, // .taskmaster/reports/ (NEW)
|
||||||
'scripts/', // Legacy location
|
'scripts/', // Legacy location
|
||||||
@@ -268,7 +305,7 @@ export function resolveTasksOutputPath(
|
|||||||
args = null,
|
args = null,
|
||||||
log = null
|
log = null
|
||||||
) {
|
) {
|
||||||
const logger = log || console;
|
const logger = getLoggerOrDefault(log);
|
||||||
|
|
||||||
// 1. If explicit path is provided, use it
|
// 1. If explicit path is provided, use it
|
||||||
if (explicitPath) {
|
if (explicitPath) {
|
||||||
@@ -281,9 +318,13 @@ export function resolveTasksOutputPath(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2. Try to get project root from args (MCP) or find it
|
// 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);
|
const defaultPath = path.join(projectRoot, TASKMASTER_TASKS_FILE);
|
||||||
logger.info?.(`Using default output path: ${defaultPath}`);
|
logger.info?.(`Using default output path: ${defaultPath}`);
|
||||||
|
|
||||||
@@ -309,7 +350,7 @@ export function resolveComplexityReportOutputPath(
|
|||||||
args = null,
|
args = null,
|
||||||
log = null
|
log = null
|
||||||
) {
|
) {
|
||||||
const logger = log || console;
|
const logger = getLoggerOrDefault(log);
|
||||||
|
|
||||||
// 1. If explicit path is provided, use it
|
// 1. If explicit path is provided, use it
|
||||||
if (explicitPath) {
|
if (explicitPath) {
|
||||||
@@ -324,9 +365,13 @@ export function resolveComplexityReportOutputPath(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2. Try to get project root from args (MCP) or find it
|
// 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);
|
const defaultPath = path.join(projectRoot, COMPLEXITY_REPORT_FILE);
|
||||||
logger.info?.(`Using default complexity report output path: ${defaultPath}`);
|
logger.info?.(`Using default complexity report output path: ${defaultPath}`);
|
||||||
|
|
||||||
@@ -348,7 +393,7 @@ export function resolveComplexityReportOutputPath(
|
|||||||
* @returns {string|null} - Resolved config file path or null if not found
|
* @returns {string|null} - Resolved config file path or null if not found
|
||||||
*/
|
*/
|
||||||
export function findConfigPath(explicitPath = null, args = null, log = null) {
|
export function findConfigPath(explicitPath = null, args = null, log = null) {
|
||||||
const logger = log || console;
|
const logger = getLoggerOrDefault(log);
|
||||||
|
|
||||||
// 1. If explicit path is provided, use it (highest priority)
|
// 1. If explicit path is provided, use it (highest priority)
|
||||||
if (explicitPath) {
|
if (explicitPath) {
|
||||||
@@ -367,14 +412,17 @@ export function findConfigPath(explicitPath = null, args = null, log = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2. Try to get project root from args (MCP) or find it
|
// 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');
|
logger.warn?.('Could not determine project root directory');
|
||||||
return null;
|
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 = [
|
const possiblePaths = [
|
||||||
path.join(projectRoot, TASKMASTER_CONFIG_FILE), // NEW location
|
path.join(projectRoot, TASKMASTER_CONFIG_FILE), // NEW location
|
||||||
path.join(projectRoot, LEGACY_CONFIG_FILE) // LEGACY location
|
path.join(projectRoot, LEGACY_CONFIG_FILE) // LEGACY location
|
||||||
@@ -382,12 +430,6 @@ export function findConfigPath(explicitPath = null, args = null, log = null) {
|
|||||||
|
|
||||||
for (const configPath of possiblePaths) {
|
for (const configPath of possiblePaths) {
|
||||||
if (fs.existsSync(configPath)) {
|
if (fs.existsSync(configPath)) {
|
||||||
try {
|
|
||||||
logger.info?.(`Found config file at: ${configPath}`);
|
|
||||||
} catch (error) {
|
|
||||||
// Silently handle logging errors during testing
|
|
||||||
}
|
|
||||||
|
|
||||||
// Issue deprecation warning for legacy paths
|
// Issue deprecation warning for legacy paths
|
||||||
if (configPath?.endsWith(LEGACY_CONFIG_FILE)) {
|
if (configPath?.endsWith(LEGACY_CONFIG_FILE)) {
|
||||||
logger.warn?.(
|
logger.warn?.(
|
||||||
|
|||||||
@@ -253,8 +253,7 @@ describe('MCP Server Direct Functions', () => {
|
|||||||
error: {
|
error: {
|
||||||
code: 'FILE_NOT_FOUND_ERROR',
|
code: 'FILE_NOT_FOUND_ERROR',
|
||||||
message: 'Tasks file not found'
|
message: 'Tasks file not found'
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,8 +287,7 @@ describe('MCP Server Direct Functions', () => {
|
|||||||
.length,
|
.length,
|
||||||
pending: tasksData.filter((t) => t.status === 'pending').length
|
pending: tasksData.filter((t) => t.status === 'pending').length
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,8 +303,7 @@ describe('MCP Server Direct Functions', () => {
|
|||||||
total: tasksData.length,
|
total: tasksData.length,
|
||||||
filtered: filteredTasks.length
|
filtered: filteredTasks.length
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,8 +317,7 @@ describe('MCP Server Direct Functions', () => {
|
|||||||
stats: {
|
stats: {
|
||||||
total: tasksData.length
|
total: tasksData.length
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -441,8 +437,7 @@ describe('MCP Server Direct Functions', () => {
|
|||||||
error: {
|
error: {
|
||||||
code: 'INPUT_VALIDATION_ERROR',
|
code: 'INPUT_VALIDATION_ERROR',
|
||||||
message: 'Task ID is required'
|
message: 'Task ID is required'
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -454,8 +449,7 @@ describe('MCP Server Direct Functions', () => {
|
|||||||
error: {
|
error: {
|
||||||
code: 'TASK_NOT_FOUND',
|
code: 'TASK_NOT_FOUND',
|
||||||
message: `Task with ID ${args.id} not found`
|
message: `Task with ID ${args.id} not found`
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -469,8 +463,7 @@ describe('MCP Server Direct Functions', () => {
|
|||||||
error: {
|
error: {
|
||||||
code: 'TASK_COMPLETED',
|
code: 'TASK_COMPLETED',
|
||||||
message: `Task ${args.id} is already marked as done and cannot be expanded`
|
message: `Task ${args.id} is already marked as done and cannot be expanded`
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -495,8 +488,7 @@ describe('MCP Server Direct Functions', () => {
|
|||||||
task: expandedTask,
|
task: expandedTask,
|
||||||
subtasksAdded: expandedTask.subtasks.length,
|
subtasksAdded: expandedTask.subtasks.length,
|
||||||
hasExistingSubtasks: false
|
hasExistingSubtasks: false
|
||||||
},
|
}
|
||||||
fromCache: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ const mockGetBaseUrlForRole = jest.fn();
|
|||||||
const mockGetAllProviders = jest.fn();
|
const mockGetAllProviders = jest.fn();
|
||||||
const mockGetOllamaBaseURL = jest.fn();
|
const mockGetOllamaBaseURL = jest.fn();
|
||||||
const mockGetAzureBaseURL = jest.fn();
|
const mockGetAzureBaseURL = jest.fn();
|
||||||
|
const mockGetBedrockBaseURL = jest.fn();
|
||||||
const mockGetVertexProjectId = jest.fn();
|
const mockGetVertexProjectId = jest.fn();
|
||||||
const mockGetVertexLocation = jest.fn();
|
const mockGetVertexLocation = jest.fn();
|
||||||
const mockGetAvailableModels = jest.fn();
|
const mockGetAvailableModels = jest.fn();
|
||||||
@@ -113,6 +114,7 @@ jest.unstable_mockModule('../../scripts/modules/config-manager.js', () => ({
|
|||||||
getAllProviders: mockGetAllProviders,
|
getAllProviders: mockGetAllProviders,
|
||||||
getOllamaBaseURL: mockGetOllamaBaseURL,
|
getOllamaBaseURL: mockGetOllamaBaseURL,
|
||||||
getAzureBaseURL: mockGetAzureBaseURL,
|
getAzureBaseURL: mockGetAzureBaseURL,
|
||||||
|
getBedrockBaseURL: mockGetBedrockBaseURL,
|
||||||
getVertexProjectId: mockGetVertexProjectId,
|
getVertexProjectId: mockGetVertexProjectId,
|
||||||
getVertexLocation: mockGetVertexLocation,
|
getVertexLocation: mockGetVertexLocation,
|
||||||
getMcpApiKeyStatus: mockGetMcpApiKeyStatus
|
getMcpApiKeyStatus: mockGetMcpApiKeyStatus
|
||||||
|
|||||||
@@ -139,7 +139,8 @@ const DEFAULT_CONFIG = {
|
|||||||
defaultSubtasks: 5,
|
defaultSubtasks: 5,
|
||||||
defaultPriority: 'medium',
|
defaultPriority: 'medium',
|
||||||
projectName: 'Task Master',
|
projectName: 'Task Master',
|
||||||
ollamaBaseURL: 'http://localhost:11434/api'
|
ollamaBaseURL: 'http://localhost:11434/api',
|
||||||
|
bedrockBaseURL: 'https://bedrock.us-east-1.amazonaws.com'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -127,6 +127,7 @@ jest.unstable_mockModule(
|
|||||||
getProjectName: jest.fn(() => 'Test Project'),
|
getProjectName: jest.fn(() => 'Test Project'),
|
||||||
getOllamaBaseURL: jest.fn(() => 'http://localhost:11434/api'),
|
getOllamaBaseURL: jest.fn(() => 'http://localhost:11434/api'),
|
||||||
getAzureBaseURL: jest.fn(() => undefined),
|
getAzureBaseURL: jest.fn(() => undefined),
|
||||||
|
getBedrockBaseURL: jest.fn(() => undefined),
|
||||||
getParametersForRole: jest.fn(() => ({
|
getParametersForRole: jest.fn(() => ({
|
||||||
maxTokens: 4000,
|
maxTokens: 4000,
|
||||||
temperature: 0.7
|
temperature: 0.7
|
||||||
|
|||||||
Reference in New Issue
Block a user