mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-03 08:53:36 +00:00
Merge branch 'main' of github.com:webdevcody/automaker
This commit is contained in:
@@ -65,6 +65,8 @@ import {
|
||||
FastForward,
|
||||
FlaskConical,
|
||||
CheckCircle2,
|
||||
MessageSquare,
|
||||
GitCommit,
|
||||
} from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
import { Slider } from "@/components/ui/slider";
|
||||
@@ -81,6 +83,7 @@ type ColumnId = Feature["status"];
|
||||
const COLUMNS: { id: ColumnId; title: string; color: string }[] = [
|
||||
{ id: "backlog", title: "Backlog", color: "bg-zinc-500" },
|
||||
{ id: "in_progress", title: "In Progress", color: "bg-yellow-500" },
|
||||
{ id: "waiting_approval", title: "Waiting Approval", color: "bg-orange-500" },
|
||||
{ id: "verified", title: "Verified", color: "bg-green-500" },
|
||||
];
|
||||
|
||||
@@ -119,6 +122,12 @@ export function BoardView() {
|
||||
const [showDeleteAllVerifiedDialog, setShowDeleteAllVerifiedDialog] =
|
||||
useState(false);
|
||||
const [persistedCategories, setPersistedCategories] = useState<string[]>([]);
|
||||
const [showFollowUpDialog, setShowFollowUpDialog] = useState(false);
|
||||
const [followUpFeature, setFollowUpFeature] = useState<Feature | null>(null);
|
||||
const [followUpPrompt, setFollowUpPrompt] = useState("");
|
||||
const [followUpImagePaths, setFollowUpImagePaths] = useState<
|
||||
DescriptionImagePath[]
|
||||
>([]);
|
||||
|
||||
// Make current project available globally for modal
|
||||
useEffect(() => {
|
||||
@@ -428,6 +437,7 @@ export function BoardView() {
|
||||
startedAt: f.startedAt,
|
||||
imagePaths: f.imagePaths,
|
||||
skipTests: f.skipTests,
|
||||
summary: f.summary,
|
||||
}));
|
||||
await api.writeFile(
|
||||
`${currentProject.path}/.automaker/feature_list.json`,
|
||||
@@ -776,6 +786,142 @@ export function BoardView() {
|
||||
});
|
||||
};
|
||||
|
||||
// Open follow-up dialog for waiting_approval features
|
||||
const handleOpenFollowUp = (feature: Feature) => {
|
||||
console.log("[Board] Opening follow-up dialog for feature:", {
|
||||
id: feature.id,
|
||||
description: feature.description,
|
||||
});
|
||||
setFollowUpFeature(feature);
|
||||
setFollowUpPrompt("");
|
||||
setFollowUpImagePaths([]);
|
||||
setShowFollowUpDialog(true);
|
||||
};
|
||||
|
||||
// Handle sending follow-up prompt
|
||||
const handleSendFollowUp = async () => {
|
||||
if (!currentProject || !followUpFeature || !followUpPrompt.trim()) return;
|
||||
|
||||
// Save values before clearing state
|
||||
const featureId = followUpFeature.id;
|
||||
const featureDescription = followUpFeature.description;
|
||||
const prompt = followUpPrompt;
|
||||
const imagePaths = followUpImagePaths.map((img) => img.path);
|
||||
|
||||
console.log("[Board] Sending follow-up prompt for feature:", {
|
||||
id: featureId,
|
||||
prompt: prompt,
|
||||
imagePaths: imagePaths,
|
||||
});
|
||||
|
||||
const api = getElectronAPI();
|
||||
if (!api?.autoMode?.followUpFeature) {
|
||||
console.error("Follow-up feature API not available");
|
||||
toast.error("Follow-up not available", {
|
||||
description: "This feature is not available in the current version.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Move feature back to in_progress before sending follow-up
|
||||
updateFeature(featureId, {
|
||||
status: "in_progress",
|
||||
startedAt: new Date().toISOString(),
|
||||
});
|
||||
|
||||
// Reset follow-up state immediately (close dialog, clear form)
|
||||
setShowFollowUpDialog(false);
|
||||
setFollowUpFeature(null);
|
||||
setFollowUpPrompt("");
|
||||
setFollowUpImagePaths([]);
|
||||
|
||||
// Show success toast immediately
|
||||
toast.success("Follow-up started", {
|
||||
description: `Continuing work on: ${featureDescription.slice(0, 50)}${
|
||||
featureDescription.length > 50 ? "..." : ""
|
||||
}`,
|
||||
});
|
||||
|
||||
// Call the API in the background (don't await - let it run async)
|
||||
api.autoMode
|
||||
.followUpFeature(currentProject.path, featureId, prompt, imagePaths)
|
||||
.catch((error) => {
|
||||
console.error("[Board] Error sending follow-up:", error);
|
||||
toast.error("Failed to send follow-up", {
|
||||
description:
|
||||
error instanceof Error ? error.message : "An error occurred",
|
||||
});
|
||||
// Reload features to revert status if there was an error
|
||||
loadFeatures();
|
||||
});
|
||||
};
|
||||
|
||||
// Handle commit-only for waiting_approval features (marks as verified and commits)
|
||||
const handleCommitFeature = async (feature: Feature) => {
|
||||
if (!currentProject) return;
|
||||
|
||||
console.log("[Board] Committing feature:", {
|
||||
id: feature.id,
|
||||
description: feature.description,
|
||||
});
|
||||
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (!api?.autoMode?.commitFeature) {
|
||||
console.error("Commit feature API not available");
|
||||
toast.error("Commit not available", {
|
||||
description: "This feature is not available in the current version.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Call the API to commit this feature
|
||||
const result = await api.autoMode.commitFeature(
|
||||
currentProject.path,
|
||||
feature.id
|
||||
);
|
||||
|
||||
if (result.success) {
|
||||
console.log("[Board] Feature committed successfully");
|
||||
// Move to verified status
|
||||
moveFeature(feature.id, "verified");
|
||||
toast.success("Feature committed", {
|
||||
description: `Committed and verified: ${feature.description.slice(
|
||||
0,
|
||||
50
|
||||
)}${feature.description.length > 50 ? "..." : ""}`,
|
||||
});
|
||||
} else {
|
||||
console.error("[Board] Failed to commit feature:", result.error);
|
||||
toast.error("Failed to commit feature", {
|
||||
description: result.error || "An error occurred",
|
||||
});
|
||||
await loadFeatures();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[Board] Error committing feature:", error);
|
||||
toast.error("Failed to commit feature", {
|
||||
description:
|
||||
error instanceof Error ? error.message : "An error occurred",
|
||||
});
|
||||
await loadFeatures();
|
||||
}
|
||||
};
|
||||
|
||||
// Move feature to waiting_approval (for skipTests features when agent completes)
|
||||
const handleMoveToWaitingApproval = (feature: Feature) => {
|
||||
console.log("[Board] Moving feature to waiting_approval:", {
|
||||
id: feature.id,
|
||||
description: feature.description,
|
||||
});
|
||||
updateFeature(feature.id, { status: "waiting_approval" });
|
||||
toast.info("Feature ready for review", {
|
||||
description: `Ready for approval: ${feature.description.slice(0, 50)}${
|
||||
feature.description.length > 50 ? "..." : ""
|
||||
}`,
|
||||
});
|
||||
};
|
||||
|
||||
const checkContextExists = async (featureId: string): Promise<boolean> => {
|
||||
if (!currentProject) return false;
|
||||
|
||||
@@ -844,12 +990,30 @@ export function BoardView() {
|
||||
const handleForceStopFeature = async (feature: Feature) => {
|
||||
try {
|
||||
await autoMode.stopFeature(feature.id);
|
||||
// Move the feature back to backlog status after stopping
|
||||
moveFeature(feature.id, "backlog");
|
||||
|
||||
// Determine where to move the feature after stopping:
|
||||
// - If it's a skipTests feature that was in waiting_approval (i.e., during commit operation),
|
||||
// move it back to waiting_approval so user can try commit again or do follow-up
|
||||
// - Otherwise, move to backlog
|
||||
const targetStatus =
|
||||
feature.skipTests && feature.status === "waiting_approval"
|
||||
? "waiting_approval"
|
||||
: "backlog";
|
||||
|
||||
if (targetStatus !== feature.status) {
|
||||
moveFeature(feature.id, targetStatus);
|
||||
}
|
||||
|
||||
toast.success("Agent stopped", {
|
||||
description: `Stopped working on: ${feature.description.slice(0, 50)}${
|
||||
feature.description.length > 50 ? "..." : ""
|
||||
}`,
|
||||
description:
|
||||
targetStatus === "waiting_approval"
|
||||
? `Stopped commit - returned to waiting approval: ${feature.description.slice(
|
||||
0,
|
||||
50
|
||||
)}${feature.description.length > 50 ? "..." : ""}`
|
||||
: `Stopped working on: ${feature.description.slice(0, 50)}${
|
||||
feature.description.length > 50 ? "..." : ""
|
||||
}`,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("[Board] Error stopping feature:", error);
|
||||
@@ -1114,6 +1278,8 @@ export function BoardView() {
|
||||
onMoveBackToInProgress={() =>
|
||||
handleMoveBackToInProgress(feature)
|
||||
}
|
||||
onFollowUp={() => handleOpenFollowUp(feature)}
|
||||
onCommit={() => handleCommitFeature(feature)}
|
||||
hasContext={featuresWithContext.has(feature.id)}
|
||||
isCurrentAutoTask={runningAutoTasks.includes(
|
||||
feature.id
|
||||
@@ -1457,6 +1623,86 @@ export function BoardView() {
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{/* Follow-Up Prompt Dialog */}
|
||||
<Dialog
|
||||
open={showFollowUpDialog}
|
||||
onOpenChange={(open) => {
|
||||
if (!open) {
|
||||
setShowFollowUpDialog(false);
|
||||
setFollowUpFeature(null);
|
||||
setFollowUpPrompt("");
|
||||
setFollowUpImagePaths([]);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DialogContent
|
||||
data-testid="follow-up-dialog"
|
||||
onKeyDown={(e) => {
|
||||
if (
|
||||
(e.metaKey || e.ctrlKey) &&
|
||||
e.key === "Enter" &&
|
||||
followUpPrompt.trim()
|
||||
) {
|
||||
e.preventDefault();
|
||||
handleSendFollowUp();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Follow-Up Prompt</DialogTitle>
|
||||
<DialogDescription>
|
||||
Send additional instructions to continue working on this feature.
|
||||
{followUpFeature && (
|
||||
<span className="block mt-2 text-primary">
|
||||
Feature: {followUpFeature.description.slice(0, 100)}
|
||||
{followUpFeature.description.length > 100 ? "..." : ""}
|
||||
</span>
|
||||
)}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="space-y-4 py-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="follow-up-prompt">Instructions</Label>
|
||||
<DescriptionImageDropZone
|
||||
value={followUpPrompt}
|
||||
onChange={setFollowUpPrompt}
|
||||
images={followUpImagePaths}
|
||||
onImagesChange={setFollowUpImagePaths}
|
||||
placeholder="Describe what needs to be fixed or changed..."
|
||||
/>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
The agent will continue from where it left off, using the existing
|
||||
context. You can attach screenshots to help explain the issue.
|
||||
</p>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => {
|
||||
setShowFollowUpDialog(false);
|
||||
setFollowUpFeature(null);
|
||||
setFollowUpPrompt("");
|
||||
setFollowUpImagePaths([]);
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleSendFollowUp}
|
||||
disabled={!followUpPrompt.trim()}
|
||||
data-testid="confirm-follow-up"
|
||||
>
|
||||
<MessageSquare className="w-4 h-4 mr-2" />
|
||||
Send Follow-Up
|
||||
<span className="ml-2 px-1.5 py-0.5 text-[10px] font-mono rounded bg-white/10 border border-white/20">
|
||||
⌘↵
|
||||
</span>
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user