2025-04-21 17:48:30 -04:00
import fs from 'fs' ;
import path from 'path' ;
import chalk from 'chalk' ;
import boxen from 'boxen' ;
import Table from 'cli-table3' ;
2025-05-02 17:48:59 -04:00
import { z } from 'zod' ;
2025-04-21 17:48:30 -04:00
import {
getStatusWithColor ,
startLoadingIndicator ,
stopLoadingIndicator
} from '../ui.js' ;
refactor: Standardize configuration and environment variable access
This commit centralizes configuration and environment variable access across various modules by consistently utilizing getters from scripts/modules/config-manager.js. This replaces direct access to process.env and the global CONFIG object, leading to improved consistency, maintainability, testability, and better handling of session-specific configurations within the MCP context.
Key changes include:
- Centralized Getters: Replaced numerous instances of process.env.* and CONFIG.* with corresponding getter functions (e.g., getLogLevel, getMainModelId, getResearchMaxTokens, getMainTemperature, isApiKeySet, getDebugFlag, getDefaultSubtasks).
- Session Awareness: Ensured that the session object is passed to config getters where necessary, particularly within AI service calls (ai-services.js, add-task.js) and error handling (ai-services.js), allowing for session-specific environment overrides.
- API Key Checks: Standardized API key availability checks using isApiKeySet() instead of directly checking process.env.* (e.g., for Perplexity in commands.js and ai-services.js).
- Client Instantiation Cleanup: Removed now-redundant/obsolete local client instantiation functions (getAnthropicClient, getPerplexityClient) from ai-services.js and the global Anthropic client initialization from dependency-manager.js. Client creation should now rely on the config manager and factory patterns.
- Consistent Debug Flag Usage: Standardized calls to getDebugFlag() in commands.js, removing potentially unnecessary null arguments.
- Accurate Progress Calculation: Updated AI stream progress reporting (ai-services.js, add-task.js) to use getMainMaxTokens(session) for more accurate calculations.
- Minor Cleanup: Removed unused import from scripts/modules/commands.js.
Specific module updates:
- :
- Uses getLogLevel() instead of process.env.LOG_LEVEL.
- :
- Replaced direct env/config access for model IDs, tokens, temperature, API keys, and default subtasks with appropriate getters.
- Passed session to handleClaudeError.
- Removed local getPerplexityClient and getAnthropicClient functions.
- Updated progress calculations to use getMainMaxTokens(session).
- :
- Uses isApiKeySet('perplexity') for API key checks.
- Uses getDebugFlag() consistently for debug checks.
- Removed unused import.
- :
- Removed global Anthropic client initialization.
- :
- Uses config getters (getResearch..., getMain...) for Perplexity and Claude API call parameters, preserving customEnv override logic.
This refactoring also resolves a potential SyntaxError: Identifier 'getPerplexityClient' has already been declared by removing the duplicated/obsolete function definition previously present in ai-services.js.
2025-04-21 21:30:12 -04:00
import {
2025-04-25 13:24:15 -04:00
log as consoleLog ,
readJSON ,
writeJSON ,
truncate ,
isSilentMode
} from '../utils.js' ;
2025-05-02 17:48:59 -04:00
import {
generateObjectService ,
generateTextService
} from '../ai-services-unified.js' ;
chore: Remove unused imports across modules
Removes unused import statements identified after the major refactoring of the AI service layer and other components. This cleanup improves code clarity and removes unnecessary dependencies.
Unused imports removed from:
- **`mcp-server/src/core/direct-functions/analyze-task-complexity.js`:**
- Removed `path`
- **`mcp-server/src/core/direct-functions/complexity-report.js`:**
- Removed `path`
- **`mcp-server/src/core/direct-functions/expand-all-tasks.js`:**
- Removed `path`, `fs`
- **`mcp-server/src/core/direct-functions/generate-task-files.js`:**
- Removed `path`
- **`mcp-server/src/core/direct-functions/parse-prd.js`:**
- Removed `os`, `findTasksJsonPath`
- **`mcp-server/src/core/direct-functions/update-tasks.js`:**
- Removed `isSilentMode`
- **`mcp-server/src/tools/add-task.js`:**
- Removed `createContentResponse`, `executeTaskMasterCommand`
- **`mcp-server/src/tools/analyze.js`:**
- Removed `getProjectRootFromSession` (as `projectRoot` is now required in args)
- **`mcp-server/src/tools/expand-task.js`:**
- Removed `path`
- **`mcp-server/src/tools/initialize-project.js`:**
- Removed `createContentResponse`
- **`mcp-server/src/tools/parse-prd.js`:**
- Removed `findPRDDocumentPath`, `resolveTasksOutputPath` (logic moved or handled by `resolveProjectPaths`)
- **`mcp-server/src/tools/update.js`:**
- Removed `getProjectRootFromSession` (as `projectRoot` is now required in args)
- **`scripts/modules/commands.js`:**
- Removed `exec`, `readline`
- Removed AI config getters (`getMainModelId`, etc.)
- Removed MCP helpers (`getMcpApiKeyStatus`)
- **`scripts/modules/config-manager.js`:**
- Removed `ZodError`, `readJSON`, `writeJSON`
- **`scripts/modules/task-manager/analyze-task-complexity.js`:**
- Removed AI config getters (`getMainModelId`, etc.)
- **`scripts/modules/task-manager/expand-all-tasks.js`:**
- Removed `fs`, `path`, `writeJSON`
- **`scripts/modules/task-manager/models.js`:**
- Removed `VALID_PROVIDERS`
- **`scripts/modules/task-manager/update-subtask-by-id.js`:**
- Removed AI config getters (`getMainModelId`, etc.)
- **`scripts/modules/task-manager/update-tasks.js`:**
- Removed AI config getters (`getMainModelId`, etc.)
- **`scripts/modules/ui.js`:**
- Removed `getDebugFlag`
- **`scripts/modules/utils.js`:**
- Removed `ZodError`
2025-04-25 15:11:55 -04:00
import { getDebugFlag } from '../config-manager.js' ;
2025-04-21 17:48:30 -04:00
import generateTaskFiles from './generate-task-files.js' ;
/ * *
2025-04-25 13:24:15 -04:00
* Update a subtask by appending additional timestamped information using the unified AI service .
2025-04-21 17:48:30 -04:00
* @ param { string } tasksPath - Path to the tasks . json file
* @ param { string } subtaskId - ID of the subtask to update in format "parentId.subtaskId"
* @ param { string } prompt - Prompt for generating additional information
2025-04-25 13:24:15 -04:00
* @ param { boolean } [ useResearch = false ] - Whether to use the research AI role .
* @ param { Object } context - Context object containing session and mcpLog .
* @ param { Object } [ context . session ] - Session object from MCP server .
* @ param { Object } [ context . mcpLog ] - MCP logger object .
2025-05-01 17:59:54 -04:00
* @ param { string } [ context . projectRoot ] - Project root path ( needed for AI service key resolution ) .
2025-04-25 13:24:15 -04:00
* @ param { string } [ outputFormat = 'text' ] - Output format ( 'text' or 'json' ) . Automatically 'json' if mcpLog is present .
* @ returns { Promise < Object | null > } - The updated subtask or null if update failed .
2025-04-21 17:48:30 -04:00
* /
async function updateSubtaskById (
tasksPath ,
subtaskId ,
prompt ,
useResearch = false ,
2025-04-25 13:24:15 -04:00
context = { } ,
outputFormat = context . mcpLog ? 'json' : 'text'
2025-04-21 17:48:30 -04:00
) {
2025-05-01 17:59:54 -04:00
const { session , mcpLog , projectRoot } = context ;
2025-04-25 13:24:15 -04:00
const logFn = mcpLog || consoleLog ;
const isMCP = ! ! mcpLog ;
// Report helper
const report = ( level , ... args ) => {
if ( isMCP ) {
if ( typeof logFn [ level ] === 'function' ) logFn [ level ] ( ... args ) ;
else logFn . info ( ... args ) ;
} else if ( ! isSilentMode ( ) ) {
logFn ( level , ... args ) ;
2025-04-21 17:48:30 -04:00
}
} ;
let loadingIndicator = null ;
2025-04-22 02:42:04 -04:00
2025-04-21 17:48:30 -04:00
try {
2025-04-25 13:24:15 -04:00
report ( 'info' , ` Updating subtask ${ subtaskId } with prompt: " ${ prompt } " ` ) ;
2025-04-21 17:48:30 -04:00
// Validate subtask ID format
if (
! subtaskId ||
typeof subtaskId !== 'string' ||
! subtaskId . includes ( '.' )
) {
throw new Error (
` Invalid subtask ID format: ${ subtaskId } . Subtask ID must be in format "parentId.subtaskId" `
) ;
}
// Validate prompt
if ( ! prompt || typeof prompt !== 'string' || prompt . trim ( ) === '' ) {
throw new Error (
'Prompt cannot be empty. Please provide context for the subtask update.'
) ;
}
// Validate tasks file exists
if ( ! fs . existsSync ( tasksPath ) ) {
throw new Error ( ` Tasks file not found at path: ${ tasksPath } ` ) ;
}
// Read the tasks file
const data = readJSON ( tasksPath ) ;
if ( ! data || ! data . tasks ) {
throw new Error (
` No valid tasks found in ${ tasksPath } . The file may be corrupted or have an invalid format. `
) ;
}
// Parse parent and subtask IDs
const [ parentIdStr , subtaskIdStr ] = subtaskId . split ( '.' ) ;
const parentId = parseInt ( parentIdStr , 10 ) ;
const subtaskIdNum = parseInt ( subtaskIdStr , 10 ) ;
if (
isNaN ( parentId ) ||
parentId <= 0 ||
isNaN ( subtaskIdNum ) ||
subtaskIdNum <= 0
) {
throw new Error (
` Invalid subtask ID format: ${ subtaskId } . Both parent ID and subtask ID must be positive integers. `
) ;
}
// Find the parent task
const parentTask = data . tasks . find ( ( task ) => task . id === parentId ) ;
if ( ! parentTask ) {
throw new Error (
` Parent task with ID ${ parentId } not found. Please verify the task ID and try again. `
) ;
}
// Find the subtask
if ( ! parentTask . subtasks || ! Array . isArray ( parentTask . subtasks ) ) {
throw new Error ( ` Parent task ${ parentId } has no subtasks. ` ) ;
}
2025-04-25 13:24:15 -04:00
const subtaskIndex = parentTask . subtasks . findIndex (
( st ) => st . id === subtaskIdNum
) ;
if ( subtaskIndex === - 1 ) {
2025-04-21 17:48:30 -04:00
throw new Error (
` Subtask with ID ${ subtaskId } not found. Please verify the subtask ID and try again. `
) ;
}
2025-04-25 13:24:15 -04:00
const subtask = parentTask . subtasks [ subtaskIndex ] ;
2025-05-02 17:48:59 -04:00
const subtaskSchema = z . object ( {
id : z . number ( ) . int ( ) . positive ( ) ,
title : z . string ( ) ,
description : z . string ( ) . optional ( ) ,
status : z . string ( ) ,
dependencies : z . array ( z . union ( [ z . string ( ) , z . number ( ) ] ) ) . optional ( ) ,
priority : z . string ( ) . optional ( ) ,
details : z . string ( ) . optional ( ) ,
testStrategy : z . string ( ) . optional ( )
} ) ;
2025-04-21 17:48:30 -04:00
// Only show UI elements for text output (CLI)
if ( outputFormat === 'text' ) {
// Show the subtask that will be updated
const table = new Table ( {
head : [
chalk . cyan . bold ( 'ID' ) ,
chalk . cyan . bold ( 'Title' ) ,
chalk . cyan . bold ( 'Status' )
] ,
colWidths : [ 10 , 55 , 10 ]
} ) ;
table . push ( [
subtaskId ,
truncate ( subtask . title , 52 ) ,
getStatusWithColor ( subtask . status )
] ) ;
console . log (
boxen ( chalk . white . bold ( ` Updating Subtask # ${ subtaskId } ` ) , {
padding : 1 ,
borderColor : 'blue' ,
borderStyle : 'round' ,
margin : { top : 1 , bottom : 0 }
} )
) ;
console . log ( table . toString ( ) ) ;
// Start the loading indicator - only for text output
loadingIndicator = startLoadingIndicator (
2025-05-01 17:59:54 -04:00
useResearch
? 'Updating subtask with research...'
: 'Updating subtask...'
2025-04-21 17:48:30 -04:00
) ;
}
2025-05-02 17:48:59 -04:00
let parsedAIResponse ;
2025-04-22 02:42:04 -04:00
try {
2025-05-02 17:48:59 -04:00
// --- GET PARENT & SIBLING CONTEXT ---
const parentContext = {
id : parentTask . id ,
title : parentTask . title
// Avoid sending full parent description/details unless necessary
} ;
const prevSubtask =
subtaskIndex > 0
? {
id : ` ${ parentTask . id } . ${ parentTask . subtasks [ subtaskIndex - 1 ] . id } ` ,
title : parentTask . subtasks [ subtaskIndex - 1 ] . title ,
status : parentTask . subtasks [ subtaskIndex - 1 ] . status
}
: null ;
const nextSubtask =
subtaskIndex < parentTask . subtasks . length - 1
? {
id : ` ${ parentTask . id } . ${ parentTask . subtasks [ subtaskIndex + 1 ] . id } ` ,
title : parentTask . subtasks [ subtaskIndex + 1 ] . title ,
status : parentTask . subtasks [ subtaskIndex + 1 ] . status
}
: null ;
const contextString = `
Parent Task : $ { JSON . stringify ( parentContext ) }
$ { prevSubtask ? ` Previous Subtask: ${ JSON . stringify ( prevSubtask ) } ` : '' }
$ { nextSubtask ? ` Next Subtask: ${ JSON . stringify ( nextSubtask ) } ` : '' }
` ;
const systemPrompt = ` You are an AI assistant updating a parent task's subtask. This subtask will be part of a larger parent task and will be used to direct AI agents to complete the subtask. Your goal is to GENERATE new, relevant information based on the user's request (which may be high-level, mid-level or low-level) and APPEND it to the existing subtask 'details' field, wrapped in specific XML-like tags with an ISO 8601 timestamp. Intelligently determine the level of detail to include based on the user's request. Some requests are meant simply to update the subtask with some mid-implementation details, while others are meant to update the subtask with a detailed plan or strategy.
Context Provided :
- The current subtask object .
- Basic info about the parent task ( ID , title ) .
- Basic info about the immediately preceding subtask ( ID , title , status ) , if it exists .
- Basic info about the immediately succeeding subtask ( ID , title , status ) , if it exists .
- A user request string .
2025-05-01 17:59:54 -04:00
Guidelines :
2025-05-02 17:48:59 -04:00
1. Analyze the user request considering the provided subtask details AND the context of the parent and sibling tasks .
2. GENERATE new , relevant text content that should be added to the 'details' field . Focus * only * on the substance of the update based on the user request and context . Do NOT add timestamps or any special formatting yourself . Avoid over - engineering the details , provide .
3. Update the 'details' field in the subtask object with the GENERATED text content . It ' s okay if this overwrites previous details in the object you return , as the calling code will handle the final appending .
4. Return the * entire * updated subtask object ( with your generated content in the 'details' field ) as a valid JSON object conforming to the provided schema . Do NOT return explanations or markdown formatting . ` ;
2025-05-01 17:59:54 -04:00
const subtaskDataString = JSON . stringify ( subtask , null , 2 ) ;
2025-05-02 17:48:59 -04:00
// Updated user prompt including context
const userPrompt = ` Task Context: \n ${ contextString } \n Current Subtask: \n ${ subtaskDataString } \n \n User Request: " ${ prompt } " \n \n Please GENERATE new, relevant text content for the 'details' field based on the user request and the provided context. Return the entire updated subtask object as a valid JSON object matching the schema, with the newly generated text placed in the 'details' field. ` ;
// --- END UPDATED PROMPTS ---
2025-05-01 17:59:54 -04:00
2025-05-02 17:48:59 -04:00
// Call Unified AI Service using generateObjectService
2025-05-01 17:59:54 -04:00
const role = useResearch ? 'research' : 'main' ;
2025-05-02 17:48:59 -04:00
report ( 'info' , ` Using AI object service with role: ${ role } ` ) ;
2025-05-01 17:59:54 -04:00
2025-05-02 17:48:59 -04:00
parsedAIResponse = await generateObjectService ( {
2025-05-01 17:59:54 -04:00
prompt : userPrompt ,
2025-04-25 13:24:15 -04:00
systemPrompt : systemPrompt ,
2025-05-02 17:48:59 -04:00
schema : subtaskSchema ,
objectName : 'updatedSubtask' ,
2025-05-01 17:59:54 -04:00
role ,
session ,
2025-05-02 17:48:59 -04:00
projectRoot ,
maxRetries : 2
2025-04-22 02:42:04 -04:00
} ) ;
2025-05-02 17:48:59 -04:00
report (
'success' ,
'Successfully received object response from AI service'
) ;
2025-04-21 17:48:30 -04:00
2025-04-22 02:42:04 -04:00
if ( outputFormat === 'text' && loadingIndicator ) {
stopLoadingIndicator ( loadingIndicator ) ;
loadingIndicator = null ;
}
2025-04-21 17:48:30 -04:00
2025-05-02 17:48:59 -04:00
if ( ! parsedAIResponse || typeof parsedAIResponse !== 'object' ) {
throw new Error ( 'AI did not return a valid object.' ) ;
2025-04-21 17:48:30 -04:00
}
2025-05-02 17:48:59 -04:00
2025-04-22 02:42:04 -04:00
report (
2025-04-25 13:24:15 -04:00
'success' ,
2025-05-02 17:48:59 -04:00
` Successfully generated object using AI role: ${ role } . `
2025-04-21 17:48:30 -04:00
) ;
2025-04-22 02:42:04 -04:00
} catch ( aiError ) {
2025-04-25 13:24:15 -04:00
report ( 'error' , ` AI service call failed: ${ aiError . message } ` ) ;
2025-05-02 17:48:59 -04:00
if ( outputFormat === 'text' && loadingIndicator ) {
stopLoadingIndicator ( loadingIndicator ) ; // Ensure stop on error
loadingIndicator = null ;
}
2025-04-22 02:42:04 -04:00
throw aiError ;
2025-05-02 17:48:59 -04:00
}
2025-04-21 17:48:30 -04:00
2025-05-02 17:48:59 -04:00
// --- TIMESTAMP & FORMATTING LOGIC (Handled Locally) ---
// Extract only the generated content from the AI's response details field.
const generatedContent = parsedAIResponse . details || '' ; // Default to empty string
2025-04-21 17:48:30 -04:00
2025-05-02 17:48:59 -04:00
if ( generatedContent . trim ( ) ) {
// Generate timestamp locally
const timestamp = new Date ( ) . toISOString ( ) ; // <<< Local Timestamp
2025-04-21 17:48:30 -04:00
2025-05-02 17:48:59 -04:00
// Format the content with XML-like tags and timestamp LOCALLY
const formattedBlock = ` <info added on ${ timestamp } > \n ${ generatedContent . trim ( ) } \n </info added on ${ timestamp } > ` ; // <<< Local Formatting
// Append the formatted block to the *original* subtask details
subtask . details =
( subtask . details ? subtask . details + '\n' : '' ) + formattedBlock ; // <<< Local Appending
report (
'info' ,
'Appended timestamped, formatted block with AI-generated content to subtask.details.'
) ;
} else {
report (
'warn' ,
'AI response object did not contain generated content in the "details" field. Original details remain unchanged.'
2025-04-21 17:48:30 -04:00
) ;
}
2025-05-02 17:48:59 -04:00
// --- END TIMESTAMP & FORMATTING LOGIC ---
2025-04-21 17:48:30 -04:00
2025-05-02 17:48:59 -04:00
// Get a reference to the subtask *after* its details have been updated
const updatedSubtask = parentTask . subtasks [ subtaskIndex ] ; // subtask === updatedSubtask now
2025-04-21 17:48:30 -04:00
2025-05-02 17:48:59 -04:00
report ( 'info' , 'Updated subtask details locally after AI generation.' ) ;
// --- END UPDATE SUBTASK ---
2025-04-21 17:48:30 -04:00
// Only show debug info for text output (CLI)
2025-04-25 13:24:15 -04:00
if ( outputFormat === 'text' && getDebugFlag ( session ) ) {
2025-05-02 17:48:59 -04:00
console . log (
'>>> DEBUG: Subtask details AFTER AI update:' ,
updatedSubtask . details // Use updatedSubtask
) ;
2025-04-21 17:48:30 -04:00
}
2025-05-02 17:48:59 -04:00
// Description update logic (keeping as is for now)
if ( updatedSubtask . description ) {
// Use updatedSubtask
if ( prompt . length < 100 ) {
2025-04-25 13:24:15 -04:00
if ( outputFormat === 'text' && getDebugFlag ( session ) ) {
2025-04-21 17:48:30 -04:00
console . log (
'>>> DEBUG: Subtask description BEFORE append:' ,
2025-05-02 17:48:59 -04:00
updatedSubtask . description // Use updatedSubtask
2025-04-21 17:48:30 -04:00
) ;
}
2025-05-02 17:48:59 -04:00
updatedSubtask . description += ` [Updated: ${ new Date ( ) . toLocaleDateString ( ) } ] ` ; // Use updatedSubtask
2025-04-25 13:24:15 -04:00
if ( outputFormat === 'text' && getDebugFlag ( session ) ) {
2025-04-21 17:48:30 -04:00
console . log (
'>>> DEBUG: Subtask description AFTER append:' ,
2025-05-02 17:48:59 -04:00
updatedSubtask . description // Use updatedSubtask
2025-04-21 17:48:30 -04:00
) ;
}
}
}
// Only show debug info for text output (CLI)
2025-04-25 13:24:15 -04:00
if ( outputFormat === 'text' && getDebugFlag ( session ) ) {
2025-04-21 17:48:30 -04:00
console . log ( '>>> DEBUG: About to call writeJSON with updated data...' ) ;
}
2025-05-02 17:48:59 -04:00
// Write the updated tasks to the file (parentTask already contains the updated subtask)
2025-04-21 17:48:30 -04:00
writeJSON ( tasksPath , data ) ;
// Only show debug info for text output (CLI)
2025-04-25 13:24:15 -04:00
if ( outputFormat === 'text' && getDebugFlag ( session ) ) {
2025-04-21 17:48:30 -04:00
console . log ( '>>> DEBUG: writeJSON call completed.' ) ;
}
2025-04-25 13:24:15 -04:00
report ( 'success' , ` Successfully updated subtask ${ subtaskId } ` ) ;
2025-04-21 17:48:30 -04:00
// Generate individual task files
await generateTaskFiles ( tasksPath , path . dirname ( tasksPath ) ) ;
// Stop indicator before final console output - only for text output (CLI)
if ( outputFormat === 'text' ) {
if ( loadingIndicator ) {
stopLoadingIndicator ( loadingIndicator ) ;
loadingIndicator = null ;
}
console . log (
boxen (
chalk . green ( ` Successfully updated subtask # ${ subtaskId } ` ) +
'\n\n' +
chalk . white . bold ( 'Title:' ) +
' ' +
2025-05-02 17:48:59 -04:00
updatedSubtask . title +
2025-04-21 17:48:30 -04:00
'\n\n' +
2025-05-02 17:48:59 -04:00
// Update the display to show the new details field
chalk . white . bold ( 'Updated Details:' ) +
2025-04-21 17:48:30 -04:00
'\n' +
2025-05-02 17:48:59 -04:00
chalk . white ( truncate ( updatedSubtask . details || '' , 500 , true ) ) , // Use updatedSubtask
2025-04-21 17:48:30 -04:00
{ padding : 1 , borderColor : 'green' , borderStyle : 'round' }
)
) ;
}
2025-05-02 17:48:59 -04:00
return updatedSubtask ; // Return the modified subtask object
2025-04-21 17:48:30 -04:00
} catch ( error ) {
// Outer catch block handles final errors after loop/attempts
// Stop indicator on error - only for text output (CLI)
if ( outputFormat === 'text' && loadingIndicator ) {
stopLoadingIndicator ( loadingIndicator ) ;
loadingIndicator = null ;
}
2025-04-25 13:24:15 -04:00
report ( 'error' , ` Error updating subtask: ${ error . message } ` ) ;
2025-04-21 17:48:30 -04:00
// Only show error UI for text output (CLI)
if ( outputFormat === 'text' ) {
console . error ( chalk . red ( ` Error: ${ error . message } ` ) ) ;
// Provide helpful error messages based on error type
if ( error . message ? . includes ( 'ANTHROPIC_API_KEY' ) ) {
console . log (
chalk . yellow ( '\nTo fix this issue, set your Anthropic API key:' )
) ;
console . log ( ' export ANTHROPIC_API_KEY=your_api_key_here' ) ;
} else if ( error . message ? . includes ( 'PERPLEXITY_API_KEY' ) ) {
console . log ( chalk . yellow ( '\nTo fix this issue:' ) ) ;
console . log (
' 1. Set your Perplexity API key: export PERPLEXITY_API_KEY=your_api_key_here'
) ;
console . log (
refactor: Standardize configuration and environment variable access
This commit centralizes configuration and environment variable access across various modules by consistently utilizing getters from scripts/modules/config-manager.js. This replaces direct access to process.env and the global CONFIG object, leading to improved consistency, maintainability, testability, and better handling of session-specific configurations within the MCP context.
Key changes include:
- Centralized Getters: Replaced numerous instances of process.env.* and CONFIG.* with corresponding getter functions (e.g., getLogLevel, getMainModelId, getResearchMaxTokens, getMainTemperature, isApiKeySet, getDebugFlag, getDefaultSubtasks).
- Session Awareness: Ensured that the session object is passed to config getters where necessary, particularly within AI service calls (ai-services.js, add-task.js) and error handling (ai-services.js), allowing for session-specific environment overrides.
- API Key Checks: Standardized API key availability checks using isApiKeySet() instead of directly checking process.env.* (e.g., for Perplexity in commands.js and ai-services.js).
- Client Instantiation Cleanup: Removed now-redundant/obsolete local client instantiation functions (getAnthropicClient, getPerplexityClient) from ai-services.js and the global Anthropic client initialization from dependency-manager.js. Client creation should now rely on the config manager and factory patterns.
- Consistent Debug Flag Usage: Standardized calls to getDebugFlag() in commands.js, removing potentially unnecessary null arguments.
- Accurate Progress Calculation: Updated AI stream progress reporting (ai-services.js, add-task.js) to use getMainMaxTokens(session) for more accurate calculations.
- Minor Cleanup: Removed unused import from scripts/modules/commands.js.
Specific module updates:
- :
- Uses getLogLevel() instead of process.env.LOG_LEVEL.
- :
- Replaced direct env/config access for model IDs, tokens, temperature, API keys, and default subtasks with appropriate getters.
- Passed session to handleClaudeError.
- Removed local getPerplexityClient and getAnthropicClient functions.
- Updated progress calculations to use getMainMaxTokens(session).
- :
- Uses isApiKeySet('perplexity') for API key checks.
- Uses getDebugFlag() consistently for debug checks.
- Removed unused import.
- :
- Removed global Anthropic client initialization.
- :
- Uses config getters (getResearch..., getMain...) for Perplexity and Claude API call parameters, preserving customEnv override logic.
This refactoring also resolves a potential SyntaxError: Identifier 'getPerplexityClient' has already been declared by removing the duplicated/obsolete function definition previously present in ai-services.js.
2025-04-21 21:30:12 -04:00
' 2. Or run without the research flag: task-master update-subtask --id=<id> --prompt="..."'
2025-04-21 17:48:30 -04:00
) ;
} else if ( error . message ? . includes ( 'overloaded' ) ) {
// Catch final overload error
console . log (
chalk . yellow (
'\nAI model overloaded, and fallback failed or was unavailable:'
)
) ;
console . log ( ' 1. Try again in a few minutes.' ) ;
console . log ( ' 2. Ensure PERPLEXITY_API_KEY is set for fallback.' ) ;
console . log ( ' 3. Consider breaking your prompt into smaller updates.' ) ;
} else if ( error . message ? . includes ( 'not found' ) ) {
console . log ( chalk . yellow ( '\nTo fix this issue:' ) ) ;
console . log (
' 1. Run task-master list --with-subtasks to see all available subtask IDs'
) ;
console . log (
2025-04-22 02:42:04 -04:00
' 2. Use a valid subtask ID with the --id parameter in format "parentId.subtaskId"'
2025-04-21 17:48:30 -04:00
) ;
2025-04-22 02:42:04 -04:00
} else if ( error . message ? . includes ( 'empty stream response' ) ) {
2025-04-21 17:48:30 -04:00
console . log (
chalk . yellow (
'\nThe AI model returned an empty response. This might be due to the prompt or API issues. Try rephrasing or trying again later.'
)
) ;
}
refactor: Standardize configuration and environment variable access
This commit centralizes configuration and environment variable access across various modules by consistently utilizing getters from scripts/modules/config-manager.js. This replaces direct access to process.env and the global CONFIG object, leading to improved consistency, maintainability, testability, and better handling of session-specific configurations within the MCP context.
Key changes include:
- Centralized Getters: Replaced numerous instances of process.env.* and CONFIG.* with corresponding getter functions (e.g., getLogLevel, getMainModelId, getResearchMaxTokens, getMainTemperature, isApiKeySet, getDebugFlag, getDefaultSubtasks).
- Session Awareness: Ensured that the session object is passed to config getters where necessary, particularly within AI service calls (ai-services.js, add-task.js) and error handling (ai-services.js), allowing for session-specific environment overrides.
- API Key Checks: Standardized API key availability checks using isApiKeySet() instead of directly checking process.env.* (e.g., for Perplexity in commands.js and ai-services.js).
- Client Instantiation Cleanup: Removed now-redundant/obsolete local client instantiation functions (getAnthropicClient, getPerplexityClient) from ai-services.js and the global Anthropic client initialization from dependency-manager.js. Client creation should now rely on the config manager and factory patterns.
- Consistent Debug Flag Usage: Standardized calls to getDebugFlag() in commands.js, removing potentially unnecessary null arguments.
- Accurate Progress Calculation: Updated AI stream progress reporting (ai-services.js, add-task.js) to use getMainMaxTokens(session) for more accurate calculations.
- Minor Cleanup: Removed unused import from scripts/modules/commands.js.
Specific module updates:
- :
- Uses getLogLevel() instead of process.env.LOG_LEVEL.
- :
- Replaced direct env/config access for model IDs, tokens, temperature, API keys, and default subtasks with appropriate getters.
- Passed session to handleClaudeError.
- Removed local getPerplexityClient and getAnthropicClient functions.
- Updated progress calculations to use getMainMaxTokens(session).
- :
- Uses isApiKeySet('perplexity') for API key checks.
- Uses getDebugFlag() consistently for debug checks.
- Removed unused import.
- :
- Removed global Anthropic client initialization.
- :
- Uses config getters (getResearch..., getMain...) for Perplexity and Claude API call parameters, preserving customEnv override logic.
This refactoring also resolves a potential SyntaxError: Identifier 'getPerplexityClient' has already been declared by removing the duplicated/obsolete function definition previously present in ai-services.js.
2025-04-21 21:30:12 -04:00
if ( getDebugFlag ( session ) ) {
2025-04-21 17:48:30 -04:00
// Use getter
console . error ( error ) ;
}
} else {
throw error ; // Re-throw for JSON output
}
return null ;
}
}
export default updateSubtaskById ;