mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-02 20:43:36 +00:00
feat(ui): add React Query mutation hooks
- Add feature mutations (create, update, delete with optimistic updates) - Add auto-mode mutations (start, stop, approve plan) - Add worktree mutations (create, delete, checkout, switch branch) - Add settings mutations (update global/project, validate API keys) - Add GitHub mutations (create PR, validate PR) - Add cursor permissions mutations (apply profile, copy config) - Add spec mutations (generate, update, save) - Add pipeline mutations (toggle, update config) - Add session mutations with cache invalidation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
373
apps/ui/src/hooks/mutations/use-auto-mode-mutations.ts
Normal file
373
apps/ui/src/hooks/mutations/use-auto-mode-mutations.ts
Normal file
@@ -0,0 +1,373 @@
|
||||
/**
|
||||
* Auto Mode Mutations
|
||||
*
|
||||
* React Query mutations for auto mode operations like running features,
|
||||
* stopping features, and plan approval.
|
||||
*/
|
||||
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
import { queryKeys } from '@/lib/query-keys';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
/**
|
||||
* Start running a feature in auto mode
|
||||
*
|
||||
* @param projectPath - Path to the project
|
||||
* @returns Mutation for starting a feature
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* const startFeature = useStartFeature(projectPath);
|
||||
* startFeature.mutate({ featureId: 'abc123', useWorktrees: true });
|
||||
* ```
|
||||
*/
|
||||
export function useStartFeature(projectPath: string) {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async ({
|
||||
featureId,
|
||||
useWorktrees,
|
||||
worktreePath,
|
||||
}: {
|
||||
featureId: string;
|
||||
useWorktrees?: boolean;
|
||||
worktreePath?: string;
|
||||
}) => {
|
||||
const api = getElectronAPI();
|
||||
const result = await api.autoMode.runFeature(
|
||||
projectPath,
|
||||
featureId,
|
||||
useWorktrees,
|
||||
worktreePath
|
||||
);
|
||||
if (!result.success) {
|
||||
throw new Error(result.error || 'Failed to start feature');
|
||||
}
|
||||
return result;
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.runningAgents.all() });
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.features.all(projectPath) });
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
toast.error('Failed to start feature', {
|
||||
description: error.message,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Resume a paused or interrupted feature
|
||||
*
|
||||
* @param projectPath - Path to the project
|
||||
* @returns Mutation for resuming a feature
|
||||
*/
|
||||
export function useResumeFeature(projectPath: string) {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async ({
|
||||
featureId,
|
||||
useWorktrees,
|
||||
}: {
|
||||
featureId: string;
|
||||
useWorktrees?: boolean;
|
||||
}) => {
|
||||
const api = getElectronAPI();
|
||||
const result = await api.autoMode.resumeFeature(projectPath, featureId, useWorktrees);
|
||||
if (!result.success) {
|
||||
throw new Error(result.error || 'Failed to resume feature');
|
||||
}
|
||||
return result;
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.runningAgents.all() });
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.features.all(projectPath) });
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
toast.error('Failed to resume feature', {
|
||||
description: error.message,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop a running feature
|
||||
*
|
||||
* @returns Mutation for stopping a feature
|
||||
*/
|
||||
export function useStopFeature() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (featureId: string) => {
|
||||
const api = getElectronAPI();
|
||||
const result = await api.autoMode.stopFeature(featureId);
|
||||
if (!result.success) {
|
||||
throw new Error(result.error || 'Failed to stop feature');
|
||||
}
|
||||
return result;
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.runningAgents.all() });
|
||||
toast.success('Feature stopped');
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
toast.error('Failed to stop feature', {
|
||||
description: error.message,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a completed feature
|
||||
*
|
||||
* @param projectPath - Path to the project
|
||||
* @returns Mutation for verifying a feature
|
||||
*/
|
||||
export function useVerifyFeature(projectPath: string) {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (featureId: string) => {
|
||||
const api = getElectronAPI();
|
||||
const result = await api.autoMode.verifyFeature(projectPath, featureId);
|
||||
if (!result.success) {
|
||||
throw new Error(result.error || 'Failed to verify feature');
|
||||
}
|
||||
return result;
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.features.all(projectPath) });
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
toast.error('Failed to verify feature', {
|
||||
description: error.message,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Approve or reject a plan
|
||||
*
|
||||
* @param projectPath - Path to the project
|
||||
* @returns Mutation for plan approval
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* const approvePlan = useApprovePlan(projectPath);
|
||||
* approvePlan.mutate({ featureId: 'abc', approved: true });
|
||||
* ```
|
||||
*/
|
||||
export function useApprovePlan(projectPath: string) {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async ({
|
||||
featureId,
|
||||
approved,
|
||||
editedPlan,
|
||||
feedback,
|
||||
}: {
|
||||
featureId: string;
|
||||
approved: boolean;
|
||||
editedPlan?: string;
|
||||
feedback?: string;
|
||||
}) => {
|
||||
const api = getElectronAPI();
|
||||
const result = await api.autoMode.approvePlan(
|
||||
projectPath,
|
||||
featureId,
|
||||
approved,
|
||||
editedPlan,
|
||||
feedback
|
||||
);
|
||||
if (!result.success) {
|
||||
throw new Error(result.error || 'Failed to submit plan decision');
|
||||
}
|
||||
return result;
|
||||
},
|
||||
onSuccess: (_, { approved }) => {
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.features.all(projectPath) });
|
||||
if (approved) {
|
||||
toast.success('Plan approved');
|
||||
} else {
|
||||
toast.info('Plan rejected');
|
||||
}
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
toast.error('Failed to submit plan decision', {
|
||||
description: error.message,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a follow-up prompt to a feature
|
||||
*
|
||||
* @param projectPath - Path to the project
|
||||
* @returns Mutation for sending follow-up
|
||||
*/
|
||||
export function useFollowUpFeature(projectPath: string) {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async ({
|
||||
featureId,
|
||||
prompt,
|
||||
imagePaths,
|
||||
useWorktrees,
|
||||
}: {
|
||||
featureId: string;
|
||||
prompt: string;
|
||||
imagePaths?: string[];
|
||||
useWorktrees?: boolean;
|
||||
}) => {
|
||||
const api = getElectronAPI();
|
||||
const result = await api.autoMode.followUpFeature(
|
||||
projectPath,
|
||||
featureId,
|
||||
prompt,
|
||||
imagePaths,
|
||||
useWorktrees
|
||||
);
|
||||
if (!result.success) {
|
||||
throw new Error(result.error || 'Failed to send follow-up');
|
||||
}
|
||||
return result;
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.runningAgents.all() });
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.features.all(projectPath) });
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
toast.error('Failed to send follow-up', {
|
||||
description: error.message,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit feature changes
|
||||
*
|
||||
* @param projectPath - Path to the project
|
||||
* @returns Mutation for committing feature
|
||||
*/
|
||||
export function useCommitFeature(projectPath: string) {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (featureId: string) => {
|
||||
const api = getElectronAPI();
|
||||
const result = await api.autoMode.commitFeature(projectPath, featureId);
|
||||
if (!result.success) {
|
||||
throw new Error(result.error || 'Failed to commit changes');
|
||||
}
|
||||
return result;
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.features.all(projectPath) });
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.worktrees.all(projectPath) });
|
||||
toast.success('Changes committed');
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
toast.error('Failed to commit changes', {
|
||||
description: error.message,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze project structure
|
||||
*
|
||||
* @returns Mutation for project analysis
|
||||
*/
|
||||
export function useAnalyzeProject() {
|
||||
return useMutation({
|
||||
mutationFn: async (projectPath: string) => {
|
||||
const api = getElectronAPI();
|
||||
const result = await api.autoMode.analyzeProject(projectPath);
|
||||
if (!result.success) {
|
||||
throw new Error(result.error || 'Failed to analyze project');
|
||||
}
|
||||
return result;
|
||||
},
|
||||
onSuccess: () => {
|
||||
toast.success('Project analysis started');
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
toast.error('Failed to analyze project', {
|
||||
description: error.message,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Start auto mode for all pending features
|
||||
*
|
||||
* @param projectPath - Path to the project
|
||||
* @returns Mutation for starting auto mode
|
||||
*/
|
||||
export function useStartAutoMode(projectPath: string) {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (maxConcurrency?: number) => {
|
||||
const api = getElectronAPI();
|
||||
const result = await api.autoMode.start(projectPath, maxConcurrency);
|
||||
if (!result.success) {
|
||||
throw new Error(result.error || 'Failed to start auto mode');
|
||||
}
|
||||
return result;
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.runningAgents.all() });
|
||||
toast.success('Auto mode started');
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
toast.error('Failed to start auto mode', {
|
||||
description: error.message,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop auto mode for all features
|
||||
*
|
||||
* @param projectPath - Path to the project
|
||||
* @returns Mutation for stopping auto mode
|
||||
*/
|
||||
export function useStopAutoMode(projectPath: string) {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async () => {
|
||||
const api = getElectronAPI();
|
||||
const result = await api.autoMode.stop(projectPath);
|
||||
if (!result.success) {
|
||||
throw new Error(result.error || 'Failed to stop auto mode');
|
||||
}
|
||||
return result;
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.runningAgents.all() });
|
||||
toast.success('Auto mode stopped');
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
toast.error('Failed to stop auto mode', {
|
||||
description: error.message,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user