* fix: fix mcp tool call in extension - fix console.log directly being used in scope-adjutment.js breaking mcp * chore: run format and fix tests * chore: format
344 lines
8.6 KiB
TypeScript
344 lines
8.6 KiB
TypeScript
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
import { useVSCodeContext } from '../contexts/VSCodeContext';
|
|
import type { TaskMasterTask, TaskUpdates } from '../types';
|
|
|
|
// Query keys factory
|
|
export const taskKeys = {
|
|
all: ['tasks'] as const,
|
|
lists: () => [...taskKeys.all, 'list'] as const,
|
|
list: (filters: { tag?: string; status?: string }) =>
|
|
[...taskKeys.lists(), filters] as const,
|
|
details: () => [...taskKeys.all, 'detail'] as const,
|
|
detail: (id: string) => [...taskKeys.details(), id] as const
|
|
};
|
|
|
|
// Hook to fetch all tasks
|
|
export function useTasks(options?: { tag?: string; status?: string }) {
|
|
const { sendMessage } = useVSCodeContext();
|
|
|
|
return useQuery({
|
|
queryKey: taskKeys.list(options || {}),
|
|
queryFn: async () => {
|
|
console.log('🔍 Fetching tasks with options:', options);
|
|
const response = await sendMessage({
|
|
type: 'getTasks',
|
|
data: {
|
|
tag: options?.tag,
|
|
withSubtasks: true
|
|
}
|
|
});
|
|
console.log('📋 Tasks fetched:', response);
|
|
return response as TaskMasterTask[];
|
|
},
|
|
staleTime: 0 // Consider data stale immediately
|
|
});
|
|
}
|
|
|
|
// Hook to fetch a single task with full details
|
|
export function useTaskDetails(taskId: string) {
|
|
const { sendMessage } = useVSCodeContext();
|
|
|
|
return useQuery({
|
|
queryKey: taskKeys.detail(taskId),
|
|
queryFn: async () => {
|
|
const response = await sendMessage({
|
|
type: 'mcpRequest',
|
|
tool: 'get_task',
|
|
params: {
|
|
id: taskId
|
|
}
|
|
});
|
|
|
|
// Parse the MCP response
|
|
let fullTaskData = null;
|
|
if (response?.data?.content?.[0]?.text) {
|
|
try {
|
|
const parsed = JSON.parse(response.data.content[0].text);
|
|
fullTaskData = parsed.data;
|
|
} catch (e) {
|
|
console.error('Failed to parse MCP response:', e);
|
|
}
|
|
} else if (response?.data?.data) {
|
|
fullTaskData = response.data.data;
|
|
}
|
|
|
|
return fullTaskData as TaskMasterTask;
|
|
},
|
|
enabled: !!taskId
|
|
});
|
|
}
|
|
|
|
// Hook to update task status
|
|
export function useUpdateTaskStatus() {
|
|
const { sendMessage } = useVSCodeContext();
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async ({
|
|
taskId,
|
|
newStatus
|
|
}: {
|
|
taskId: string;
|
|
newStatus: TaskMasterTask['status'];
|
|
}) => {
|
|
const response = await sendMessage({
|
|
type: 'updateTaskStatus',
|
|
data: { taskId, newStatus }
|
|
});
|
|
return { taskId, newStatus, response };
|
|
},
|
|
// Optimistic update to prevent snap-back
|
|
onMutate: async ({ taskId, newStatus }) => {
|
|
// Cancel any outgoing refetches
|
|
await queryClient.cancelQueries({ queryKey: taskKeys.all });
|
|
|
|
// Snapshot the previous value
|
|
const previousTasks = queryClient.getQueriesData({
|
|
queryKey: taskKeys.all
|
|
});
|
|
|
|
// Optimistically update all task queries
|
|
queryClient.setQueriesData({ queryKey: taskKeys.all }, (old: any) => {
|
|
if (!old) return old;
|
|
|
|
// Handle both array and object responses
|
|
if (Array.isArray(old)) {
|
|
return old.map((task: TaskMasterTask) =>
|
|
task.id === taskId ? { ...task, status: newStatus } : task
|
|
);
|
|
}
|
|
|
|
return old;
|
|
});
|
|
|
|
// Return a context object with the snapshot
|
|
return { previousTasks };
|
|
},
|
|
// If the mutation fails, roll back to the previous value
|
|
onError: (err, variables, context) => {
|
|
if (context?.previousTasks) {
|
|
context.previousTasks.forEach(([queryKey, data]) => {
|
|
queryClient.setQueryData(queryKey, data);
|
|
});
|
|
}
|
|
},
|
|
// Always refetch after error or success to ensure consistency
|
|
onSettled: () => {
|
|
queryClient.invalidateQueries({ queryKey: taskKeys.all });
|
|
}
|
|
});
|
|
}
|
|
|
|
// Hook to update task content
|
|
export function useUpdateTask() {
|
|
const { sendMessage } = useVSCodeContext();
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async ({
|
|
taskId,
|
|
updates,
|
|
options = {}
|
|
}: {
|
|
taskId: string;
|
|
updates: TaskUpdates | { description: string };
|
|
options?: { append?: boolean; research?: boolean };
|
|
}) => {
|
|
console.log('🔄 Updating task:', taskId, updates, options);
|
|
|
|
const response = await sendMessage({
|
|
type: 'updateTask',
|
|
data: { taskId, updates, options }
|
|
});
|
|
|
|
console.log('📥 Update task response:', response);
|
|
|
|
// Check for error in response
|
|
if (response && typeof response === 'object' && 'error' in response) {
|
|
throw new Error(response.error || 'Failed to update task');
|
|
}
|
|
|
|
return response;
|
|
},
|
|
onSuccess: async (data, variables) => {
|
|
console.log('✅ Task update successful, invalidating all task queries');
|
|
console.log('Response data:', data);
|
|
console.log('Task ID:', variables.taskId);
|
|
|
|
// Invalidate ALL task-related queries (same as handleRefresh)
|
|
await queryClient.invalidateQueries({
|
|
queryKey: taskKeys.all
|
|
});
|
|
|
|
console.log(
|
|
'🔄 All task queries invalidated for task:',
|
|
variables.taskId
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Hook to update subtask
|
|
export function useUpdateSubtask() {
|
|
const { sendMessage } = useVSCodeContext();
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async ({
|
|
taskId,
|
|
prompt,
|
|
options = {}
|
|
}: {
|
|
taskId: string;
|
|
prompt: string;
|
|
options?: { research?: boolean };
|
|
}) => {
|
|
console.log('🔄 Updating subtask:', taskId, prompt, options);
|
|
|
|
const response = await sendMessage({
|
|
type: 'updateSubtask',
|
|
data: { taskId, prompt, options }
|
|
});
|
|
|
|
console.log('📥 Update subtask response:', response);
|
|
|
|
// Check for error in response
|
|
if (response && typeof response === 'object' && 'error' in response) {
|
|
throw new Error(response.error || 'Failed to update subtask');
|
|
}
|
|
|
|
return response;
|
|
},
|
|
onSuccess: async (data, variables) => {
|
|
console.log(
|
|
'✅ Subtask update successful, invalidating all task queries'
|
|
);
|
|
console.log('Subtask ID:', variables.taskId);
|
|
|
|
// Invalidate ALL task-related queries (same as handleRefresh)
|
|
await queryClient.invalidateQueries({
|
|
queryKey: taskKeys.all
|
|
});
|
|
|
|
console.log(
|
|
'🔄 All task queries invalidated for subtask:',
|
|
variables.taskId
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Hook to scope up task complexity
|
|
export function useScopeUpTask() {
|
|
const { sendMessage } = useVSCodeContext();
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async ({
|
|
taskId,
|
|
strength = 'regular',
|
|
prompt,
|
|
options = {}
|
|
}: {
|
|
taskId: string;
|
|
strength?: 'light' | 'regular' | 'heavy';
|
|
prompt?: string;
|
|
options?: { research?: boolean };
|
|
}) => {
|
|
console.log('🔄 Scoping up task:', taskId, strength, prompt, options);
|
|
|
|
const response = await sendMessage({
|
|
type: 'mcpRequest',
|
|
tool: 'scope_up_task',
|
|
params: {
|
|
id: String(taskId),
|
|
strength,
|
|
prompt,
|
|
research: options.research || false
|
|
}
|
|
});
|
|
|
|
console.log('📥 Scope up task response:', response);
|
|
|
|
// Check for error in response
|
|
if (response && typeof response === 'object' && 'error' in response) {
|
|
throw new Error(response.error || 'Failed to scope up task');
|
|
}
|
|
|
|
return response;
|
|
},
|
|
onSuccess: async (data, variables) => {
|
|
console.log('✅ Task scope up successful, invalidating all task queries');
|
|
console.log('Task ID:', variables.taskId);
|
|
|
|
// Invalidate ALL task-related queries
|
|
await queryClient.invalidateQueries({
|
|
queryKey: taskKeys.all
|
|
});
|
|
|
|
console.log(
|
|
'🔄 All task queries invalidated for scoped up task:',
|
|
variables.taskId
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Hook to scope down task complexity
|
|
export function useScopeDownTask() {
|
|
const { sendMessage } = useVSCodeContext();
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async ({
|
|
taskId,
|
|
strength = 'regular',
|
|
prompt,
|
|
options = {}
|
|
}: {
|
|
taskId: string;
|
|
strength?: 'light' | 'regular' | 'heavy';
|
|
prompt?: string;
|
|
options?: { research?: boolean };
|
|
}) => {
|
|
console.log('🔄 Scoping down task:', taskId, strength, prompt, options);
|
|
|
|
const response = await sendMessage({
|
|
type: 'mcpRequest',
|
|
tool: 'scope_down_task',
|
|
params: {
|
|
id: String(taskId),
|
|
strength,
|
|
prompt,
|
|
research: options.research || false
|
|
}
|
|
});
|
|
|
|
console.log('📥 Scope down task response:', response);
|
|
|
|
// Check for error in response
|
|
if (response && typeof response === 'object' && 'error' in response) {
|
|
throw new Error(response.error || 'Failed to scope down task');
|
|
}
|
|
|
|
return response;
|
|
},
|
|
onSuccess: async (data, variables) => {
|
|
console.log(
|
|
'✅ Task scope down successful, invalidating all task queries'
|
|
);
|
|
console.log('Task ID:', variables.taskId);
|
|
|
|
// Invalidate ALL task-related queries
|
|
await queryClient.invalidateQueries({
|
|
queryKey: taskKeys.all
|
|
});
|
|
|
|
console.log(
|
|
'🔄 All task queries invalidated for scoped down task:',
|
|
variables.taskId
|
|
);
|
|
}
|
|
});
|
|
}
|