feat: Implement worktree initialization script functionality

This commit introduces a new feature for managing worktree initialization scripts, allowing users to configure and execute scripts upon worktree creation. Key changes include:

1. **New API Endpoints**: Added endpoints for getting, setting, and deleting init scripts.
2. **Worktree Routes**: Updated worktree routes to include init script handling.
3. **Init Script Service**: Created a service to execute the init scripts asynchronously, with support for cross-platform compatibility.
4. **UI Components**: Added UI components for displaying and editing init scripts, including a dedicated section in the settings view.
5. **Event Handling**: Implemented event handling for init script execution status, providing real-time feedback in the UI.

This enhancement improves the user experience by allowing automated setup processes for new worktrees, streamlining project workflows.
This commit is contained in:
Kacper
2026-01-10 22:19:34 +01:00
parent 427832e72e
commit 05d96a7d6e
23 changed files with 1481 additions and 46 deletions

View File

@@ -0,0 +1,79 @@
import { useEffect } from 'react';
import { useAppStore } from '@/store/app-store';
import { getHttpApiClient } from '@/lib/http-api-client';
import { pathsEqual } from '@/lib/utils';
interface InitScriptStartedPayload {
projectPath: string;
worktreePath: string;
branch: string;
}
interface InitScriptOutputPayload {
projectPath: string;
branch: string;
type: 'stdout' | 'stderr';
content: string;
}
interface InitScriptCompletedPayload {
projectPath: string;
worktreePath: string;
branch: string;
success: boolean;
exitCode?: number;
error?: string;
}
/**
* Hook to subscribe to init script WebSocket events and update the store.
* Should be used in a component that's always mounted (e.g., board-view).
*/
export function useInitScriptEvents(projectPath: string | null) {
const setInitScriptState = useAppStore((s) => s.setInitScriptState);
const appendInitScriptOutput = useAppStore((s) => s.appendInitScriptOutput);
useEffect(() => {
if (!projectPath) return;
const api = getHttpApiClient();
const unsubscribe = api.worktree.onInitScriptEvent((event) => {
const payload = event.payload as
| InitScriptStartedPayload
| InitScriptOutputPayload
| InitScriptCompletedPayload;
// Only handle events for the current project (use pathsEqual for cross-platform path comparison)
if (!pathsEqual(payload.projectPath, projectPath)) return;
switch (event.type) {
case 'worktree:init-started': {
const startPayload = payload as InitScriptStartedPayload;
setInitScriptState(projectPath, {
status: 'running',
branch: startPayload.branch,
output: [],
error: undefined,
});
break;
}
case 'worktree:init-output': {
const outputPayload = payload as InitScriptOutputPayload;
appendInitScriptOutput(projectPath, outputPayload.content);
break;
}
case 'worktree:init-completed': {
const completePayload = payload as InitScriptCompletedPayload;
setInitScriptState(projectPath, {
status: completePayload.success ? 'success' : 'failed',
error: completePayload.error,
});
break;
}
}
});
return unsubscribe;
}, [projectPath, setInitScriptState, appendInitScriptOutput]);
}