mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-31 06:42:03 +00:00
- Update query keys to include all relevant parameters (branches, agents) - Fix use-branches to pass includeRemote parameter to query key - Fix use-settings to include sources in agents query key - Update running-agents-view to use correct query key structure - Update use-spec-loading to properly use spec query hooks - Add missing queryClient invalidation in auto-mode mutations - Add missing cache invalidation in spec mutations after creation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
185 lines
4.7 KiB
TypeScript
185 lines
4.7 KiB
TypeScript
/**
|
|
* Spec Mutation Hooks
|
|
*
|
|
* React Query mutations for spec operations like creating, regenerating, and saving.
|
|
*/
|
|
|
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
import { getElectronAPI } from '@/lib/electron';
|
|
import { queryKeys } from '@/lib/query-keys';
|
|
import { toast } from 'sonner';
|
|
import type { FeatureCount } from '@/components/views/spec-view/types';
|
|
|
|
/**
|
|
* Input for creating a spec
|
|
*/
|
|
interface CreateSpecInput {
|
|
projectOverview: string;
|
|
generateFeatures: boolean;
|
|
analyzeProject: boolean;
|
|
featureCount?: FeatureCount;
|
|
}
|
|
|
|
/**
|
|
* Input for regenerating a spec
|
|
*/
|
|
interface RegenerateSpecInput {
|
|
projectDefinition: string;
|
|
generateFeatures: boolean;
|
|
analyzeProject: boolean;
|
|
featureCount?: FeatureCount;
|
|
}
|
|
|
|
/**
|
|
* Create a new spec for a project
|
|
*
|
|
* This mutation triggers an async spec creation process. Progress and completion
|
|
* are delivered via WebSocket events (spec_regeneration_progress, spec_regeneration_complete).
|
|
*
|
|
* @param projectPath - Path to the project
|
|
* @returns Mutation for creating specs
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* const createMutation = useCreateSpec(projectPath);
|
|
*
|
|
* createMutation.mutate({
|
|
* projectOverview: 'A todo app with...',
|
|
* generateFeatures: true,
|
|
* analyzeProject: true,
|
|
* featureCount: 50,
|
|
* });
|
|
* ```
|
|
*/
|
|
export function useCreateSpec(projectPath: string) {
|
|
return useMutation({
|
|
mutationFn: async (input: CreateSpecInput) => {
|
|
const { projectOverview, generateFeatures, analyzeProject, featureCount } = input;
|
|
|
|
const api = getElectronAPI();
|
|
if (!api.specRegeneration) {
|
|
throw new Error('Spec regeneration API not available');
|
|
}
|
|
|
|
const result = await api.specRegeneration.create(
|
|
projectPath,
|
|
projectOverview.trim(),
|
|
generateFeatures,
|
|
analyzeProject,
|
|
generateFeatures ? featureCount : undefined
|
|
);
|
|
|
|
if (!result.success) {
|
|
throw new Error(result.error || 'Failed to start spec creation');
|
|
}
|
|
|
|
return result;
|
|
},
|
|
// Toast/state updates are handled by the component since it tracks WebSocket events
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Regenerate an existing spec
|
|
*
|
|
* @param projectPath - Path to the project
|
|
* @returns Mutation for regenerating specs
|
|
*/
|
|
export function useRegenerateSpec(projectPath: string) {
|
|
return useMutation({
|
|
mutationFn: async (input: RegenerateSpecInput) => {
|
|
const { projectDefinition, generateFeatures, analyzeProject, featureCount } = input;
|
|
|
|
const api = getElectronAPI();
|
|
if (!api.specRegeneration) {
|
|
throw new Error('Spec regeneration API not available');
|
|
}
|
|
|
|
const result = await api.specRegeneration.generate(
|
|
projectPath,
|
|
projectDefinition.trim(),
|
|
generateFeatures,
|
|
analyzeProject,
|
|
generateFeatures ? featureCount : undefined
|
|
);
|
|
|
|
if (!result.success) {
|
|
throw new Error(result.error || 'Failed to start spec regeneration');
|
|
}
|
|
|
|
return result;
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Generate features from existing spec
|
|
*
|
|
* @param projectPath - Path to the project
|
|
* @returns Mutation for generating features
|
|
*/
|
|
export function useGenerateFeatures(projectPath: string) {
|
|
return useMutation({
|
|
mutationFn: async () => {
|
|
const api = getElectronAPI();
|
|
if (!api.specRegeneration) {
|
|
throw new Error('Spec regeneration API not available');
|
|
}
|
|
|
|
const result = await api.specRegeneration.generateFeatures(projectPath);
|
|
|
|
if (!result.success) {
|
|
throw new Error(result.error || 'Failed to start feature generation');
|
|
}
|
|
|
|
return result;
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Save spec file content
|
|
*
|
|
* @param projectPath - Path to the project
|
|
* @returns Mutation for saving spec
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* const saveMutation = useSaveSpec(projectPath);
|
|
*
|
|
* saveMutation.mutate(specContent, {
|
|
* onSuccess: () => setHasChanges(false),
|
|
* });
|
|
* ```
|
|
*/
|
|
export function useSaveSpec(projectPath: string) {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async (content: string) => {
|
|
// Guard against empty projectPath to prevent writing to invalid locations
|
|
if (!projectPath || projectPath.trim() === '') {
|
|
throw new Error('Invalid project path: cannot save spec without a valid project');
|
|
}
|
|
|
|
const api = getElectronAPI();
|
|
|
|
await api.writeFile(`${projectPath}/.automaker/app_spec.txt`, content);
|
|
|
|
return { content };
|
|
},
|
|
onSuccess: () => {
|
|
// Invalidate spec file cache
|
|
queryClient.invalidateQueries({
|
|
queryKey: queryKeys.spec.file(projectPath),
|
|
});
|
|
toast.success('Spec saved');
|
|
},
|
|
onError: (error) => {
|
|
toast.error('Failed to save spec', {
|
|
description: error instanceof Error ? error.message : 'Unknown error',
|
|
});
|
|
},
|
|
});
|
|
}
|