Merge remote-tracking branch 'origin/next' into telemetry

This commit is contained in:
Ralph Khreish
2025-05-16 18:16:58 +02:00
40 changed files with 387 additions and 229 deletions

View File

@@ -32,7 +32,7 @@ The script can be configured through environment variables in a `.env` file at t
- `PERPLEXITY_API_KEY`: Your Perplexity API key for research-backed subtask generation
- `PERPLEXITY_MODEL`: Specify which Perplexity model to use (default: "sonar-medium-online")
- `DEBUG`: Enable debug logging (default: false)
- `LOG_LEVEL`: Log level - debug, info, warn, error (default: info)
- `TASKMASTER_LOG_LEVEL`: Log level - debug, info, warn, error (default: info)
- `DEFAULT_SUBTASKS`: Default number of subtasks when expanding (default: 3)
- `DEFAULT_PRIORITY`: Default priority for generated tasks (default: medium)
- `PROJECT_NAME`: Override default project name in tasks.json
@@ -47,7 +47,7 @@ The script can be configured through environment variables in a `.env` file at t
- Tasks can have `subtasks` for more detailed implementation steps.
- Dependencies are displayed with status indicators (✅ for completed, ⏱️ for pending) to easily track progress.
2. **Script Commands**
2. **Script Commands**
You can run the script via:
```bash
@@ -225,7 +225,7 @@ To use the Perplexity integration:
## Logging
The script supports different logging levels controlled by the `LOG_LEVEL` environment variable:
The script supports different logging levels controlled by the `TASKMASTER_LOG_LEVEL` environment variable:
- `debug`: Detailed information, typically useful for troubleshooting
- `info`: Confirmation that things are working as expected (default)

View File

@@ -38,10 +38,10 @@ const LOG_LEVELS = {
success: 4
};
// Get log level from environment or default to info
const LOG_LEVEL = process.env.LOG_LEVEL
? LOG_LEVELS[process.env.LOG_LEVEL.toLowerCase()]
: LOG_LEVELS.info;
// Determine log level from environment variable or default to 'info'
const LOG_LEVEL = process.env.TASKMASTER_LOG_LEVEL
? LOG_LEVELS[process.env.TASKMASTER_LOG_LEVEL.toLowerCase()]
: LOG_LEVELS.info; // Default to info
// Create a color gradient for the banner
const coolGradient = gradient(['#00b4d8', '#0077b6', '#03045e']);

View File

@@ -17,7 +17,8 @@ import {
getParametersForRole,
getUserId,
MODEL_MAP,
getDebugFlag
getDebugFlag,
getBaseUrlForRole
} from './config-manager.js';
import { log, resolveEnvVariable, isSilentMode } from './utils.js';
@@ -339,9 +340,15 @@ async function _unifiedServiceRunner(serviceType, params) {
'AI service call failed for all configured roles.';
for (const currentRole of sequence) {
let providerName, modelId, apiKey, roleParams, providerFnSet, providerApiFn;
let providerResponse;
let telemetryData = null;
let providerName,
modelId,
apiKey,
roleParams,
providerFnSet,
providerApiFn,
baseUrl,
providerResponse,
telemetryData = null;
try {
log('info', `New AI service call with role: ${currentRole}`);
@@ -382,6 +389,7 @@ async function _unifiedServiceRunner(serviceType, params) {
// Pass effectiveProjectRoot to getParametersForRole
roleParams = getParametersForRole(currentRole, effectiveProjectRoot);
baseUrl = getBaseUrlForRole(currentRole, effectiveProjectRoot);
// 2. Get Provider Function Set
providerFnSet = PROVIDER_FUNCTIONS[providerName?.toLowerCase()];
@@ -458,6 +466,7 @@ async function _unifiedServiceRunner(serviceType, params) {
maxTokens: roleParams.maxTokens,
temperature: roleParams.temperature,
messages,
baseUrl,
...(serviceType === 'generateObject' && { schema, objectName }),
...restApiParams
};

View File

@@ -74,7 +74,11 @@ import {
getApiKeyStatusReport
} from './task-manager/models.js';
import { findProjectRoot } from './utils.js';
import {
isValidTaskStatus,
TASK_STATUS_OPTIONS
} from '../../src/constants/task-status.js';
import { getTaskMasterVersion } from '../../src/utils/getVersion.js';
/**
* Runs the interactive setup process for model configuration.
* @param {string|null} projectRoot - The resolved project root directory.
@@ -487,11 +491,6 @@ function registerCommands(programInstance) {
process.exit(1);
});
// Default help
programInstance.on('--help', function () {
displayHelp();
});
// parse-prd command
programInstance
.command('parse-prd')
@@ -1039,7 +1038,7 @@ function registerCommands(programInstance) {
)
.option(
'-s, --status <status>',
'New status (todo, in-progress, review, done)'
`New status (one of: ${TASK_STATUS_OPTIONS.join(', ')})`
)
.option('-f, --file <file>', 'Path to the tasks file', 'tasks/tasks.json')
.action(async (options) => {
@@ -1052,6 +1051,16 @@ function registerCommands(programInstance) {
process.exit(1);
}
if (!isValidTaskStatus(status)) {
console.error(
chalk.red(
`Error: Invalid status value: ${status}. Use one of: ${TASK_STATUS_OPTIONS.join(', ')}`
)
);
process.exit(1);
}
console.log(
chalk.blue(`Setting status of task(s) ${taskId} to: ${status}`)
);
@@ -1273,10 +1282,6 @@ function registerCommands(programInstance) {
'--details <details>',
'Implementation details (for manual task creation)'
)
.option(
'--test-strategy <testStrategy>',
'Test strategy (for manual task creation)'
)
.option(
'--dependencies <dependencies>',
'Comma-separated list of task IDs this task depends on'
@@ -1654,6 +1659,7 @@ function registerCommands(programInstance) {
}
} catch (error) {
console.error(chalk.red(`Error: ${error.message}`));
showAddSubtaskHelp();
process.exit(1);
}
})
@@ -2357,14 +2363,7 @@ function setupCLI() {
return 'unknown'; // Default fallback if package.json fails
})
.helpOption('-h, --help', 'Display help')
.addHelpCommand(false) // Disable default help command
.on('--help', () => {
displayHelp(); // Use your custom help display instead
})
.on('-h', () => {
displayHelp();
process.exit(0);
});
.addHelpCommand(false); // Disable default help command
// Modify the help option to use your custom display
programInstance.helpInformation = () => {
@@ -2384,28 +2383,7 @@ function setupCLI() {
*/
async function checkForUpdate() {
// Get current version from package.json ONLY
let currentVersion = 'unknown'; // Initialize with a default
try {
// Try to get the version from the installed package (if applicable) or current dir
let packageJsonPath = path.join(
process.cwd(),
'node_modules',
'task-master-ai',
'package.json'
);
// Fallback to current directory package.json if not found in node_modules
if (!fs.existsSync(packageJsonPath)) {
packageJsonPath = path.join(process.cwd(), 'package.json');
}
if (fs.existsSync(packageJsonPath)) {
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
currentVersion = packageJson.version;
}
} catch (error) {
// Silently fail and use default
log('debug', `Error reading current package version: ${error.message}`);
}
const currentVersion = getTaskMasterVersion();
return new Promise((resolve) => {
// Get the latest version from npm registry

View File

@@ -687,6 +687,13 @@ function getAllProviders() {
return Object.keys(MODEL_MAP || {});
}
function getBaseUrlForRole(role, explicitRoot = null) {
const roleConfig = getModelConfigForRole(role, explicitRoot);
return roleConfig && typeof roleConfig.baseUrl === 'string'
? roleConfig.baseUrl
: undefined;
}
export {
// Core config access
getConfig,
@@ -714,6 +721,7 @@ export {
getFallbackModelId,
getFallbackMaxTokens,
getFallbackTemperature,
getBaseUrlForRole,
// Global setting getters (No env var overrides)
getLogLevel,

View File

@@ -8,6 +8,10 @@ import { validateTaskDependencies } from '../dependency-manager.js';
import { getDebugFlag } from '../config-manager.js';
import updateSingleTaskStatus from './update-single-task-status.js';
import generateTaskFiles from './generate-task-files.js';
import {
isValidTaskStatus,
TASK_STATUS_OPTIONS
} from '../../../src/constants/task-status.js';
/**
* Set the status of a task
@@ -19,6 +23,11 @@ import generateTaskFiles from './generate-task-files.js';
*/
async function setTaskStatus(tasksPath, taskIdInput, newStatus, options = {}) {
try {
if (!isValidTaskStatus(newStatus)) {
throw new Error(
`Error: Invalid status value: ${newStatus}. Use one of: ${TASK_STATUS_OPTIONS.join(', ')}`
);
}
// Determine if we're in MCP mode by checking for mcpLog
const isMcpMode = !!options?.mcpLog;

View File

@@ -1,6 +1,7 @@
import chalk from 'chalk';
import { log } from '../utils.js';
import { isValidTaskStatus } from '../../../src/constants/task-status.js';
/**
* Update the status of a single task
@@ -17,6 +18,12 @@ async function updateSingleTaskStatus(
data,
showUi = true
) {
if (!isValidTaskStatus(newStatus)) {
throw new Error(
`Error: Invalid status value: ${newStatus}. Use one of: ${TASK_STATUS_OPTIONS.join(', ')}`
);
}
// Check if it's a subtask (e.g., "1.2")
if (taskIdInput.includes('.')) {
const [parentId, subtaskId] = taskIdInput

View File

@@ -16,10 +16,11 @@ import {
truncate,
isSilentMode
} from './utils.js';
import path from 'path';
import fs from 'fs';
import { findNextTask, analyzeTaskComplexity } from './task-manager.js';
import { getProjectName, getDefaultSubtasks } from './config-manager.js';
import { TASK_STATUS_OPTIONS } from '../../src/constants/task-status.js';
import { getTaskMasterVersion } from '../../src/utils/getVersion.js';
// Create a color gradient for the banner
const coolGradient = gradient(['#00b4d8', '#0077b6', '#03045e']);
@@ -46,17 +47,7 @@ function displayBanner() {
);
// Read version directly from package.json
let version = 'unknown'; // Initialize with a default
try {
const packageJsonPath = path.join(process.cwd(), 'package.json');
if (fs.existsSync(packageJsonPath)) {
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
version = packageJson.version;
}
} catch (error) {
// Silently fall back to default version
log('warn', 'Could not read package.json for version info.');
}
const version = getTaskMasterVersion();
console.log(
boxen(
@@ -458,7 +449,7 @@ function displayHelp() {
{
name: 'set-status',
args: '--id=<id> --status=<status>',
desc: 'Update task status (done, pending, etc.)'
desc: `Update task status (${TASK_STATUS_OPTIONS.join(', ')})`
},
{
name: 'update',
@@ -809,12 +800,7 @@ async function displayNextTask(tasksPath) {
'padding-bottom': 0,
compact: true
},
chars: {
mid: '',
'left-mid': '',
'mid-mid': '',
'right-mid': ''
},
chars: { mid: '', 'left-mid': '', 'mid-mid': '', 'right-mid': '' },
colWidths: [15, Math.min(75, process.stdout.columns - 20 || 60)],
wordWrap: true
});
@@ -905,12 +891,7 @@ async function displayNextTask(tasksPath) {
'padding-bottom': 0,
compact: true
},
chars: {
mid: '',
'left-mid': '',
'mid-mid': '',
'right-mid': ''
},
chars: { mid: '', 'left-mid': '', 'mid-mid': '', 'right-mid': '' },
wordWrap: true
});