mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-31 20:03:37 +00:00
FINSIHED
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { CheckCircle2, Circle, Loader2, ChevronDown, ChevronRight } from "lucide-react";
|
||||
import { getElectronAPI } from "@/lib/electron";
|
||||
@@ -16,15 +16,65 @@ interface TaskInfo {
|
||||
|
||||
interface TaskProgressPanelProps {
|
||||
featureId: string;
|
||||
projectPath?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function TaskProgressPanel({ featureId, className }: TaskProgressPanelProps) {
|
||||
export function TaskProgressPanel({ featureId, projectPath, className }: TaskProgressPanelProps) {
|
||||
const [tasks, setTasks] = useState<TaskInfo[]>([]);
|
||||
const [isExpanded, setIsExpanded] = useState(true);
|
||||
const [currentTaskId, setCurrentTaskId] = useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
// Listen to task events
|
||||
// Load initial tasks from feature's planSpec
|
||||
const loadInitialTasks = useCallback(async () => {
|
||||
if (!projectPath) {
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (!api?.features) {
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await api.features.get(projectPath, featureId);
|
||||
if (result.success && result.feature?.planSpec?.tasks) {
|
||||
const planTasks = result.feature.planSpec.tasks;
|
||||
const currentId = result.feature.planSpec.currentTaskId;
|
||||
const completedCount = result.feature.planSpec.tasksCompleted || 0;
|
||||
|
||||
// Convert planSpec tasks to TaskInfo with proper status
|
||||
const initialTasks: TaskInfo[] = planTasks.map((t: any, index: number) => ({
|
||||
id: t.id,
|
||||
description: t.description,
|
||||
filePath: t.filePath,
|
||||
phase: t.phase,
|
||||
status: index < completedCount
|
||||
? "completed" as const
|
||||
: t.id === currentId
|
||||
? "in_progress" as const
|
||||
: "pending" as const,
|
||||
}));
|
||||
|
||||
setTasks(initialTasks);
|
||||
setCurrentTaskId(currentId || null);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to load initial tasks:", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [featureId, projectPath]);
|
||||
|
||||
// Load initial state on mount
|
||||
useEffect(() => {
|
||||
loadInitialTasks();
|
||||
}, [loadInitialTasks]);
|
||||
|
||||
// Listen to task events for real-time updates
|
||||
useEffect(() => {
|
||||
const api = getElectronAPI();
|
||||
if (!api?.autoMode) return;
|
||||
@@ -69,24 +119,25 @@ export function TaskProgressPanel({ featureId, className }: TaskProgressPanelPro
|
||||
t.id === taskEvent.taskId ? { ...t, status: "completed" as const } : t
|
||||
)
|
||||
);
|
||||
// Clear current task if it was completed
|
||||
if (currentTaskId === taskEvent.taskId) {
|
||||
setCurrentTaskId(null);
|
||||
}
|
||||
setCurrentTaskId(null);
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return unsubscribe;
|
||||
}, [featureId, currentTaskId]);
|
||||
}, [featureId]);
|
||||
|
||||
// Calculate progress
|
||||
const completedCount = tasks.filter((t) => t.status === "completed").length;
|
||||
const totalCount = tasks.length;
|
||||
const progressPercent = totalCount > 0 ? Math.round((completedCount / totalCount) * 100) : 0;
|
||||
|
||||
// Don't render if no tasks
|
||||
// Don't render if loading or no tasks
|
||||
if (isLoading) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (tasks.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -218,6 +218,13 @@ export function AgentOutputModal({
|
||||
// Show when plan is auto-approved
|
||||
newContent = `\n✅ Plan auto-approved - continuing to implementation...\n`;
|
||||
break;
|
||||
case "plan_revision_requested":
|
||||
// Show when user requests plan revision
|
||||
if ("planVersion" in event) {
|
||||
const revisionEvent = event as Extract<AutoModeEvent, { type: "plan_revision_requested" }>;
|
||||
newContent = `\n🔄 Revising plan based on your feedback (v${revisionEvent.planVersion})...\n`;
|
||||
}
|
||||
break;
|
||||
case "auto_mode_task_started":
|
||||
// Show when a task starts
|
||||
if ("taskId" in event && "taskDescription" in event) {
|
||||
@@ -362,7 +369,11 @@ export function AgentOutputModal({
|
||||
</DialogHeader>
|
||||
|
||||
{/* Task Progress Panel - shows when tasks are being executed */}
|
||||
<TaskProgressPanel featureId={featureId} className="flex-shrink-0 mx-1" />
|
||||
<TaskProgressPanel
|
||||
featureId={featureId}
|
||||
projectPath={projectPath}
|
||||
className="flex-shrink-0 mx-1"
|
||||
/>
|
||||
|
||||
{viewMode === "changes" ? (
|
||||
<div className="flex-1 min-h-[400px] max-h-[60vh] overflow-y-auto scrollbar-visible">
|
||||
|
||||
@@ -14,7 +14,7 @@ import { Textarea } from "@/components/ui/textarea";
|
||||
import { Markdown } from "@/components/ui/markdown";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Feature } from "@/store/app-store";
|
||||
import { Check, X, Edit2, Eye, Loader2 } from "lucide-react";
|
||||
import { Check, RefreshCw, Edit2, Eye, Loader2 } from "lucide-react";
|
||||
|
||||
interface PlanApprovalDialogProps {
|
||||
open: boolean;
|
||||
@@ -143,18 +143,21 @@ export function PlanApprovalDialog({
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Reject Feedback Section - Only show when not in viewOnly mode */}
|
||||
{/* Revision Feedback Section - Only show when not in viewOnly mode */}
|
||||
{showRejectFeedback && !viewOnly && (
|
||||
<div className="mt-4 space-y-2">
|
||||
<Label htmlFor="reject-feedback">Feedback (optional)</Label>
|
||||
<Label htmlFor="reject-feedback">What changes would you like?</Label>
|
||||
<Textarea
|
||||
id="reject-feedback"
|
||||
value={rejectFeedback}
|
||||
onChange={(e) => setRejectFeedback(e.target.value)}
|
||||
placeholder="Provide feedback on why this plan is being rejected..."
|
||||
placeholder="Describe the changes you'd like to see in the plan..."
|
||||
className="min-h-[80px]"
|
||||
disabled={isLoading}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Leave empty to cancel the feature, or provide feedback to regenerate the plan.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -171,30 +174,30 @@ export function PlanApprovalDialog({
|
||||
onClick={handleCancelReject}
|
||||
disabled={isLoading}
|
||||
>
|
||||
Cancel
|
||||
Back
|
||||
</Button>
|
||||
<Button
|
||||
variant="destructive"
|
||||
variant="secondary"
|
||||
onClick={handleReject}
|
||||
disabled={isLoading}
|
||||
>
|
||||
{isLoading ? (
|
||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
||||
) : (
|
||||
<X className="w-4 h-4 mr-2" />
|
||||
<RefreshCw className="w-4 h-4 mr-2" />
|
||||
)}
|
||||
Confirm Reject
|
||||
{rejectFeedback.trim() ? "Revise Plan" : "Cancel Feature"}
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Button
|
||||
variant="destructive"
|
||||
variant="outline"
|
||||
onClick={handleReject}
|
||||
disabled={isLoading}
|
||||
>
|
||||
<X className="w-4 h-4 mr-2" />
|
||||
Reject
|
||||
<RefreshCw className="w-4 h-4 mr-2" />
|
||||
Request Changes
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleApprove}
|
||||
|
||||
@@ -12,6 +12,15 @@ import { cn } from "@/lib/utils";
|
||||
|
||||
export type PlanningMode = 'skip' | 'lite' | 'spec' | 'full';
|
||||
|
||||
// Parsed task from spec (for spec and full planning modes)
|
||||
export interface ParsedTask {
|
||||
id: string; // e.g., "T001"
|
||||
description: string; // e.g., "Create user model"
|
||||
filePath?: string; // e.g., "src/models/user.ts"
|
||||
phase?: string; // e.g., "Phase 1: Foundation" (for full mode)
|
||||
status: 'pending' | 'in_progress' | 'completed' | 'failed';
|
||||
}
|
||||
|
||||
export interface PlanSpec {
|
||||
status: 'pending' | 'generating' | 'generated' | 'approved' | 'rejected';
|
||||
content?: string;
|
||||
@@ -21,6 +30,8 @@ export interface PlanSpec {
|
||||
reviewedByUser: boolean;
|
||||
tasksCompleted?: number;
|
||||
tasksTotal?: number;
|
||||
currentTaskId?: string; // ID of the task currently being worked on
|
||||
tasks?: ParsedTask[]; // Parsed tasks from the spec
|
||||
}
|
||||
|
||||
interface PlanningModeSelectorProps {
|
||||
|
||||
@@ -294,6 +294,20 @@ export function useAutoMode() {
|
||||
}
|
||||
break;
|
||||
|
||||
case "plan_revision_requested":
|
||||
// Log when user requests plan revision with feedback
|
||||
if (event.featureId) {
|
||||
const revisionEvent = event as Extract<AutoModeEvent, { type: "plan_revision_requested" }>;
|
||||
console.log(`[AutoMode] Plan revision requested for ${event.featureId} (v${revisionEvent.planVersion})`);
|
||||
addAutoModeActivity({
|
||||
featureId: event.featureId,
|
||||
type: "planning",
|
||||
message: `Revising plan based on feedback (v${revisionEvent.planVersion})...`,
|
||||
phase: "planning",
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case "auto_mode_task_started":
|
||||
// Task started - show which task is being worked on
|
||||
if (event.featureId && "taskId" in event && "taskDescription" in event) {
|
||||
|
||||
@@ -305,6 +305,15 @@ export interface Feature {
|
||||
requirePlanApproval?: boolean; // Whether to pause and require manual approval before implementation
|
||||
}
|
||||
|
||||
// Parsed task from spec (for spec and full planning modes)
|
||||
export interface ParsedTask {
|
||||
id: string; // e.g., "T001"
|
||||
description: string; // e.g., "Create user model"
|
||||
filePath?: string; // e.g., "src/models/user.ts"
|
||||
phase?: string; // e.g., "Phase 1: Foundation" (for full mode)
|
||||
status: 'pending' | 'in_progress' | 'completed' | 'failed';
|
||||
}
|
||||
|
||||
// PlanSpec status for feature planning/specification
|
||||
export interface PlanSpec {
|
||||
status: 'pending' | 'generating' | 'generated' | 'approved' | 'rejected';
|
||||
@@ -315,6 +324,8 @@ export interface PlanSpec {
|
||||
reviewedByUser: boolean; // True if user has seen the spec
|
||||
tasksCompleted?: number;
|
||||
tasksTotal?: number;
|
||||
currentTaskId?: string; // ID of the task currently being worked on
|
||||
tasks?: ParsedTask[]; // Parsed tasks from the spec
|
||||
}
|
||||
|
||||
// File tree node for project analysis
|
||||
|
||||
10
apps/app/src/types/electron.d.ts
vendored
10
apps/app/src/types/electron.d.ts
vendored
@@ -244,6 +244,7 @@ export type AutoModeEvent =
|
||||
projectPath?: string;
|
||||
planContent: string;
|
||||
planningMode: "lite" | "spec" | "full";
|
||||
planVersion?: number;
|
||||
}
|
||||
| {
|
||||
type: "plan_auto_approved";
|
||||
@@ -257,6 +258,7 @@ export type AutoModeEvent =
|
||||
featureId: string;
|
||||
projectPath?: string;
|
||||
hasEdits: boolean;
|
||||
planVersion?: number;
|
||||
}
|
||||
| {
|
||||
type: "plan_rejected";
|
||||
@@ -264,6 +266,14 @@ export type AutoModeEvent =
|
||||
projectPath?: string;
|
||||
feedback?: string;
|
||||
}
|
||||
| {
|
||||
type: "plan_revision_requested";
|
||||
featureId: string;
|
||||
projectPath?: string;
|
||||
feedback?: string;
|
||||
hasEdits?: boolean;
|
||||
planVersion?: number;
|
||||
}
|
||||
| {
|
||||
type: "planning_started";
|
||||
featureId: string;
|
||||
|
||||
Reference in New Issue
Block a user