mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-02 08:33:36 +00:00
Update app_spec.txt and coding_prompt.md for improved clarity and consistency
- Updated references to `app_spec.txt` and `feature_list.json` in app_spec.txt to include the correct path. - Enhanced coding_prompt.md by incorporating testing utilities for better test management and readability. - Added new utility functions in tests/utils.ts to streamline test interactions. This commit aims to improve documentation accuracy and maintainability of testing practices.
This commit is contained in:
@@ -106,6 +106,11 @@ export function AgentOutputModal({
|
||||
if (!api?.autoMode) return;
|
||||
|
||||
const unsubscribe = api.autoMode.onEvent((event) => {
|
||||
// Filter events for this specific feature only
|
||||
if (event.featureId !== featureId) {
|
||||
return;
|
||||
}
|
||||
|
||||
let newContent = "";
|
||||
|
||||
if (event.type === "auto_mode_progress") {
|
||||
|
||||
@@ -79,6 +79,7 @@ export function BoardView() {
|
||||
const [showActivityLog, setShowActivityLog] = useState(false);
|
||||
const [showOutputModal, setShowOutputModal] = useState(false);
|
||||
const [outputFeature, setOutputFeature] = useState<Feature | null>(null);
|
||||
const [featuresWithContext, setFeaturesWithContext] = useState<Set<string>>(new Set());
|
||||
|
||||
// Make current project available globally for modal
|
||||
useEffect(() => {
|
||||
@@ -185,6 +186,32 @@ export function BoardView() {
|
||||
loadFeatures();
|
||||
}, [loadFeatures]);
|
||||
|
||||
// Check which features have context files
|
||||
useEffect(() => {
|
||||
const checkAllContexts = async () => {
|
||||
const inProgressFeatures = features.filter((f) => f.status === "in_progress");
|
||||
const contextChecks = await Promise.all(
|
||||
inProgressFeatures.map(async (f) => ({
|
||||
id: f.id,
|
||||
hasContext: await checkContextExists(f.id),
|
||||
}))
|
||||
);
|
||||
|
||||
const newSet = new Set<string>();
|
||||
contextChecks.forEach(({ id, hasContext }) => {
|
||||
if (hasContext) {
|
||||
newSet.add(id);
|
||||
}
|
||||
});
|
||||
|
||||
setFeaturesWithContext(newSet);
|
||||
};
|
||||
|
||||
if (features.length > 0 && !isLoading) {
|
||||
checkAllContexts();
|
||||
}
|
||||
}, [features, isLoading]);
|
||||
|
||||
// Save features to file
|
||||
const saveFeatures = useCallback(async () => {
|
||||
if (!currentProject) return;
|
||||
@@ -360,6 +387,59 @@ export function BoardView() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleResumeFeature = async (feature: Feature) => {
|
||||
if (!currentProject) return;
|
||||
|
||||
console.log("[Board] Resuming feature:", { id: feature.id, description: feature.description });
|
||||
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (!api?.autoMode) {
|
||||
console.error("Auto mode API not available");
|
||||
return;
|
||||
}
|
||||
|
||||
// Call the API to resume this specific feature by ID with context
|
||||
const result = await api.autoMode.resumeFeature(
|
||||
currentProject.path,
|
||||
feature.id
|
||||
);
|
||||
|
||||
if (result.success) {
|
||||
console.log("[Board] Feature resume started successfully");
|
||||
// The feature status will be updated by the auto mode service
|
||||
// and the UI will reload features when resume completes
|
||||
} else {
|
||||
console.error("[Board] Failed to resume feature:", result.error);
|
||||
await loadFeatures();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[Board] Error resuming feature:", error);
|
||||
await loadFeatures();
|
||||
}
|
||||
};
|
||||
|
||||
const checkContextExists = async (featureId: string): Promise<boolean> => {
|
||||
if (!currentProject) return false;
|
||||
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (!api?.autoMode?.contextExists) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const result = await api.autoMode.contextExists(
|
||||
currentProject.path,
|
||||
featureId
|
||||
);
|
||||
|
||||
return result.success && result.exists === true;
|
||||
} catch (error) {
|
||||
console.error("[Board] Error checking context:", error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const getColumnFeatures = (columnId: ColumnId) => {
|
||||
return features.filter((f) => f.status === columnId);
|
||||
};
|
||||
@@ -504,6 +584,8 @@ export function BoardView() {
|
||||
onDelete={() => handleDeleteFeature(feature.id)}
|
||||
onViewOutput={() => handleViewOutput(feature)}
|
||||
onVerify={() => handleVerifyFeature(feature)}
|
||||
onResume={() => handleResumeFeature(feature)}
|
||||
hasContext={featuresWithContext.has(feature.id)}
|
||||
isCurrentAutoTask={runningAutoTasks.includes(feature.id)}
|
||||
/>
|
||||
))}
|
||||
|
||||
@@ -295,11 +295,14 @@ export function InterviewView() {
|
||||
await api.mkdir(fullProjectPath);
|
||||
|
||||
// Write app_spec.txt with generated content
|
||||
await api.writeFile(`${fullProjectPath}/app_spec.txt`, generatedSpec);
|
||||
await api.writeFile(
|
||||
`${fullProjectPath}/.automaker/app_spec.txt`,
|
||||
generatedSpec
|
||||
);
|
||||
|
||||
// Create initial .automaker/feature_list.json
|
||||
await api.writeFile(
|
||||
`${fullProjectPath}/feature_list.json`,
|
||||
`${fullProjectPath}/.automaker/feature_list.json`,
|
||||
JSON.stringify(
|
||||
[
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
} from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Feature } from "@/store/app-store";
|
||||
import { GripVertical, Edit, CheckCircle2, Circle, Loader2, Trash2, Eye, PlayCircle } from "lucide-react";
|
||||
import { GripVertical, Edit, CheckCircle2, Circle, Loader2, Trash2, Eye, PlayCircle, RotateCcw } from "lucide-react";
|
||||
|
||||
interface KanbanCardProps {
|
||||
feature: Feature;
|
||||
@@ -20,10 +20,12 @@ interface KanbanCardProps {
|
||||
onDelete: () => void;
|
||||
onViewOutput?: () => void;
|
||||
onVerify?: () => void;
|
||||
onResume?: () => void;
|
||||
hasContext?: boolean;
|
||||
isCurrentAutoTask?: boolean;
|
||||
}
|
||||
|
||||
export function KanbanCard({ feature, onEdit, onDelete, onViewOutput, onVerify, isCurrentAutoTask }: KanbanCardProps) {
|
||||
export function KanbanCard({ feature, onEdit, onDelete, onViewOutput, onVerify, onResume, hasContext, isCurrentAutoTask }: KanbanCardProps) {
|
||||
// Disable dragging if the feature is in progress or verified
|
||||
const isDraggable = feature.status === "backlog";
|
||||
const {
|
||||
@@ -127,7 +129,21 @@ export function KanbanCard({ feature, onEdit, onDelete, onViewOutput, onVerify,
|
||||
)}
|
||||
{!isCurrentAutoTask && feature.status === "in_progress" && (
|
||||
<>
|
||||
{onVerify && (
|
||||
{hasContext && onResume ? (
|
||||
<Button
|
||||
variant="default"
|
||||
size="sm"
|
||||
className="flex-1 h-7 text-xs bg-blue-600 hover:bg-blue-700"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onResume();
|
||||
}}
|
||||
data-testid={`resume-feature-${feature.id}`}
|
||||
>
|
||||
<RotateCcw className="w-3 h-3 mr-1" />
|
||||
Resume
|
||||
</Button>
|
||||
) : onVerify ? (
|
||||
<Button
|
||||
variant="default"
|
||||
size="sm"
|
||||
@@ -139,9 +155,9 @@ export function KanbanCard({ feature, onEdit, onDelete, onViewOutput, onVerify,
|
||||
data-testid={`verify-feature-${feature.id}`}
|
||||
>
|
||||
<PlayCircle className="w-3 h-3 mr-1" />
|
||||
Verify
|
||||
Implement
|
||||
</Button>
|
||||
)}
|
||||
) : null}
|
||||
{onViewOutput && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
|
||||
@@ -12,10 +12,25 @@ import {
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { useAppStore } from "@/store/app-store";
|
||||
import { getElectronAPI } from "@/lib/electron";
|
||||
import { FolderOpen, Plus, Cpu, Folder, Clock, Sparkles, MessageSquare, ChevronDown } from "lucide-react";
|
||||
import {
|
||||
FolderOpen,
|
||||
Plus,
|
||||
Cpu,
|
||||
Folder,
|
||||
Clock,
|
||||
Sparkles,
|
||||
MessageSquare,
|
||||
ChevronDown,
|
||||
} from "lucide-react";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@@ -24,7 +39,8 @@ import {
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
|
||||
export function WelcomeView() {
|
||||
const { projects, addProject, setCurrentProject, setCurrentView } = useAppStore();
|
||||
const { projects, addProject, setCurrentProject, setCurrentView } =
|
||||
useAppStore();
|
||||
const [showNewProjectDialog, setShowNewProjectDialog] = useState(false);
|
||||
const [newProjectName, setNewProjectName] = useState("");
|
||||
const [newProjectPath, setNewProjectPath] = useState("");
|
||||
@@ -101,13 +117,17 @@ export function WelcomeView() {
|
||||
);
|
||||
|
||||
await api.writeFile(
|
||||
`${projectPath}/feature_list.json`,
|
||||
`${projectPath}/.automaker/feature_list.json`,
|
||||
JSON.stringify(
|
||||
[
|
||||
{
|
||||
category: "Core",
|
||||
description: "First feature to implement",
|
||||
steps: ["Step 1: Define requirements", "Step 2: Implement", "Step 3: Test"],
|
||||
steps: [
|
||||
"Step 1: Define requirements",
|
||||
"Step 2: Implement",
|
||||
"Step 3: Test",
|
||||
],
|
||||
passes: false,
|
||||
},
|
||||
],
|
||||
@@ -151,8 +171,12 @@ export function WelcomeView() {
|
||||
<Cpu className="w-5 h-5 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-white">Welcome to Automaker</h1>
|
||||
<p className="text-sm text-zinc-400">Your autonomous AI development studio</p>
|
||||
<h1 className="text-2xl font-bold text-white">
|
||||
Welcome to Automaker
|
||||
</h1>
|
||||
<p className="text-sm text-zinc-400">
|
||||
Your autonomous AI development studio
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -174,9 +198,12 @@ export function WelcomeView() {
|
||||
<Plus className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="text-lg font-semibold text-white mb-1">New Project</h3>
|
||||
<h3 className="text-lg font-semibold text-white mb-1">
|
||||
New Project
|
||||
</h3>
|
||||
<p className="text-sm text-zinc-400">
|
||||
Create a new project from scratch with AI-powered development
|
||||
Create a new project from scratch with AI-powered
|
||||
development
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -223,7 +250,9 @@ export function WelcomeView() {
|
||||
<FolderOpen className="w-6 h-6 text-zinc-400 group-hover:text-white transition-colors" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="text-lg font-semibold text-white mb-1">Open Project</h3>
|
||||
<h3 className="text-lg font-semibold text-white mb-1">
|
||||
Open Project
|
||||
</h3>
|
||||
<p className="text-sm text-zinc-400">
|
||||
Open an existing project folder to continue working
|
||||
</p>
|
||||
@@ -246,7 +275,9 @@ export function WelcomeView() {
|
||||
<div>
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<Clock className="w-5 h-5 text-zinc-400" />
|
||||
<h2 className="text-lg font-semibold text-white">Recent Projects</h2>
|
||||
<h2 className="text-lg font-semibold text-white">
|
||||
Recent Projects
|
||||
</h2>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{recentProjects.map((project) => (
|
||||
@@ -266,10 +297,14 @@ export function WelcomeView() {
|
||||
<p className="font-medium text-white truncate group-hover:text-brand-500 transition-colors">
|
||||
{project.name}
|
||||
</p>
|
||||
<p className="text-xs text-zinc-500 truncate mt-0.5">{project.path}</p>
|
||||
<p className="text-xs text-zinc-500 truncate mt-0.5">
|
||||
{project.path}
|
||||
</p>
|
||||
{project.lastOpened && (
|
||||
<p className="text-xs text-zinc-600 mt-1">
|
||||
{new Date(project.lastOpened).toLocaleDateString()}
|
||||
{new Date(
|
||||
project.lastOpened
|
||||
).toLocaleDateString()}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
@@ -287,7 +322,9 @@ export function WelcomeView() {
|
||||
<div className="w-16 h-16 rounded-2xl bg-zinc-900/50 border border-white/10 flex items-center justify-center mb-4">
|
||||
<Sparkles className="w-8 h-8 text-zinc-600" />
|
||||
</div>
|
||||
<h3 className="text-lg font-semibold text-white mb-2">No projects yet</h3>
|
||||
<h3 className="text-lg font-semibold text-white mb-2">
|
||||
No projects yet
|
||||
</h3>
|
||||
<p className="text-sm text-zinc-400 max-w-md">
|
||||
Get started by creating a new project or opening an existing one
|
||||
</p>
|
||||
@@ -297,7 +334,10 @@ export function WelcomeView() {
|
||||
</div>
|
||||
|
||||
{/* New Project Dialog */}
|
||||
<Dialog open={showNewProjectDialog} onOpenChange={setShowNewProjectDialog}>
|
||||
<Dialog
|
||||
open={showNewProjectDialog}
|
||||
onOpenChange={setShowNewProjectDialog}
|
||||
>
|
||||
<DialogContent
|
||||
className="bg-zinc-900 border-white/10"
|
||||
data-testid="new-project-dialog"
|
||||
|
||||
Reference in New Issue
Block a user