mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-01 20:23:36 +00:00
feat: implement pipeline feature for automated workflow steps
- Introduced a new pipeline service to manage custom workflow steps that execute after a feature is marked "In Progress". - Added API endpoints for configuring, saving, adding, updating, deleting, and reordering pipeline steps. - Enhanced the UI to support pipeline settings, including a dialog for managing steps and integration with the Kanban board. - Updated the application state management to handle pipeline configurations per project. - Implemented dynamic column generation in the Kanban board to display pipeline steps between "In Progress" and "Waiting Approval". - Added documentation for the new pipeline feature, including usage instructions and configuration details. This feature allows for a more structured workflow, enabling automated processes such as code reviews and testing after feature implementation.
This commit is contained in:
@@ -7,6 +7,9 @@ import type {
|
||||
AgentModel,
|
||||
PlanningMode,
|
||||
AIProfile,
|
||||
FeatureStatusWithPipeline,
|
||||
PipelineConfig,
|
||||
PipelineStep,
|
||||
} from '@automaker/types';
|
||||
|
||||
// Re-export ThemeMode for convenience
|
||||
@@ -263,7 +266,7 @@ export interface Feature extends Omit<
|
||||
category: string;
|
||||
description: string;
|
||||
steps: string[]; // Required in UI (not optional)
|
||||
status: 'backlog' | 'in_progress' | 'waiting_approval' | 'verified' | 'completed';
|
||||
status: FeatureStatusWithPipeline;
|
||||
images?: FeatureImage[]; // UI-specific base64 images
|
||||
imagePaths?: FeatureImagePath[]; // Stricter type than base (no string | union)
|
||||
textFilePaths?: FeatureTextFilePath[]; // Text file attachments for context
|
||||
@@ -534,6 +537,9 @@ export interface AppState {
|
||||
claudeRefreshInterval: number; // Refresh interval in seconds (default: 60)
|
||||
claudeUsage: ClaudeUsage | null;
|
||||
claudeUsageLastUpdated: number | null;
|
||||
|
||||
// Pipeline Configuration (per-project, keyed by project path)
|
||||
pipelineConfigByProject: Record<string, PipelineConfig>;
|
||||
}
|
||||
|
||||
// Claude Usage interface matching the server response
|
||||
@@ -857,6 +863,21 @@ export interface AppActions {
|
||||
} | null
|
||||
) => void;
|
||||
|
||||
// Pipeline actions
|
||||
setPipelineConfig: (projectPath: string, config: PipelineConfig) => void;
|
||||
getPipelineConfig: (projectPath: string) => PipelineConfig | null;
|
||||
addPipelineStep: (
|
||||
projectPath: string,
|
||||
step: Omit<PipelineStep, 'id' | 'createdAt' | 'updatedAt'>
|
||||
) => PipelineStep;
|
||||
updatePipelineStep: (
|
||||
projectPath: string,
|
||||
stepId: string,
|
||||
updates: Partial<Omit<PipelineStep, 'id' | 'createdAt'>>
|
||||
) => void;
|
||||
deletePipelineStep: (projectPath: string, stepId: string) => void;
|
||||
reorderPipelineSteps: (projectPath: string, stepIds: string[]) => void;
|
||||
|
||||
// Reset
|
||||
reset: () => void;
|
||||
}
|
||||
@@ -961,6 +982,10 @@ const initialState: AppState = {
|
||||
defaultRequirePlanApproval: false,
|
||||
defaultAIProfileId: null,
|
||||
pendingPlanApproval: null,
|
||||
claudeRefreshInterval: 60,
|
||||
claudeUsage: null,
|
||||
claudeUsageLastUpdated: null,
|
||||
pipelineConfigByProject: {},
|
||||
};
|
||||
|
||||
export const useAppStore = create<AppState & AppActions>()(
|
||||
@@ -2597,6 +2622,105 @@ export const useAppStore = create<AppState & AppActions>()(
|
||||
claudeUsageLastUpdated: usage ? Date.now() : null,
|
||||
}),
|
||||
|
||||
// Pipeline actions
|
||||
setPipelineConfig: (projectPath, config) => {
|
||||
set({
|
||||
pipelineConfigByProject: {
|
||||
...get().pipelineConfigByProject,
|
||||
[projectPath]: config,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
getPipelineConfig: (projectPath) => {
|
||||
return get().pipelineConfigByProject[projectPath] || null;
|
||||
},
|
||||
|
||||
addPipelineStep: (projectPath, step) => {
|
||||
const config = get().pipelineConfigByProject[projectPath] || { version: 1, steps: [] };
|
||||
const now = new Date().toISOString();
|
||||
const newStep: PipelineStep = {
|
||||
...step,
|
||||
id: `step_${Date.now().toString(36)}_${Math.random().toString(36).substring(2, 8)}`,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
|
||||
const newSteps = [...config.steps, newStep].sort((a, b) => a.order - b.order);
|
||||
newSteps.forEach((s, index) => {
|
||||
s.order = index;
|
||||
});
|
||||
|
||||
set({
|
||||
pipelineConfigByProject: {
|
||||
...get().pipelineConfigByProject,
|
||||
[projectPath]: { ...config, steps: newSteps },
|
||||
},
|
||||
});
|
||||
|
||||
return newStep;
|
||||
},
|
||||
|
||||
updatePipelineStep: (projectPath, stepId, updates) => {
|
||||
const config = get().pipelineConfigByProject[projectPath];
|
||||
if (!config) return;
|
||||
|
||||
const stepIndex = config.steps.findIndex((s) => s.id === stepId);
|
||||
if (stepIndex === -1) return;
|
||||
|
||||
const updatedSteps = [...config.steps];
|
||||
updatedSteps[stepIndex] = {
|
||||
...updatedSteps[stepIndex],
|
||||
...updates,
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
set({
|
||||
pipelineConfigByProject: {
|
||||
...get().pipelineConfigByProject,
|
||||
[projectPath]: { ...config, steps: updatedSteps },
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
deletePipelineStep: (projectPath, stepId) => {
|
||||
const config = get().pipelineConfigByProject[projectPath];
|
||||
if (!config) return;
|
||||
|
||||
const newSteps = config.steps.filter((s) => s.id !== stepId);
|
||||
newSteps.forEach((s, index) => {
|
||||
s.order = index;
|
||||
});
|
||||
|
||||
set({
|
||||
pipelineConfigByProject: {
|
||||
...get().pipelineConfigByProject,
|
||||
[projectPath]: { ...config, steps: newSteps },
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
reorderPipelineSteps: (projectPath, stepIds) => {
|
||||
const config = get().pipelineConfigByProject[projectPath];
|
||||
if (!config) return;
|
||||
|
||||
const stepMap = new Map(config.steps.map((s) => [s.id, s]));
|
||||
const reorderedSteps = stepIds
|
||||
.map((id, index) => {
|
||||
const step = stepMap.get(id);
|
||||
if (!step) return null;
|
||||
return { ...step, order: index, updatedAt: new Date().toISOString() };
|
||||
})
|
||||
.filter((s): s is PipelineStep => s !== null);
|
||||
|
||||
set({
|
||||
pipelineConfigByProject: {
|
||||
...get().pipelineConfigByProject,
|
||||
[projectPath]: { ...config, steps: reorderedSteps },
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
// Reset
|
||||
reset: () => set(initialState),
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user