feat: add universal logger instead of console.log.

- fixed esbuild issues
- converted to npm instead of pnpm since root project is npm (might switch whole project to pnpm later)
This commit is contained in:
Ralph Khreish
2025-07-28 17:03:35 +03:00
parent 662a4eaecb
commit 722b6c5836
20 changed files with 8232 additions and 6766 deletions

View File

@@ -69,8 +69,8 @@ async function main() {
bundle: true,
format: 'cjs',
minify: production,
sourcemap: !production,
sourcesContent: false,
sourcemap: !production ? 'inline' : false,
sourcesContent: !production,
platform: 'node',
outdir: 'dist',
external: ['vscode'],
@@ -90,8 +90,8 @@ async function main() {
format: 'iife',
globalName: 'App',
minify: production,
sourcemap: !production,
sourcesContent: false,
sourcemap: !production ? 'inline' : false,
sourcesContent: !production,
platform: 'browser',
outdir: 'dist',
logLevel: 'silent',
@@ -99,6 +99,13 @@ async function main() {
jsx: 'automatic',
jsxImportSource: 'react',
external: ['*.css'],
// Bundle React with webview since it's not available in the runtime
// This prevents the multiple React instances issue
// Ensure React is resolved from the workspace root to avoid duplicates
alias: {
react: path.resolve(__dirname, '../../node_modules/react'),
'react-dom': path.resolve(__dirname, '../../node_modules/react-dom')
},
define: {
'process.env.NODE_ENV': production ? '"production"' : '"development"',
global: 'globalThis'

View File

@@ -9,12 +9,7 @@
"engines": {
"vscode": "^1.93.0"
},
"categories": [
"AI",
"Visualization",
"Education",
"Other"
],
"categories": ["AI", "Visualization", "Education", "Other"],
"main": "./dist/extension.js",
"contributes": {
"commands": [
@@ -48,11 +43,7 @@
"items": {
"type": "string"
},
"default": [
"-y",
"--package=task-master-ai",
"task-master-ai"
],
"default": ["-y", "--package=task-master-ai", "task-master-ai"],
"description": "An array of arguments to pass to the MCP server command."
},
"taskmaster.mcp.cwd": {
@@ -112,11 +103,7 @@
},
"taskmaster.ui.theme": {
"type": "string",
"enum": [
"auto",
"light",
"dark"
],
"enum": ["auto", "light", "dark"],
"default": "auto",
"description": "UI theme preference"
},
@@ -177,12 +164,7 @@
},
"taskmaster.debug.logLevel": {
"type": "string",
"enum": [
"error",
"warn",
"info",
"debug"
],
"enum": ["error", "warn", "info", "debug"],
"default": "info",
"description": "Logging level"
},
@@ -207,15 +189,15 @@
}
},
"scripts": {
"vscode:prepublish": false,
"build": "pnpm run build:js && pnpm run build:css",
"vscode:prepublish": "npm run build",
"build": "npm run build:js && npm run build:css",
"build:js": "node ./esbuild.js --production",
"build:css": "npx @tailwindcss/cli -o ./dist/index.css --minify",
"package": "pnpm exec node ./package.mjs",
"package": "npm exec node ./package.mjs",
"package:direct": "node ./package.mjs",
"debug:env": "node ./debug-env.mjs",
"compile": "node ./esbuild.js",
"watch": "pnpm run watch:js & pnpm run watch:css",
"watch": "npm run watch:js & npm run watch:css",
"watch:js": "node ./esbuild.js --watch",
"watch:css": "npx @tailwindcss/cli -o ./dist/index.css --watch",
"lint": "eslint src --ext ts,tsx",
@@ -254,8 +236,6 @@
"lucide-react": "^0.525.0",
"npm-run-all": "^4.1.5",
"postcss": "8.5.6",
"react": "19.1.0",
"react-dom": "19.1.0",
"tailwind-merge": "^3.3.1",
"tailwindcss": "4.1.11",
"typescript": "^5.8.3"

View File

@@ -54,26 +54,35 @@ try {
// 5. Sync versions and prepare the final package.json
console.log('Syncing versions and preparing the final package.json...');
// Read current versions
const devPackagePath = path.resolve(__dirname, 'package.json');
const publishPackagePath = path.resolve(__dirname, 'package.publish.json');
const devPackage = JSON.parse(fs.readFileSync(devPackagePath, 'utf8'));
const publishPackage = JSON.parse(fs.readFileSync(publishPackagePath, 'utf8'));
const publishPackage = JSON.parse(
fs.readFileSync(publishPackagePath, 'utf8')
);
// Check if versions are in sync
if (devPackage.version !== publishPackage.version) {
console.log(` - Version sync needed: ${publishPackage.version}${devPackage.version}`);
console.log(
` - Version sync needed: ${publishPackage.version}${devPackage.version}`
);
publishPackage.version = devPackage.version;
// Update the source package.publish.json file
fs.writeFileSync(publishPackagePath, JSON.stringify(publishPackage, null, '\t') + '\n');
console.log(` - Updated package.publish.json version to ${devPackage.version}`);
fs.writeFileSync(
publishPackagePath,
JSON.stringify(publishPackage, null, '\t') + '\n'
);
console.log(
` - Updated package.publish.json version to ${devPackage.version}`
);
} else {
console.log(` - Versions already in sync: ${devPackage.version}`);
}
// Copy the (now synced) package.publish.json as package.json
fs.copySync(publishPackagePath, path.resolve(packageDir, 'package.json'));
console.log(' - Copied package.publish.json as package.json');

View File

@@ -8,12 +8,7 @@
"engines": {
"vscode": "^1.93.0"
},
"categories": [
"AI",
"Visualization",
"Education",
"Other"
],
"categories": ["AI", "Visualization", "Education", "Other"],
"keywords": [
"kanban",
"kanban board",
@@ -87,11 +82,7 @@
"items": {
"type": "string"
},
"default": [
"-y",
"--package=task-master-ai",
"task-master-ai"
],
"default": ["-y", "--package=task-master-ai", "task-master-ai"],
"description": "An array of arguments to pass to the MCP server command."
},
"taskmaster.mcp.cwd": {
@@ -151,11 +142,7 @@
},
"taskmaster.ui.theme": {
"type": "string",
"enum": [
"auto",
"light",
"dark"
],
"enum": ["auto", "light", "dark"],
"default": "auto",
"description": "UI theme preference"
},
@@ -216,12 +203,7 @@
},
"taskmaster.debug.logLevel": {
"type": "string",
"enum": [
"error",
"warn",
"info",
"debug"
],
"enum": ["error", "warn", "info", "debug"],
"default": "info",
"description": "Logging level"
},

File diff suppressed because it is too large Load Diff

View File

@@ -261,8 +261,9 @@ export const TaskDetailsView: React.FC<TaskDetailsViewProps> = ({
onNavigateToTask
}) => {
const context = useContext(VSCodeContext);
if (!context)
{throw new Error('TaskDetailsView must be used within VSCodeContext');}
if (!context) {
throw new Error('TaskDetailsView must be used within VSCodeContext');
}
const { state, sendMessage } = context;
const { tasks } = state;
@@ -372,7 +373,9 @@ export const TaskDetailsView: React.FC<TaskDetailsViewProps> = ({
// Handle running complexity analysis for a task
const handleRunComplexityAnalysis = useCallback(async () => {
if (!currentTask) {return;}
if (!currentTask) {
return;
}
setIsLoadingComplexity(true);
try {
@@ -416,7 +419,9 @@ export const TaskDetailsView: React.FC<TaskDetailsViewProps> = ({
// Function to fetch task file data (implementation details and test strategy only)
const fetchTaskFileData = async () => {
if (!currentTask?.id) {return;}
if (!currentTask?.id) {
return;
}
setIsLoadingTaskFileData(true);
setTaskFileDataError(null);
@@ -543,7 +548,9 @@ export const TaskDetailsView: React.FC<TaskDetailsViewProps> = ({
// Handle AI Actions
const handleRegenerate = async () => {
if (!currentTask || !prompt.trim()) {return;}
if (!currentTask || !prompt.trim()) {
return;
}
setIsRegenerating(true);
try {
@@ -584,7 +591,9 @@ export const TaskDetailsView: React.FC<TaskDetailsViewProps> = ({
};
const handleAppend = async () => {
if (!currentTask || !prompt.trim()) {return;}
if (!currentTask || !prompt.trim()) {
return;
}
setIsAppending(true);
try {
@@ -626,7 +635,9 @@ export const TaskDetailsView: React.FC<TaskDetailsViewProps> = ({
// Handle adding a new subtask
const handleAddSubtask = async () => {
if (!currentTask || !newSubtaskTitle.trim() || isSubtask) {return;}
if (!currentTask || !newSubtaskTitle.trim() || isSubtask) {
return;
}
setIsSubmittingSubtask(true);
try {
@@ -672,7 +683,9 @@ export const TaskDetailsView: React.FC<TaskDetailsViewProps> = ({
// Handle status change
const handleStatusChange = async (newStatus: TaskMasterTask['status']) => {
if (!currentTask) {return;}
if (!currentTask) {
return;
}
try {
await sendMessage({

View File

@@ -25,6 +25,7 @@ import {
import { getToastDuration } from './utils/notificationPreferences';
import { parseTaskFileData } from './utils/taskFileReader';
import { TaskMasterTask } from './utils/taskMasterApi';
import { logger } from './utils/logger';
// Global MCP client manager instance
let mcpClient: MCPClientManager | null = null;
@@ -82,8 +83,8 @@ let pollingState: PollingState = {
// Initialize MCP components
async function initializeMCPComponents(context: vscode.ExtensionContext) {
try {
console.log('🔄 Initializing MCP components...');
console.log(
logger.log('🔄 Initializing MCP components...');
logger.log(
'🔍 DEBUGGING: initializeMCPComponents started at',
new Date().toISOString()
);
@@ -95,7 +96,7 @@ async function initializeMCPComponents(context: vscode.ExtensionContext) {
const mcpConfig = createMCPConfigFromSettings();
// Initialize MCP client
console.log(
logger.log(
'🔍 DEBUGGING: About to create MCPClientManager with config:',
mcpConfig
);
@@ -110,14 +111,14 @@ async function initializeMCPComponents(context: vscode.ExtensionContext) {
});
// Try to connect to MCP server
console.log('🔗 Connecting to Task Master MCP server...');
logger.log('🔗 Connecting to Task Master MCP server...');
try {
await mcpClient.connect();
// Test connection
const connectionTest = await taskMasterApi.testConnection();
if (connectionTest.success && connectionTest.data) {
console.log('✅ Task Master MCP connection established');
logger.log('✅ Task Master MCP connection established');
vscode.window.showInformationMessage(
'Task Master connected successfully!'
);
@@ -125,8 +126,8 @@ async function initializeMCPComponents(context: vscode.ExtensionContext) {
throw new Error(connectionTest.error || 'Connection test failed');
}
} catch (connectionError) {
console.error('❌ Task Master MCP connection failed:', connectionError);
console.error('Connection error details:', {
logger.error('❌ Task Master MCP connection failed:', connectionError);
logger.error('Connection error details:', {
message:
connectionError instanceof Error
? connectionError.message
@@ -165,7 +166,7 @@ async function initializeMCPComponents(context: vscode.ExtensionContext) {
// Initialize in offline mode
pollingState.isOfflineMode = true;
console.log(
logger.log(
'📴 Starting in offline mode - some features will be unavailable'
);
}
@@ -181,7 +182,7 @@ async function startPolling(): Promise<void> {
return;
}
console.log('🔄 Starting task polling with interval:', pollingState.interval);
logger.log('🔄 Starting task polling with interval:', pollingState.interval);
pollingState.isPolling = true;
pollingState.errorCount = 0;
@@ -197,7 +198,7 @@ function stopPolling(): void {
return;
}
console.log('⏹️ Stopping task polling');
logger.log('⏹️ Stopping task polling');
pollingState.isPolling = false;
if (pollingState.timer) {
@@ -212,7 +213,7 @@ async function pollForUpdates(): Promise<void> {
}
try {
console.log('📡 Polling for task updates...');
logger.log('📡 Polling for task updates...');
const tasksResult = await taskMasterApi.getTasks({
withSubtasks: true,
@@ -223,7 +224,7 @@ async function pollForUpdates(): Promise<void> {
const hasChanges = detectTaskChanges(tasksResult.data);
if (hasChanges) {
console.log('📋 Task changes detected, notifying webviews');
logger.log('📋 Task changes detected, notifying webviews');
// Track change for adaptive frequency
pollingState.changeDetectionWindow.push(Date.now());
@@ -242,7 +243,7 @@ async function pollForUpdates(): Promise<void> {
});
});
} else {
console.log('📋 No task changes detected');
logger.log('📋 No task changes detected');
pollingState.consecutiveNoChanges++;
}
@@ -260,7 +261,7 @@ async function pollForUpdates(): Promise<void> {
if (pollingState.isOfflineMode) {
pollingState.isOfflineMode = false;
notifyConnectionStatus('online', 'Connected');
console.log('✅ Reconnected successfully from offline mode');
logger.log('✅ Reconnected successfully from offline mode');
}
} else {
throw new Error(tasksResult.error || 'Failed to fetch tasks');
@@ -291,7 +292,7 @@ function detectTaskChanges(newTasks: any[]): boolean {
);
return newTasksStr !== oldTasksStr;
} catch (error) {
console.warn('⚠️ Error comparing tasks, assuming changed:', error);
logger.warn('⚠️ Error comparing tasks, assuming changed:', error);
return true;
}
}
@@ -343,11 +344,11 @@ function adjustPollingFrequency(): void {
pollingState.minInterval,
pollingState.baseInterval * 0.5
);
console.log('📈 High activity detected, increasing polling frequency');
logger.log('📈 High activity detected, increasing polling frequency');
} else if (changesPerMinute > 0.5) {
// Moderate activity: use base interval
newInterval = pollingState.baseInterval;
console.log('📊 Moderate activity, using base polling interval');
logger.log('📊 Moderate activity, using base polling interval');
} else if (pollingState.consecutiveNoChanges > 3) {
// Low activity: reduce polling frequency with exponential backoff
const backoffMultiplier = Math.min(
@@ -358,7 +359,7 @@ function adjustPollingFrequency(): void {
pollingState.maxInterval,
pollingState.baseInterval * backoffMultiplier
);
console.log(
logger.log(
`📉 Low activity detected (${pollingState.consecutiveNoChanges} no-change cycles), reducing polling frequency`
);
}
@@ -368,7 +369,7 @@ function adjustPollingFrequency(): void {
Math.abs(newInterval - pollingState.interval) > 500 &&
pollingState.isPolling
) {
console.log(
logger.log(
`🔄 Adjusting polling interval from ${pollingState.interval}ms to ${newInterval}ms`
);
pollingState.interval = newInterval;
@@ -388,7 +389,7 @@ function handleNetworkError(error: any): void {
pollingState.errorCount++;
pollingState.reconnectAttempts++;
console.error(
logger.error(
`❌ Network error (attempt ${pollingState.reconnectAttempts}/${pollingState.maxReconnectAttempts}):`,
error
);
@@ -410,7 +411,7 @@ function handleNetworkError(error: any): void {
const maxBackoffDelay = pollingState.maxInterval;
const finalDelay = Math.min(backoffDelay, maxBackoffDelay);
console.log(
logger.log(
`🔄 Retrying connection in ${finalDelay}ms (attempt ${pollingState.reconnectAttempts})`
);
@@ -432,7 +433,7 @@ function handleNetworkError(error: any): void {
}
function enterOfflineMode(): void {
console.warn('⚠️ Entering offline mode due to persistent connection failures');
logger.warn('⚠️ Entering offline mode due to persistent connection failures');
pollingState.isOfflineMode = true;
stopPolling();
@@ -462,7 +463,7 @@ function attemptReconnection(): void {
return;
}
console.log('🔄 Attempting to reconnect from offline mode...');
logger.log('🔄 Attempting to reconnect from offline mode...');
// Reset connection state
pollingState.isOfflineMode = false;
@@ -474,7 +475,7 @@ function attemptReconnection(): void {
// Try to restart polling
startPolling().catch((error) => {
console.error('Failed to reconnect:', error);
logger.error('Failed to reconnect:', error);
enterOfflineMode();
});
}
@@ -509,7 +510,7 @@ async function handleExtensionError(
...context
});
console.error(`Extension Error [${operation}]:`, error);
logger.error(`Extension Error [${operation}]:`, error);
await errorHandler.handleError(
error instanceof Error ? error : new Error(String(error)),
context
@@ -527,7 +528,7 @@ async function handleMCPError(
context
);
console.error(`MCP Error [${operation}]:`, error);
logger.error(`MCP Error [${operation}]:`, error);
await errorHandler.handleError(mcpError, context);
}
@@ -542,7 +543,7 @@ async function handleTaskLoadingError(
context
);
console.error(`Task Loading Error [${operation}]:`, error);
logger.error(`Task Loading Error [${operation}]:`, error);
await errorHandler.handleError(taskError, context);
}
@@ -557,7 +558,7 @@ async function handleNetworkConnectionError(
context
);
console.error(`Network Error [${operation}]:`, error);
logger.error(`Network Error [${operation}]:`, error);
await errorHandler.handleError(networkError, context);
}
@@ -572,16 +573,16 @@ async function handleUIError(
context
);
console.error(`UI Error [${operation}]:`, error);
logger.error(`UI Error [${operation}]:`, error);
await errorHandler.handleError(uiError, context);
}
// This method is called when your extension is activated
// Your extension is activated the very first time the command is executed
export function activate(context: vscode.ExtensionContext) {
console.log('🎉 Task Master Kanban extension is now active!');
console.log('🎉 Extension context:', context);
console.log(
logger.log('🎉 Task Master Kanban extension is now active!');
logger.log('🎉 Extension context:', context);
logger.log(
'🔍 DEBUGGING: Extension activation started at',
new Date().toISOString()
);
@@ -614,7 +615,7 @@ export function activate(context: vscode.ExtensionContext) {
const showKanbanCommand = vscode.commands.registerCommand(
'taskr.showKanbanBoard',
async () => {
console.log('🎯 Show Kanban command executed!');
logger.log('🎯 Show Kanban command executed!');
// Check if panel already exists
const existingPanel = activeWebviewPanels.find(
@@ -668,11 +669,11 @@ export function activate(context: vscode.ExtensionContext) {
// Handle messages from webview
panel.webview.onDidReceiveMessage(async (message) => {
console.log('📨 Received message from webview:', message);
logger.log('📨 Received message from webview:', message);
switch (message.type) {
case 'ready':
console.log('🚀 Webview is ready!');
logger.log('🚀 Webview is ready!');
// Send initial configuration or data
panel.webview.postMessage({
type: 'init',
@@ -681,7 +682,7 @@ export function activate(context: vscode.ExtensionContext) {
break;
case 'getTasks':
console.log('📋 Getting tasks...');
logger.log('📋 Getting tasks...');
try {
if (!taskMasterApi) {
throw new Error(
@@ -707,14 +708,14 @@ export function activate(context: vscode.ExtensionContext) {
requestId: message.requestId,
data: tasksResult.data
});
console.log(
logger.log(
`✅ Retrieved ${tasksResult.data?.length || 0} tasks from Task Master`
);
} else {
throw new Error(tasksResult.error || 'Failed to get tasks');
}
} catch (error) {
console.error('❌ Error getting tasks:', error);
logger.error('❌ Error getting tasks:', error);
// Send error to webview instead of falling back to sample data
panel.webview.postMessage({
@@ -735,7 +736,7 @@ export function activate(context: vscode.ExtensionContext) {
break;
case 'updateTaskStatus':
console.log('🔄 Updating task status:', message.data);
logger.log('🔄 Updating task status:', message.data);
try {
if (
taskMasterApi &&
@@ -761,7 +762,7 @@ export function activate(context: vscode.ExtensionContext) {
newStatus: message.data.newStatus
}
});
console.log(
logger.log(
`✅ Updated task ${message.data.taskId} status to ${message.data.newStatus}`
);
} else {
@@ -775,7 +776,7 @@ export function activate(context: vscode.ExtensionContext) {
);
}
} catch (error) {
console.error('❌ Error updating task status:', error);
logger.error('❌ Error updating task status:', error);
panel.webview.postMessage({
type: 'error',
requestId: message.requestId,
@@ -788,7 +789,7 @@ export function activate(context: vscode.ExtensionContext) {
break;
case 'updateTask':
console.log('📝 Updating task content:', message.data);
logger.log('📝 Updating task content:', message.data);
try {
if (
taskMasterApi &&
@@ -816,7 +817,7 @@ export function activate(context: vscode.ExtensionContext) {
updates: message.data.updates
}
});
console.log(`✅ Updated task ${message.data.taskId} content`);
logger.log(`✅ Updated task ${message.data.taskId} content`);
} else {
throw new Error(
updateResult.error || 'Failed to update task'
@@ -828,7 +829,7 @@ export function activate(context: vscode.ExtensionContext) {
);
}
} catch (error) {
console.error('❌ Error updating task:', error);
logger.error('❌ Error updating task:', error);
panel.webview.postMessage({
type: 'error',
requestId: message.requestId,
@@ -841,7 +842,7 @@ export function activate(context: vscode.ExtensionContext) {
break;
case 'updateSubtask':
console.log('📝 Updating subtask content:', message.data);
logger.log('📝 Updating subtask content:', message.data);
try {
if (
taskMasterApi &&
@@ -868,7 +869,7 @@ export function activate(context: vscode.ExtensionContext) {
prompt: message.data.prompt
}
});
console.log(
logger.log(
`✅ Updated subtask ${message.data.taskId} content`
);
} else {
@@ -882,7 +883,7 @@ export function activate(context: vscode.ExtensionContext) {
);
}
} catch (error) {
console.error('❌ Error updating subtask:', error);
logger.error('❌ Error updating subtask:', error);
panel.webview.postMessage({
type: 'error',
requestId: message.requestId,
@@ -895,7 +896,7 @@ export function activate(context: vscode.ExtensionContext) {
break;
case 'addSubtask':
console.log(' Adding new subtask:', message.data);
logger.log(' Adding new subtask:', message.data);
try {
if (
taskMasterApi &&
@@ -921,7 +922,7 @@ export function activate(context: vscode.ExtensionContext) {
subtaskData: message.data.subtaskData
}
});
console.log(
logger.log(
`✅ Added subtask to task ${message.data.parentTaskId}`
);
} else {
@@ -933,7 +934,7 @@ export function activate(context: vscode.ExtensionContext) {
);
}
} catch (error) {
console.error('❌ Error adding subtask:', error);
logger.error('❌ Error adding subtask:', error);
panel.webview.postMessage({
type: 'error',
requestId: message.requestId,
@@ -946,7 +947,7 @@ export function activate(context: vscode.ExtensionContext) {
break;
case 'startPolling':
console.log('🔄 Manual start polling requested');
logger.log('🔄 Manual start polling requested');
await startPolling();
panel.webview.postMessage({
type: 'pollingStarted',
@@ -956,7 +957,7 @@ export function activate(context: vscode.ExtensionContext) {
break;
case 'stopPolling':
console.log('⏹️ Manual stop polling requested');
logger.log('⏹️ Manual stop polling requested');
stopPolling();
panel.webview.postMessage({
type: 'pollingStopped',
@@ -966,7 +967,7 @@ export function activate(context: vscode.ExtensionContext) {
break;
case 'getPollingStatus':
console.log('📊 Polling status requested');
logger.log('📊 Polling status requested');
panel.webview.postMessage({
type: 'pollingStatus',
requestId: message.requestId,
@@ -980,7 +981,7 @@ export function activate(context: vscode.ExtensionContext) {
break;
case 'attemptReconnection':
console.log('🔄 Manual reconnection requested');
logger.log('🔄 Manual reconnection requested');
if (pollingState.isOfflineMode) {
attemptReconnection();
panel.webview.postMessage({
@@ -999,7 +1000,7 @@ export function activate(context: vscode.ExtensionContext) {
break;
case 'getNetworkStatus':
console.log('📊 Network status requested');
logger.log('📊 Network status requested');
panel.webview.postMessage({
type: 'networkStatus',
requestId: message.requestId,
@@ -1014,7 +1015,7 @@ export function activate(context: vscode.ExtensionContext) {
break;
case 'reactError':
console.log('🔥 React error reported from webview:', message.data);
logger.log('🔥 React error reported from webview:', message.data);
try {
await handleUIError(
new Error(message.data.message),
@@ -1026,12 +1027,12 @@ export function activate(context: vscode.ExtensionContext) {
}
);
} catch (error) {
console.error('Failed to handle React error:', error);
logger.error('Failed to handle React error:', error);
}
break;
case 'readTaskFileData':
console.log('📄 Reading task file data:', message.data);
logger.log('📄 Reading task file data:', message.data);
{
const { requestId } = message;
try {
@@ -1050,7 +1051,7 @@ export function activate(context: vscode.ExtensionContext) {
'tasks',
'tasks.json'
);
console.log('🔍 Looking for tasks.json at:', tasksJsonPath);
logger.log('🔍 Looking for tasks.json at:', tasksJsonPath);
// Check if file exists
if (!fs.existsSync(tasksJsonPath)) {
@@ -1060,7 +1061,7 @@ export function activate(context: vscode.ExtensionContext) {
'tasks',
'tasks.json'
);
console.log('🔍 Trying legacy path:', legacyPath);
logger.log('🔍 Trying legacy path:', legacyPath);
if (!fs.existsSync(legacyPath)) {
throw new Error(
'tasks.json not found in .taskmaster/tasks/ or tasks/ directory'
@@ -1068,7 +1069,7 @@ export function activate(context: vscode.ExtensionContext) {
}
// Use legacy path
const content = fs.readFileSync(legacyPath, 'utf8');
console.log(
logger.log(
'📖 Read legacy tasks.json, content length:',
content.length
);
@@ -1078,7 +1079,7 @@ export function activate(context: vscode.ExtensionContext) {
tagName,
workspaceFolder.uri.fsPath
);
console.log('✅ Parsed task data for legacy path:', taskData);
logger.log('✅ Parsed task data for legacy path:', taskData);
panel.webview.postMessage({
type: 'response',
requestId,
@@ -1089,7 +1090,7 @@ export function activate(context: vscode.ExtensionContext) {
// Read and parse tasks.json
const content = fs.readFileSync(tasksJsonPath, 'utf8');
console.log(
logger.log(
'📖 Read tasks.json, content length:',
content.length
);
@@ -1099,7 +1100,7 @@ export function activate(context: vscode.ExtensionContext) {
tagName,
workspaceFolder.uri.fsPath
);
console.log('✅ Parsed task data:', taskData);
logger.log('✅ Parsed task data:', taskData);
panel.webview.postMessage({
type: 'response',
@@ -1107,9 +1108,9 @@ export function activate(context: vscode.ExtensionContext) {
data: taskData
});
console.log(`✅ Retrieved task file data for task ${taskId}`);
logger.log(`✅ Retrieved task file data for task ${taskId}`);
} catch (error) {
console.error('❌ Error reading task file data:', error);
logger.error('❌ Error reading task file data:', error);
panel.webview.postMessage({
type: 'error',
requestId,
@@ -1123,7 +1124,7 @@ export function activate(context: vscode.ExtensionContext) {
break;
case 'mcpRequest':
console.log('📊 MCP Request:', message);
logger.log('📊 MCP Request:', message);
const { requestId: mcpRequestId, tool, parameters } = message;
try {
if (!taskMasterApi) {
@@ -1140,7 +1141,7 @@ export function activate(context: vscode.ExtensionContext) {
switch (tool) {
case 'complexity_report':
console.log('📊 Calling complexity_report MCP tool');
logger.log('📊 Calling complexity_report MCP tool');
try {
// Use the private callMCPTool method via type assertion to access it
const mcpResult = await (taskMasterApi as any).callMCPTool(
@@ -1164,7 +1165,7 @@ export function activate(context: vscode.ExtensionContext) {
break;
case 'analyze_project_complexity':
console.log('🧮 Calling analyze_project_complexity MCP tool');
logger.log('🧮 Calling analyze_project_complexity MCP tool');
try {
// Use the private callMCPTool method via type assertion to access it
const mcpResult = await (taskMasterApi as any).callMCPTool(
@@ -1197,14 +1198,14 @@ export function activate(context: vscode.ExtensionContext) {
requestId: mcpRequestId,
data: result.data
});
console.log(`✅ MCP tool ${tool} executed successfully`);
logger.log(`✅ MCP tool ${tool} executed successfully`);
} else {
throw new Error(
result.error || `Failed to execute MCP tool: ${tool}`
);
}
} catch (error) {
console.error(`❌ Error executing MCP tool ${tool}:`, error);
logger.error(`❌ Error executing MCP tool ${tool}:`, error);
panel.webview.postMessage({
type: 'error',
requestId: mcpRequestId,
@@ -1217,7 +1218,7 @@ export function activate(context: vscode.ExtensionContext) {
break;
default:
console.log('❓ Unknown message type:', message.type);
logger.log('❓ Unknown message type:', message.type);
}
});
@@ -1228,7 +1229,7 @@ export function activate(context: vscode.ExtensionContext) {
const checkConnectionCommand = vscode.commands.registerCommand(
'taskr.checkConnection',
async () => {
console.log('🔗 Check connection command executed!');
logger.log('🔗 Check connection command executed!');
vscode.window.showInformationMessage('Check connection command works!');
}
);
@@ -1236,7 +1237,7 @@ export function activate(context: vscode.ExtensionContext) {
const reconnectCommand = vscode.commands.registerCommand(
'taskr.reconnect',
async () => {
console.log('🔄 Reconnect command executed!');
logger.log('🔄 Reconnect command executed!');
vscode.window.showInformationMessage('Reconnect command works!');
}
);
@@ -1244,7 +1245,7 @@ export function activate(context: vscode.ExtensionContext) {
const openSettingsCommand = vscode.commands.registerCommand(
'taskr.openSettings',
() => {
console.log('⚙️ Open settings command executed!');
logger.log('⚙️ Open settings command executed!');
vscode.commands.executeCommand(
'workbench.action.openSettings',
'@ext:taskr taskmaster'
@@ -1259,7 +1260,7 @@ export function activate(context: vscode.ExtensionContext) {
openSettingsCommand
);
console.log('✅ All commands registered successfully!');
logger.log('✅ All commands registered successfully!');
}
// Generate webview HTML content
@@ -1393,7 +1394,7 @@ function getSampleTasks() {
// This method is called when your extension is deactivated
export function deactivate() {
console.log('👋 Task Master Kanban extension deactivated');
logger.log('👋 Task Master Kanban extension deactivated');
// Stop polling
stopPolling();

View File

@@ -1,5 +1,6 @@
import * as vscode from 'vscode';
import { MCPConfig } from './mcpClient';
import { logger } from './logger';
export interface TaskMasterConfig {
mcp: MCPServerConfig;
@@ -469,7 +470,7 @@ export class ConfigManager {
private setupConfigWatcher(): void {
vscode.workspace.onDidChangeConfiguration((event) => {
if (event.affectsConfiguration('taskmaster')) {
console.log('Task Master configuration changed, reloading...');
logger.log('Task Master configuration changed, reloading...');
this.config = this.loadConfig();
this.notifyConfigChange();
}
@@ -499,7 +500,7 @@ export class ConfigManager {
try {
listener(this.config);
} catch (error) {
console.error('Error in configuration change listener:', error);
logger.error('Error in configuration change listener:', error);
}
});
}

View File

@@ -1,5 +1,6 @@
import * as vscode from 'vscode';
import { MCPClientManager, MCPConfig, MCPServerStatus } from './mcpClient';
import { logger } from './logger';
export interface ConnectionEvent {
type: 'connected' | 'disconnected' | 'error' | 'reconnecting';
@@ -82,7 +83,7 @@ export class ConnectionManager {
this.logEvent({ type: 'connected', timestamp: new Date() });
console.log('Connection manager: Successfully connected');
logger.log('Connection manager: Successfully connected');
} catch (error) {
this.logEvent({
type: 'error',
@@ -216,11 +217,11 @@ export class ConnectionManager {
// Attempt reconnection if connection seems lost
if (this.health.consecutiveFailures >= 3 && !this.isReconnecting) {
console.log(
logger.log(
'Multiple consecutive failures detected, attempting reconnection...'
);
this.reconnectWithBackoff().catch((err) => {
console.error('Reconnection failed:', err);
logger.error('Reconnection failed:', err);
});
}
@@ -242,7 +243,7 @@ export class ConnectionManager {
try {
await this.connect();
} catch (error) {
console.error('Failed to connect with new configuration:', error);
logger.error('Failed to connect with new configuration:', error);
}
}
@@ -256,7 +257,7 @@ export class ConnectionManager {
try {
await this.testConnection();
} catch (error) {
console.error('Health check failed:', error);
logger.error('Health check failed:', error);
}
}, 15000); // Check every 15 seconds
}
@@ -303,7 +304,7 @@ export class ConnectionManager {
this.maxBackoffMs
);
console.log(
logger.log(
`Attempting reconnection ${this.reconnectAttempts}/${this.maxReconnectAttempts} in ${backoffMs}ms...`
);
@@ -312,7 +313,7 @@ export class ConnectionManager {
try {
await this.connect();
} catch (error) {
console.error(
logger.error(
`Reconnection attempt ${this.reconnectAttempts} failed:`,
error
);

View File

@@ -1,4 +1,5 @@
import * as vscode from 'vscode';
import { logger } from './logger';
import {
shouldShowNotification,
getNotificationType,
@@ -322,7 +323,7 @@ export class ErrorHandler {
await errorDetails.recovery.action();
this.markErrorResolved(logEntry.id);
} catch (recoveryError) {
console.error('Error recovery failed:', recoveryError);
logger.error('Error recovery failed:', recoveryError);
logEntry.attempts++;
logEntry.lastAttempt = new Date();
}
@@ -548,10 +549,10 @@ export class ErrorHandler {
switch (errorDetails.severity) {
case ErrorSeverity.CRITICAL:
case ErrorSeverity.HIGH:
console.error(logMessage, errorDetails);
logger.error(logMessage, errorDetails);
break;
case ErrorSeverity.MEDIUM:
console.warn(logMessage, errorDetails);
logger.warn(logMessage, errorDetails);
break;
case ErrorSeverity.LOW:
console.info(logMessage, errorDetails);
@@ -634,7 +635,7 @@ ${errorDetails.context ? JSON.stringify(errorDetails.context, null, 2) : 'None'}
try {
listener(errorDetails);
} catch (error) {
console.error('Error in error listener:', error);
logger.error('Error in error listener:', error);
}
});
}

View File

@@ -0,0 +1,86 @@
import * as vscode from 'vscode';
/**
* Logger that outputs to VS Code's output channel instead of console
* This prevents interference with MCP stdio communication
*/
export class ExtensionLogger {
private static instance: ExtensionLogger;
private outputChannel: vscode.OutputChannel;
private debugMode: boolean;
private constructor() {
this.outputChannel = vscode.window.createOutputChannel('Task Master');
const config = vscode.workspace.getConfiguration('taskmaster');
this.debugMode = config.get<boolean>('debug.enableLogging', true);
}
static getInstance(): ExtensionLogger {
if (!ExtensionLogger.instance) {
ExtensionLogger.instance = new ExtensionLogger();
}
return ExtensionLogger.instance;
}
log(message: string, ...args: any[]): void {
if (!this.debugMode) return;
const timestamp = new Date().toISOString();
const formattedMessage = this.formatMessage(message, args);
this.outputChannel.appendLine(`[${timestamp}] ${formattedMessage}`);
}
error(message: string, ...args: any[]): void {
const timestamp = new Date().toISOString();
const formattedMessage = this.formatMessage(message, args);
this.outputChannel.appendLine(`[${timestamp}] ERROR: ${formattedMessage}`);
}
warn(message: string, ...args: any[]): void {
if (!this.debugMode) return;
const timestamp = new Date().toISOString();
const formattedMessage = this.formatMessage(message, args);
this.outputChannel.appendLine(`[${timestamp}] WARN: ${formattedMessage}`);
}
debug(message: string, ...args: any[]): void {
if (!this.debugMode) return;
const timestamp = new Date().toISOString();
const formattedMessage = this.formatMessage(message, args);
this.outputChannel.appendLine(`[${timestamp}] DEBUG: ${formattedMessage}`);
}
private formatMessage(message: string, args: any[]): string {
if (args.length === 0) {
return message;
}
// Convert objects to JSON for better readability
const formattedArgs = args.map((arg) => {
if (typeof arg === 'object' && arg !== null) {
try {
return JSON.stringify(arg, null, 2);
} catch {
return String(arg);
}
}
return String(arg);
});
return `${message} ${formattedArgs.join(' ')}`;
}
show(): void {
this.outputChannel.show();
}
dispose(): void {
this.outputChannel.dispose();
}
setDebugMode(enabled: boolean): void {
this.debugMode = enabled;
}
}
// Export a singleton instance for convenience
export const logger = ExtensionLogger.getInstance();

View File

@@ -1,6 +1,7 @@
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import * as vscode from 'vscode';
import { logger } from './logger';
export interface MCPConfig {
command: string;
@@ -23,7 +24,7 @@ export class MCPClientManager {
private connectionPromise: Promise<void> | null = null;
constructor(config: MCPConfig) {
console.log(
logger.log(
'🔍 DEBUGGING: MCPClientManager constructor called with config:',
config
);
@@ -55,11 +56,11 @@ export class MCPClientManager {
await this.disconnect();
// Create the transport - it will handle spawning the server process internally
console.log(
logger.log(
`Starting MCP server: ${this.config.command} ${this.config.args?.join(' ') || ''}`
);
console.log('🔍 DEBUGGING: Transport config cwd:', this.config.cwd);
console.log('🔍 DEBUGGING: Process cwd before spawn:', process.cwd());
logger.log('🔍 DEBUGGING: Transport config cwd:', this.config.cwd);
logger.log('🔍 DEBUGGING: Process cwd before spawn:', process.cwd());
// Test if the target directory and .taskmaster exist
const fs = require('fs');
@@ -69,19 +70,19 @@ export class MCPClientManager {
const taskmasterDir = path.join(targetDir, '.taskmaster');
const tasksFile = path.join(taskmasterDir, 'tasks', 'tasks.json');
console.log(
logger.log(
'🔍 DEBUGGING: Checking target directory:',
targetDir,
'exists:',
fs.existsSync(targetDir)
);
console.log(
logger.log(
'🔍 DEBUGGING: Checking .taskmaster dir:',
taskmasterDir,
'exists:',
fs.existsSync(taskmasterDir)
);
console.log(
logger.log(
'🔍 DEBUGGING: Checking tasks.json:',
tasksFile,
'exists:',
@@ -90,10 +91,10 @@ export class MCPClientManager {
if (fs.existsSync(tasksFile)) {
const stats = fs.statSync(tasksFile);
console.log('🔍 DEBUGGING: tasks.json size:', stats.size, 'bytes');
logger.log('🔍 DEBUGGING: tasks.json size:', stats.size, 'bytes');
}
} catch (error) {
console.log('🔍 DEBUGGING: Error checking filesystem:', error);
logger.log('🔍 DEBUGGING: Error checking filesystem:', error);
}
this.transport = new StdioClientTransport({
@@ -108,12 +109,12 @@ export class MCPClientManager {
}
});
console.log('🔍 DEBUGGING: Transport created, checking process...');
logger.log('🔍 DEBUGGING: Transport created, checking process...');
// Set up transport event handlers
this.transport.onerror = (error: Error) => {
console.error('❌ MCP transport error:', error);
console.error('Transport error details:', {
logger.error('❌ MCP transport error:', error);
logger.error('Transport error details:', {
message: error.message,
stack: error.stack,
code: (error as any).code,
@@ -127,7 +128,7 @@ export class MCPClientManager {
};
this.transport.onclose = () => {
console.log('🔌 MCP transport closed');
logger.log('🔌 MCP transport closed');
this.status = { isRunning: false };
this.client = null;
this.transport = null;
@@ -135,7 +136,7 @@ export class MCPClientManager {
// Add message handler like the working debug script
this.transport.onmessage = (message: any) => {
console.log('📤 MCP server message:', message);
logger.log('📤 MCP server message:', message);
};
// Create the client
@@ -152,14 +153,14 @@ export class MCPClientManager {
);
// Connect the client to the transport (this automatically starts the transport)
console.log('🔄 Attempting MCP client connection...');
console.log('MCP config:', {
logger.log('🔄 Attempting MCP client connection...');
logger.log('MCP config:', {
command: this.config.command,
args: this.config.args,
cwd: this.config.cwd
});
console.log('Current working directory:', process.cwd());
console.log(
logger.log('Current working directory:', process.cwd());
logger.log(
'VS Code workspace folders:',
vscode.workspace.workspaceFolders?.map((f) => f.uri.fsPath)
);
@@ -167,37 +168,37 @@ export class MCPClientManager {
// Check if process was created before connecting
if (this.transport && (this.transport as any).process) {
const proc = (this.transport as any).process;
console.log('📝 MCP server process PID:', proc.pid);
console.log('📝 Process working directory will be:', this.config.cwd);
logger.log('📝 MCP server process PID:', proc.pid);
logger.log('📝 Process working directory will be:', this.config.cwd);
proc.on('exit', (code: number, signal: string) => {
console.log(
logger.log(
`🔚 MCP server process exited with code ${code}, signal ${signal}`
);
if (code !== 0) {
console.log('❌ Non-zero exit code indicates server failure');
logger.log('❌ Non-zero exit code indicates server failure');
}
});
proc.on('error', (error: Error) => {
console.log('❌ MCP server process error:', error);
logger.log('❌ MCP server process error:', error);
});
// Listen to stderr to see server-side errors
if (proc.stderr) {
proc.stderr.on('data', (data: Buffer) => {
console.log('📥 MCP server stderr:', data.toString());
logger.log('📥 MCP server stderr:', data.toString());
});
}
// Listen to stdout for server messages
if (proc.stdout) {
proc.stdout.on('data', (data: Buffer) => {
console.log('📤 MCP server stdout:', data.toString());
logger.log('📤 MCP server stdout:', data.toString());
});
}
} else {
console.log('⚠️ No process found in transport before connection');
logger.log('⚠️ No process found in transport before connection');
}
await this.client.connect(this.transport);
@@ -208,12 +209,12 @@ export class MCPClientManager {
pid: this.transport.pid || undefined
};
console.log('MCP client connected successfully');
logger.log('MCP client connected successfully');
vscode.window.showInformationMessage(
'Task Master connected successfully'
);
} catch (error) {
console.error('Failed to connect to MCP server:', error);
logger.error('Failed to connect to MCP server:', error);
this.status = {
isRunning: false,
error: error instanceof Error ? error.message : 'Unknown error'
@@ -232,13 +233,13 @@ export class MCPClientManager {
* Disconnect from the MCP server and clean up resources
*/
async disconnect(): Promise<void> {
console.log('Disconnecting from MCP server');
logger.log('Disconnecting from MCP server');
if (this.client) {
try {
await this.client.close();
} catch (error) {
console.error('Error closing MCP client:', error);
logger.error('Error closing MCP client:', error);
}
this.client = null;
}
@@ -247,7 +248,7 @@ export class MCPClientManager {
try {
await this.transport.close();
} catch (error) {
console.error('Error closing MCP transport:', error);
logger.error('Error closing MCP transport:', error);
}
this.transport = null;
}
@@ -281,7 +282,7 @@ export class MCPClientManager {
return result;
} catch (error) {
console.error(`Error calling MCP tool "${toolName}":`, error);
logger.error(`Error calling MCP tool "${toolName}":`, error);
throw error;
}
}
@@ -297,13 +298,13 @@ export class MCPClientManager {
}
const result = await this.client.listTools();
console.log(
logger.log(
'Available MCP tools:',
result.tools?.map((t) => t.name) || []
);
return true;
} catch (error) {
console.error('Connection test failed:', error);
logger.error('Connection test failed:', error);
return false;
}
}
@@ -328,7 +329,7 @@ export class MCPClientManager {
* Create MCP configuration from VS Code settings
*/
export function createMCPConfigFromSettings(): MCPConfig {
console.log(
logger.log(
'🔍 DEBUGGING: createMCPConfigFromSettings called at',
new Date().toISOString()
);
@@ -347,7 +348,7 @@ export function createMCPConfigFromSettings(): MCPConfig {
const cwd = config.get<string>('mcp.cwd', defaultCwd);
const env = config.get<Record<string, string>>('mcp.env');
console.log('✅ Using workspace directory:', defaultCwd);
logger.log('✅ Using workspace directory:', defaultCwd);
// If using default 'npx', try to find the full path on macOS/Linux
if (command === 'npx') {
@@ -363,7 +364,7 @@ export function createMCPConfigFromSettings(): MCPConfig {
try {
if (path === 'npx' || fs.existsSync(path)) {
command = path;
console.log(`✅ Using npx at: ${path}`);
logger.log(`✅ Using npx at: ${path}`);
break;
}
} catch (error) {

View File

@@ -1,5 +1,6 @@
import * as vscode from 'vscode';
import { ErrorCategory, ErrorSeverity, NotificationType } from './errorHandler';
import { logger } from './logger';
export interface NotificationPreferences {
// Global notification toggles
@@ -240,9 +241,7 @@ export class NotificationPreferencesManager {
vscode.ConfigurationTarget.Global
);
console.log(
'Task Master Kanban notification preferences reset to defaults'
);
logger.log('Task Master Kanban notification preferences reset to defaults');
}
/**

View File

@@ -1,5 +1,6 @@
import * as fs from 'fs';
import * as path from 'path';
import { logger } from './logger';
export interface TaskFileData {
details?: string;
@@ -85,7 +86,7 @@ export async function readTaskFileData(
return { details: undefined, testStrategy: undefined };
}
} catch (error) {
console.error('Error reading task file data:', error);
logger.error('Error reading task file data:', error);
return { details: undefined, testStrategy: undefined };
}
}
@@ -103,21 +104,21 @@ export function findTaskById(
// Check if this is a subtask ID with dotted notation (e.g., "1.2")
if (taskId.includes('.')) {
const [parentId, subtaskId] = taskId.split('.');
console.log('🔍 Looking for subtask:', { parentId, subtaskId, taskId });
logger.log('🔍 Looking for subtask:', { parentId, subtaskId, taskId });
// Find the parent task first
const parentTask = tasks.find((task) => String(task.id) === parentId);
if (!parentTask || !parentTask.subtasks) {
console.log('❌ Parent task not found or has no subtasks:', parentId);
logger.log('❌ Parent task not found or has no subtasks:', parentId);
return undefined;
}
console.log(
logger.log(
'📋 Parent task found with',
parentTask.subtasks.length,
'subtasks'
);
console.log(
logger.log(
'🔍 Subtask IDs in parent:',
parentTask.subtasks.map((st) => st.id)
);
@@ -127,9 +128,9 @@ export function findTaskById(
(st) => String(st.id) === subtaskId
);
if (subtask) {
console.log('✅ Subtask found:', subtask.id);
logger.log('✅ Subtask found:', subtask.id);
} else {
console.log('❌ Subtask not found:', subtaskId);
logger.log('❌ Subtask not found:', subtaskId);
}
return subtask;
}
@@ -159,7 +160,7 @@ export function parseTaskFileData(
tagName: string,
workspacePath?: string
): TaskFileData {
console.log('🔍 parseTaskFileData called with:', {
logger.log('🔍 parseTaskFileData called with:', {
taskId,
tagName,
contentLength: content.length
@@ -167,17 +168,17 @@ export function parseTaskFileData(
try {
const tasksJson: TasksJsonStructure = JSON.parse(content);
console.log('📊 Available tags:', Object.keys(tasksJson));
logger.log('📊 Available tags:', Object.keys(tasksJson));
// Get the tag data
const tagData = tasksJson[tagName];
if (!tagData || !tagData.tasks) {
console.log('❌ Tag not found or no tasks in tag:', tagName);
logger.log('❌ Tag not found or no tasks in tag:', tagName);
return { details: undefined, testStrategy: undefined };
}
console.log('📋 Tag found with', tagData.tasks.length, 'tasks');
console.log(
logger.log('📋 Tag found with', tagData.tasks.length, 'tasks');
logger.log(
'🔍 Available task IDs:',
tagData.tasks.map((t) => t.id)
);
@@ -185,18 +186,18 @@ export function parseTaskFileData(
// Find the task
const task = findTaskById(tagData.tasks, taskId);
if (!task) {
console.log('❌ Task not found:', taskId);
logger.log('❌ Task not found:', taskId);
return { details: undefined, testStrategy: undefined };
}
console.log('✅ Task found:', task.id);
console.log(
logger.log('✅ Task found:', task.id);
logger.log(
'📝 Task has details:',
!!task.details,
'length:',
task.details?.length
);
console.log(
logger.log(
'🧪 Task has testStrategy:',
!!task.testStrategy,
'length:',
@@ -208,7 +209,7 @@ export function parseTaskFileData(
testStrategy: task.testStrategy
};
} catch (error) {
console.error('❌ Error parsing tasks.json:', error);
logger.error('❌ Error parsing tasks.json:', error);
return { details: undefined, testStrategy: undefined };
}
}

View File

@@ -1,5 +1,6 @@
import * as vscode from 'vscode';
import { MCPClientManager } from './mcpClient';
import { logger } from './logger';
// Task Master MCP API response types
export interface MCPTaskResponse {
@@ -155,7 +156,7 @@ export class TaskMasterApi {
this.initializeBackgroundRefresh();
}
console.log('TaskMasterApi: Initialized with enhanced caching:', {
logger.log('TaskMasterApi: Initialized with enhanced caching:', {
cacheDuration: this.config.cacheDuration,
maxSize: this.config.cache?.maxSize,
backgroundRefresh: this.config.cache?.enableBackgroundRefresh,
@@ -200,7 +201,7 @@ export class TaskMasterApi {
mcpArgs.tag = options.tag;
}
console.log('TaskMasterApi: Calling get_tasks with args:', mcpArgs);
logger.log('TaskMasterApi: Calling get_tasks with args:', mcpArgs);
// Call the MCP tool
const mcpResponse = await this.callMCPTool('get_tasks', mcpArgs);
@@ -217,7 +218,7 @@ export class TaskMasterApi {
requestDuration: Date.now() - startTime
};
} catch (error) {
console.error('TaskMasterApi: Error getting tasks:', error);
logger.error('TaskMasterApi: Error getting tasks:', error);
return {
success: false,
@@ -247,7 +248,7 @@ export class TaskMasterApi {
projectRoot: options?.projectRoot || this.getWorkspaceRoot()
};
console.log('TaskMasterApi: Calling set_task_status with args:', mcpArgs);
logger.log('TaskMasterApi: Calling set_task_status with args:', mcpArgs);
await this.callMCPTool('set_task_status', mcpArgs);
@@ -260,7 +261,7 @@ export class TaskMasterApi {
requestDuration: Date.now() - startTime
};
} catch (error) {
console.error('TaskMasterApi: Error updating task status:', error);
logger.error('TaskMasterApi: Error updating task status:', error);
return {
success: false,
@@ -331,7 +332,7 @@ export class TaskMasterApi {
mcpArgs.research = options.research;
}
console.log('TaskMasterApi: Calling update_task with args:', mcpArgs);
logger.log('TaskMasterApi: Calling update_task with args:', mcpArgs);
await this.callMCPTool('update_task', mcpArgs);
@@ -344,7 +345,7 @@ export class TaskMasterApi {
requestDuration: Date.now() - startTime
};
} catch (error) {
console.error('TaskMasterApi: Error updating task:', error);
logger.error('TaskMasterApi: Error updating task:', error);
return {
success: false,
@@ -380,7 +381,7 @@ export class TaskMasterApi {
mcpArgs.research = options.research;
}
console.log('TaskMasterApi: Calling update_subtask with args:', mcpArgs);
logger.log('TaskMasterApi: Calling update_subtask with args:', mcpArgs);
await this.callMCPTool('update_subtask', mcpArgs);
@@ -393,7 +394,7 @@ export class TaskMasterApi {
requestDuration: Date.now() - startTime
};
} catch (error) {
console.error('TaskMasterApi: Error updating subtask:', error);
logger.error('TaskMasterApi: Error updating subtask:', error);
return {
success: false,
@@ -439,7 +440,7 @@ export class TaskMasterApi {
mcpArgs.status = subtaskData.status;
}
console.log('TaskMasterApi: Calling add_subtask with args:', mcpArgs);
logger.log('TaskMasterApi: Calling add_subtask with args:', mcpArgs);
await this.callMCPTool('add_subtask', mcpArgs);
@@ -452,7 +453,7 @@ export class TaskMasterApi {
requestDuration: Date.now() - startTime
};
} catch (error) {
console.error('TaskMasterApi: Error adding subtask:', error);
logger.error('TaskMasterApi: Error adding subtask:', error);
return {
success: false,
@@ -489,7 +490,7 @@ export class TaskMasterApi {
requestDuration: Date.now() - startTime
};
} catch (error) {
console.error('TaskMasterApi: Connection test failed:', error);
logger.error('TaskMasterApi: Connection test failed:', error);
return {
success: false,
@@ -529,7 +530,7 @@ export class TaskMasterApi {
this.performBackgroundRefresh();
}, interval);
console.log(
logger.log(
`TaskMasterApi: Background refresh initialized with ${interval}ms interval`
);
}
@@ -542,7 +543,7 @@ export class TaskMasterApi {
return;
}
console.log('TaskMasterApi: Starting background cache refresh');
logger.log('TaskMasterApi: Starting background cache refresh');
const startTime = Date.now();
// Find frequently accessed entries that are close to expiration
@@ -565,7 +566,7 @@ export class TaskMasterApi {
const optionsMatch = key.match(/get_tasks_(.+)/);
if (optionsMatch) {
const options = JSON.parse(optionsMatch[1]);
console.log(`TaskMasterApi: Background refreshing cache key: ${key}`);
logger.log(`TaskMasterApi: Background refreshing cache key: ${key}`);
// Perform the refresh (this will update the cache)
await this.getTasks(options);
@@ -573,7 +574,7 @@ export class TaskMasterApi {
this.cacheAnalytics.refreshes++;
}
} catch (error) {
console.warn(
logger.warn(
`TaskMasterApi: Background refresh failed for key ${key}:`,
error
);
@@ -581,7 +582,7 @@ export class TaskMasterApi {
}
const duration = Date.now() - startTime;
console.log(
logger.log(
`TaskMasterApi: Background refresh completed in ${duration}ms, refreshed ${refreshedCount} entries`
);
}
@@ -633,7 +634,7 @@ export class TaskMasterApi {
if (evictedCount > 0) {
this.cacheAnalytics.evictions += evictedCount;
console.log(
logger.log(
`TaskMasterApi: Evicted ${evictedCount} cache entries matching pattern: ${pattern}`
);
}
@@ -661,14 +662,14 @@ export class TaskMasterApi {
}
const accessTime = Date.now() - startTime;
console.log(
logger.log(
`TaskMasterApi: Cache hit for ${key} (${accessTime}ms, ${cached.accessCount} accesses)`
);
return cached.data;
} else {
// Remove expired entry
this.cache.delete(key);
console.log(`TaskMasterApi: Cache entry expired and removed: ${key}`);
logger.log(`TaskMasterApi: Cache entry expired and removed: ${key}`);
}
}
@@ -676,7 +677,7 @@ export class TaskMasterApi {
this.cacheAnalytics.misses++;
}
console.log(`TaskMasterApi: Cache miss for ${key}`);
logger.log(`TaskMasterApi: Cache miss for ${key}`);
return null;
}
@@ -709,7 +710,7 @@ export class TaskMasterApi {
}
this.cache.set(key, entry);
console.log(
logger.log(
`TaskMasterApi: Cached data for ${key} (size: ${dataSize} bytes, TTL: ${entry.ttl || this.config.cacheDuration}ms)`
);
@@ -733,7 +734,7 @@ export class TaskMasterApi {
}
if (entries.length > 0) {
console.log(`TaskMasterApi: Evicted ${entries.length} LRU cache entries`);
logger.log(`TaskMasterApi: Evicted ${entries.length} LRU cache entries`);
}
}
@@ -755,7 +756,7 @@ export class TaskMasterApi {
// This is a simple implementation - in a more sophisticated system,
// we might prefetch related tasks, subtasks, or dependency data
if (key.includes('get_tasks') && Array.isArray(data)) {
console.log(
logger.log(
`TaskMasterApi: Scheduled prefetch for ${data.length} tasks related to ${key}`
);
// Future enhancement: prefetch individual task details, related dependencies, etc.
@@ -771,7 +772,7 @@ export class TaskMasterApi {
this.backgroundRefreshTimer = undefined;
}
this.clearCache();
console.log('TaskMasterApi: Destroyed and cleaned up resources');
logger.log('TaskMasterApi: Destroyed and cleaned up resources');
}
/**
@@ -786,7 +787,7 @@ export class TaskMasterApi {
for (let attempt = 1; attempt <= this.config.retryAttempts; attempt++) {
try {
const rawResponse = await this.mcpClient.callTool(toolName, args);
console.log(
logger.log(
`🔍 DEBUGGING: Raw MCP response for ${toolName}:`,
JSON.stringify(rawResponse, null, 2)
);
@@ -802,30 +803,30 @@ export class TaskMasterApi {
if (contentItem.type === 'text' && contentItem.text) {
try {
const parsedData = JSON.parse(contentItem.text);
console.log(
logger.log(
`🔍 DEBUGGING: Parsed MCP data for ${toolName}:`,
parsedData
);
return parsedData;
} catch (parseError) {
console.error(
logger.error(
`TaskMasterApi: Failed to parse MCP response text for ${toolName}:`,
parseError
);
console.error(`TaskMasterApi: Raw text was:`, contentItem.text);
logger.error(`TaskMasterApi: Raw text was:`, contentItem.text);
return rawResponse; // Fall back to original response
}
}
}
// If not in expected format, return as-is
console.warn(
logger.warn(
`TaskMasterApi: Unexpected MCP response format for ${toolName}, returning raw response`
);
return rawResponse;
} catch (error) {
lastError = error instanceof Error ? error : new Error('Unknown error');
console.warn(
logger.warn(
`TaskMasterApi: Attempt ${attempt}/${this.config.retryAttempts} failed for ${toolName}:`,
lastError.message
);
@@ -856,7 +857,7 @@ export class TaskMasterApi {
// Validate response structure
const validationResult = this.validateMCPResponse(mcpResponse);
if (!validationResult.isValid) {
console.warn(
logger.warn(
'TaskMasterApi: MCP response validation failed:',
validationResult.errors
);
@@ -864,7 +865,7 @@ export class TaskMasterApi {
}
const tasks = mcpResponse.data.tasks || [];
console.log(
logger.log(
`TaskMasterApi: Transforming ${tasks.length} tasks from MCP response`
);
@@ -892,7 +893,7 @@ export class TaskMasterApi {
error: errorMsg,
task: tasks[i]
});
console.error(
logger.error(
`TaskMasterApi: Failed to transform task at index ${i}:`,
errorMsg,
tasks[i]
@@ -902,7 +903,7 @@ export class TaskMasterApi {
// Log transformation summary
const transformDuration = Date.now() - transformStartTime;
console.log(
logger.log(
`TaskMasterApi: Transformation completed in ${transformDuration}ms`,
{
totalTasks: tasks.length,
@@ -917,7 +918,7 @@ export class TaskMasterApi {
return transformedTasks;
} catch (error) {
console.error(
logger.error(
'TaskMasterApi: Critical error during response transformation:',
error
);
@@ -972,7 +973,7 @@ export class TaskMasterApi {
*/
private transformSingleTask(task: any, index: number): TaskMasterTask | null {
if (!task || typeof task !== 'object') {
console.warn(
logger.warn(
`TaskMasterApi: Task at index ${index} is not a valid object:`,
task
);
@@ -1045,7 +1046,7 @@ export class TaskMasterApi {
dependencies.length > 0 ||
complexityScore !== undefined
) {
console.log(
logger.log(
`TaskMasterApi: Successfully transformed complex task ${taskId}:`,
{
subtaskCount: subtasks.length,
@@ -1059,7 +1060,7 @@ export class TaskMasterApi {
return transformedTask;
} catch (error) {
console.error(
logger.error(
`TaskMasterApi: Error transforming task at index ${index}:`,
error,
task
@@ -1074,14 +1075,14 @@ export class TaskMasterApi {
private validateAndNormalizeId(id: any, fallbackIndex: number): string {
if (id === null || id === undefined) {
const generatedId = `generated_${fallbackIndex}_${Date.now()}`;
console.warn(`TaskMasterApi: Task missing ID, generated: ${generatedId}`);
logger.warn(`TaskMasterApi: Task missing ID, generated: ${generatedId}`);
return generatedId;
}
const stringId = String(id).trim();
if (stringId === '') {
const generatedId = `empty_${fallbackIndex}_${Date.now()}`;
console.warn(
logger.warn(
`TaskMasterApi: Task has empty ID, generated: ${generatedId}`
);
return generatedId;
@@ -1103,7 +1104,7 @@ export class TaskMasterApi {
}
if (typeof value !== 'string') {
console.warn(
logger.warn(
`TaskMasterApi: ${fieldName} is not a string, converting:`,
value
);
@@ -1127,7 +1128,7 @@ export class TaskMasterApi {
}
if (!Array.isArray(dependencies)) {
console.warn(
logger.warn(
`TaskMasterApi: Dependencies for task ${taskId} is not an array:`,
dependencies
);
@@ -1138,7 +1139,7 @@ export class TaskMasterApi {
for (let i = 0; i < dependencies.length; i++) {
const dep = dependencies[i];
if (dep === null || dep === undefined) {
console.warn(
logger.warn(
`TaskMasterApi: Null dependency at index ${i} for task ${taskId}`
);
continue;
@@ -1146,7 +1147,7 @@ export class TaskMasterApi {
const stringDep = String(dep).trim();
if (stringDep === '') {
console.warn(
logger.warn(
`TaskMasterApi: Empty dependency at index ${i} for task ${taskId}`
);
continue;
@@ -1154,7 +1155,7 @@ export class TaskMasterApi {
// Check for self-dependency
if (stringDep === taskId) {
console.warn(
logger.warn(
`TaskMasterApi: Self-dependency detected for task ${taskId}, skipping`
);
continue;
@@ -1185,7 +1186,7 @@ export class TaskMasterApi {
}
if (!Array.isArray(subtasks)) {
console.warn(
logger.warn(
`TaskMasterApi: Subtasks for task ${parentTaskId} is not an array:`,
subtasks
);
@@ -1197,7 +1198,7 @@ export class TaskMasterApi {
try {
const subtask = subtasks[i];
if (!subtask || typeof subtask !== 'object') {
console.warn(
logger.warn(
`TaskMasterApi: Invalid subtask at index ${i} for task ${parentTaskId}:`,
subtask
);
@@ -1233,7 +1234,7 @@ export class TaskMasterApi {
validSubtasks.push(transformedSubtask);
} catch (error) {
console.error(
logger.error(
`TaskMasterApi: Error transforming subtask at index ${i} for task ${parentTaskId}:`,
error
);
@@ -1285,7 +1286,7 @@ export class TaskMasterApi {
const result = statusMap[normalized] || 'pending';
if (original && original !== result) {
console.log(
logger.log(
`TaskMasterApi: Normalized status '${original}' -> '${result}'`
);
}
@@ -1330,7 +1331,7 @@ export class TaskMasterApi {
}
if (original && original !== result) {
console.log(
logger.log(
`TaskMasterApi: Normalized priority '${original}' -> '${result}'`
);
}

View File

@@ -648,14 +648,21 @@ const TaskEditModal: React.FC<{
// Only include changed fields
const updates: TaskUpdates = {};
if (formData.title !== task.title) {updates.title = formData.title;}
if (formData.description !== task.description)
{updates.description = formData.description;}
if (formData.details !== task.details) {updates.details = formData.details;}
if (formData.priority !== task.priority)
{updates.priority = formData.priority;}
if (formData.testStrategy !== task.testStrategy)
{updates.testStrategy = formData.testStrategy;}
if (formData.title !== task.title) {
updates.title = formData.title;
}
if (formData.description !== task.description) {
updates.description = formData.description;
}
if (formData.details !== task.details) {
updates.details = formData.details;
}
if (formData.priority !== task.priority) {
updates.priority = formData.priority;
}
if (formData.testStrategy !== task.testStrategy) {
updates.testStrategy = formData.testStrategy;
}
if (
JSON.stringify(formData.dependencies) !==
JSON.stringify(task.dependencies)
@@ -874,8 +881,9 @@ const TaskCard: React.FC<{
// Main Kanban Board Component
const TaskMasterKanban: React.FC = () => {
const context = useContext(VSCodeContext);
if (!context)
{throw new Error('TaskMasterKanban must be used within VSCodeContext');}
if (!context) {
throw new Error('TaskMasterKanban must be used within VSCodeContext');
}
const { state, dispatch, sendMessage, availableHeight } = context;
const {
@@ -984,14 +992,18 @@ const TaskMasterKanban: React.FC = () => {
dispatch({ type: 'SET_USER_INTERACTING', payload: false });
}, 1000); // 1 second delay to ensure smooth completion
if (!over) {return;}
if (!over) {
return;
}
const taskId = active.id as string;
const newStatus = over.id as TaskMasterTask['status'];
// Find the task that was moved
const task = tasks.find((t) => t.id === taskId);
if (!task || task.status === newStatus) {return;}
if (!task || task.status === newStatus) {
return;
}
console.log(`🔄 Moving task ${taskId} from ${task.status} to ${newStatus}`);
@@ -1382,7 +1394,9 @@ const App: React.FC = () => {
// Handle messages from extension
useEffect(() => {
if (!vscode) {return;}
if (!vscode) {
return;
}
const handleMessage = (event: MessageEvent) => {
const message: WebviewMessage = event.data;
@@ -1526,13 +1540,16 @@ const App: React.FC = () => {
// Map error severity to toast type
let toastType: ToastNotification['type'] = 'error';
if (errorData.severity === 'low') {toastType = 'info';}
else if (errorData.severity === 'medium') {toastType = 'warning';}
else if (
if (errorData.severity === 'low') {
toastType = 'info';
} else if (errorData.severity === 'medium') {
toastType = 'warning';
} else if (
errorData.severity === 'high' ||
errorData.severity === 'critical'
)
{toastType = 'error';}
) {
toastType = 'error';
}
// Create appropriate toast based on error category
const title =

7885
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -9,10 +9,7 @@
"task-master-mcp": "mcp-server/server.js",
"task-master-ai": "mcp-server/server.js"
},
"workspaces": [
"apps/*",
"."
],
"workspaces": ["apps/*", "."],
"scripts": {
"test": "node --experimental-vm-modules node_modules/.bin/jest",
"test:fails": "node --experimental-vm-modules node_modules/.bin/jest --onlyFailures",
@@ -126,7 +123,8 @@
"jest-environment-node": "^29.7.0",
"mock-fs": "^5.5.0",
"prettier": "^3.5.3",
"react": "^18.3.1",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"supertest": "^7.1.0",
"tsx": "^4.16.2"
}

View File

@@ -332,9 +332,9 @@
"input": 1.0,
"output": 3.0
},
"allowed_roles": ["main", "fallback"],
"max_tokens": 16384,
"supported": true
"allowed_roles": ["main", "fallback"],
"max_tokens": 16384,
"supported": true
},
{
"id": "llama-3.3-70b-versatile",

View File

@@ -1,52 +1,47 @@
#!/usr/bin/env node
import assert from 'node:assert/strict'
import { spawnSync } from 'node:child_process'
import { readFileSync } from 'node:fs'
import { join, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
import assert from 'node:assert/strict';
import { spawnSync } from 'node:child_process';
import { readFileSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Read the extension's publishing package.json for accurate version/name
const extensionDir = join(__dirname, '..', 'apps', 'extension')
const publishPkgPath = join(extensionDir, 'package.publish.json')
const extensionDir = join(__dirname, '..', 'apps', 'extension');
const publishPkgPath = join(extensionDir, 'package.publish.json');
let pkg
let pkg;
try {
const pkgContent = readFileSync(publishPkgPath, 'utf8')
pkg = JSON.parse(pkgContent)
const pkgContent = readFileSync(publishPkgPath, 'utf8');
pkg = JSON.parse(pkgContent);
} catch (error) {
console.error('Failed to read package.publish.json:', error.message)
process.exit(1)
console.error('Failed to read package.publish.json:', error.message);
process.exit(1);
}
// Ensure we have required fields
assert(pkg.name, 'package.publish.json must have a name field')
assert(pkg.version, 'package.publish.json must have a version field')
assert(pkg.repository, 'package.publish.json must have a repository field')
assert(pkg.name, 'package.publish.json must have a name field');
assert(pkg.version, 'package.publish.json must have a version field');
assert(pkg.repository, 'package.publish.json must have a repository field');
const tag = `${pkg.name}@${pkg.version}`
const tag = `${pkg.name}@${pkg.version}`;
// Get repository URL - handle both string and object format
const repoUrl = typeof pkg.repository === 'string'
? pkg.repository
: pkg.repository.url
const repoUrl =
typeof pkg.repository === 'string' ? pkg.repository : pkg.repository.url;
assert(repoUrl, 'Repository URL not found in package.publish.json')
assert(repoUrl, 'Repository URL not found in package.publish.json');
const { status, stdout, error } = spawnSync('git', [
'ls-remote',
repoUrl,
tag
])
const { status, stdout, error } = spawnSync('git', ['ls-remote', repoUrl, tag]);
assert.equal(status, 0, error)
assert.equal(status, 0, error);
const exists = String(stdout).trim() !== ''
const exists = String(stdout).trim() !== '';
if (!exists) {
console.log(`\nNew extension tag: ${tag}`)
console.log(`\nNew extension tag: ${tag}`);
} else {
console.log(`\nExtension tag already exists: ${tag}`)
}
console.log(`\nExtension tag already exists: ${tag}`);
}