Skip to content

Commit cdc8334

Browse files
authored
Merge pull request #94 from AutoMaker-Org/app_spec_fixes
working on improving the app spec page
2 parents 4a3a98b + c280225 commit cdc8334

30 files changed

Lines changed: 2209 additions & 876 deletions

README.md

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,36 @@
1+
> **[!TIP]**
2+
>
3+
> **Learn more about Agentic Coding!**
4+
>
5+
> Automaker itself was built by a group of engineers using AI and agentic coding techniques to build features faster than ever. By leveraging tools like Cursor IDE and Claude Code CLI, the team orchestrated AI agents to implement complex functionality in days instead of weeks.
6+
>
7+
> **Learn how:** Master these same techniques and workflows in the [Agentic Jumpstart course](https://agenticjumpstart.com/).
8+
19
# Automaker
210

3-
Automaker is an autonomous AI development studio that helps you build software faster using AI-powered agents. It provides a visual Kanban board interface to manage features, automatically assigns AI agents to implement them, and tracks progress through an intuitive workflow from backlog to verified completion.
11+
**Stop typing code. Start directing AI agents.**
12+
13+
Automaker is an autonomous AI development studio that transforms how you build software. Instead of manually writing every line of code, you describe features on a Kanban board and watch as AI agents powered by Claude Code automatically implement them.
14+
15+
## What Makes Automaker Different?
16+
17+
Traditional development tools help you write code. Automaker helps you **orchestrate AI agents** to build entire features autonomously. Think of it as having a team of AI developers working for you—you define what needs to be built, and Automaker handles the implementation.
18+
19+
### The Workflow
20+
21+
1. **Add Features** - Describe features you want built (with text, images, or screenshots)
22+
2. **Move to "In Progress"** - Automaker automatically assigns an AI agent to implement the feature
23+
3. **Watch It Build** - See real-time progress as the agent writes code, runs tests, and makes changes
24+
4. **Review & Verify** - Review the changes, run tests, and approve when ready
25+
5. **Ship Faster** - Build entire applications in days, not weeks
26+
27+
### Powered by Claude Code
28+
29+
Automaker leverages the [Claude Agent SDK](https://docs.anthropic.com/en/docs/claude-code) to give AI agents full access to your codebase. Agents can read files, write code, execute commands, run tests, and make git commits—all while working in isolated git worktrees to keep your main branch safe.
30+
31+
### Why This Matters
32+
33+
The future of software development is **agentic coding**—where developers become architects directing AI agents rather than manual coders. Automaker puts this future in your hands today, letting you experience what it's like to build software 10x faster with AI agents handling the implementation while you focus on architecture and business logic.
434

535
---
636

@@ -36,20 +66,22 @@ cd automaker
3666
# 2. Install dependencies
3767
npm install
3868

39-
# 3. Get your Claude Code OAuth token
40-
claude setup-token
41-
# ⚠️ This prints your token - don't share your screen!
42-
43-
# 4. Set the token and run
44-
export CLAUDE_CODE_OAUTH_TOKEN="sk-ant-oat01-..."
45-
npm run dev:electron
69+
# 3. Run Automaker (pick your mode)
70+
npm run dev
71+
# Then choose your run mode when prompted, or use specific commands below
4672
```
4773

4874
## How to Run
4975

50-
### Development Modes
76+
### Development Mode
5177

52-
Automaker can be run in several modes:
78+
Start Automaker in development mode:
79+
80+
```bash
81+
npm run dev
82+
```
83+
84+
This will prompt you to choose your run mode, or you can specify a mode directly:
5385

5486
#### Electron Desktop App (Recommended)
5587

@@ -72,8 +104,6 @@ npm run dev:electron:wsl:gpu
72104
```bash
73105
# Run in web browser (http://localhost:3007)
74106
npm run dev:web
75-
# or
76-
npm run dev
77107
```
78108

79109
### Building for Production

apps/app/src/components/layout/sidebar.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -381,8 +381,6 @@ export function Sidebar() {
381381
toast.success("App specification created", {
382382
description: "Your project is now set up and ready to go!",
383383
});
384-
// Navigate to spec view to show the new spec
385-
setCurrentView("spec");
386384
} else if (event.type === "spec_regeneration_error") {
387385
setSpecCreatingForProject(null);
388386
toast.error("Failed to create specification", {

apps/app/src/components/new-project-modal.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ import { getHttpApiClient } from "@/lib/http-api-client";
3131
import { cn } from "@/lib/utils";
3232
import { useFileBrowser } from "@/contexts/file-browser-context";
3333

34+
const LAST_PROJECT_DIR_KEY = "automaker:lastProjectDir";
35+
3436
interface ValidationErrors {
3537
projectName?: boolean;
3638
workspaceDir?: boolean;
@@ -80,6 +82,14 @@ export function NewProjectModal({
8082
// Fetch workspace directory when modal opens
8183
useEffect(() => {
8284
if (open) {
85+
// First, check localStorage for last used directory
86+
const lastUsedDir = localStorage.getItem(LAST_PROJECT_DIR_KEY);
87+
if (lastUsedDir) {
88+
setWorkspaceDir(lastUsedDir);
89+
return;
90+
}
91+
92+
// Fall back to server config if no saved directory
8393
setIsLoadingWorkspace(true);
8494
const httpClient = getHttpApiClient();
8595
httpClient.workspace
@@ -201,6 +211,8 @@ export function NewProjectModal({
201211
});
202212
if (selectedPath) {
203213
setWorkspaceDir(selectedPath);
214+
// Save to localStorage for next time
215+
localStorage.setItem(LAST_PROJECT_DIR_KEY, selectedPath);
204216
// Clear any workspace error when a valid directory is selected
205217
if (errors.workspaceDir) {
206218
setErrors((prev) => ({ ...prev, workspaceDir: false }));

apps/app/src/components/views/board-view.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1554,6 +1554,13 @@ export function BoardView() {
15541554
}
15551555
});
15561556

1557+
// Sort backlog by priority: 1 (high) -> 2 (medium) -> 3 (low) -> no priority
1558+
map.backlog.sort((a, b) => {
1559+
const aPriority = a.priority ?? 999; // Features without priority go last
1560+
const bPriority = b.priority ?? 999;
1561+
return aPriority - bPriority;
1562+
});
1563+
15571564
return map;
15581565
}, [features, runningAutoTasks, searchQuery]);
15591566

apps/app/src/components/views/kanban-card.tsx

Lines changed: 85 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import {
5757
ChevronDown,
5858
ChevronUp,
5959
Brain,
60+
Flag,
6061
} from "lucide-react";
6162
import { CountUpTimer } from "@/components/ui/count-up-timer";
6263
import { getElectronAPI } from "@/lib/electron";
@@ -89,6 +90,33 @@ function formatThinkingLevel(level: ThinkingLevel | undefined): string {
8990
return labels[level];
9091
}
9192

93+
/**
94+
* Formats priority for display
95+
*/
96+
function formatPriority(priority: number | undefined): string | null {
97+
if (!priority) return null;
98+
const labels: Record<number, string> = {
99+
1: "High",
100+
2: "Medium",
101+
3: "Low",
102+
};
103+
return labels[priority] || null;
104+
}
105+
106+
/**
107+
* Gets priority badge color classes
108+
*/
109+
function getPriorityBadgeClasses(priority: number | undefined): string {
110+
if (priority === 1) {
111+
return "bg-red-500/20 border border-red-500/50 text-red-400";
112+
} else if (priority === 2) {
113+
return "bg-yellow-500/20 border border-yellow-500/50 text-yellow-400";
114+
} else if (priority === 3) {
115+
return "bg-blue-500/20 border border-blue-500/50 text-blue-400";
116+
}
117+
return "";
118+
}
119+
92120
interface KanbanCardProps {
93121
feature: Feature;
94122
onEdit: () => void;
@@ -198,6 +226,34 @@ export const KanbanCard = memo(function KanbanCard({
198226
return () => clearInterval(interval);
199227
}, [feature.justFinishedAt, feature.status, currentTime]);
200228

229+
// Calculate priority badge position
230+
const priorityLabel = formatPriority(feature.priority);
231+
const hasPriority = !!priorityLabel;
232+
233+
// Calculate top position for badges (stacking vertically)
234+
const getBadgeTopPosition = (badgeIndex: number) => {
235+
return badgeIndex === 0
236+
? "top-2"
237+
: badgeIndex === 1
238+
? "top-8"
239+
: badgeIndex === 2
240+
? "top-14"
241+
: "top-20";
242+
};
243+
244+
// Determine badge positions (must be after isJustFinished is defined)
245+
let badgeIndex = 0;
246+
const priorityBadgeIndex = hasPriority ? badgeIndex++ : -1;
247+
const skipTestsBadgeIndex =
248+
feature.skipTests && !feature.error ? badgeIndex++ : -1;
249+
const errorBadgeIndex = feature.error ? badgeIndex++ : -1;
250+
const justFinishedBadgeIndex = isJustFinished ? badgeIndex++ : -1;
251+
const branchBadgeIndex =
252+
hasWorktree && !isCurrentAutoTask ? badgeIndex++ : -1;
253+
254+
// Total number of badges displayed
255+
const totalBadgeCount = badgeIndex;
256+
201257
// Load context file for in_progress, waiting_approval, and verified features
202258
useEffect(() => {
203259
const loadContext = async () => {
@@ -353,12 +409,29 @@ export const KanbanCard = memo(function KanbanCard({
353409
style={{ opacity: opacity / 100 }}
354410
/>
355411
)}
412+
{/* Priority badge */}
413+
{hasPriority && (
414+
<div
415+
className={cn(
416+
"absolute px-1.5 py-0.5 text-[10px] font-medium rounded flex items-center gap-1 z-10",
417+
getBadgeTopPosition(priorityBadgeIndex),
418+
"left-2",
419+
getPriorityBadgeClasses(feature.priority)
420+
)}
421+
data-testid={`priority-badge-${feature.id}`}
422+
title={`Priority: ${priorityLabel}`}
423+
>
424+
<Flag className="w-3 h-3" />
425+
<span>{priorityLabel}</span>
426+
</div>
427+
)}
356428
{/* Skip Tests indicator badge */}
357429
{feature.skipTests && !feature.error && (
358430
<div
359431
className={cn(
360432
"absolute px-1.5 py-0.5 text-[10px] font-medium rounded flex items-center gap-1 z-10",
361-
"top-2 left-2",
433+
getBadgeTopPosition(skipTestsBadgeIndex),
434+
"left-2",
362435
"bg-orange-500/20 border border-orange-500/50 text-orange-400"
363436
)}
364437
data-testid={`skip-tests-badge-${feature.id}`}
@@ -373,7 +446,8 @@ export const KanbanCard = memo(function KanbanCard({
373446
<div
374447
className={cn(
375448
"absolute px-1.5 py-0.5 text-[10px] font-medium rounded flex items-center gap-1 z-10",
376-
"top-2 left-2",
449+
getBadgeTopPosition(errorBadgeIndex),
450+
"left-2",
377451
"bg-red-500/20 border border-red-500/50 text-red-400"
378452
)}
379453
data-testid={`error-badge-${feature.id}`}
@@ -388,7 +462,8 @@ export const KanbanCard = memo(function KanbanCard({
388462
<div
389463
className={cn(
390464
"absolute px-1.5 py-0.5 text-[10px] font-medium rounded flex items-center gap-1 z-10",
391-
feature.skipTests ? "top-8 left-2" : "top-2 left-2",
465+
getBadgeTopPosition(justFinishedBadgeIndex),
466+
"left-2",
392467
"bg-green-500/20 border border-green-500/50 text-green-400 animate-pulse"
393468
)}
394469
data-testid={`just-finished-badge-${feature.id}`}
@@ -407,10 +482,8 @@ export const KanbanCard = memo(function KanbanCard({
407482
className={cn(
408483
"absolute px-1.5 py-0.5 text-[10px] font-medium rounded flex items-center gap-1 z-10 cursor-default",
409484
"bg-purple-500/20 border border-purple-500/50 text-purple-400",
410-
// Position below other badges if present, otherwise use normal position
411-
feature.error || feature.skipTests || isJustFinished
412-
? "top-8 left-2"
413-
: "top-2 left-2"
485+
getBadgeTopPosition(branchBadgeIndex),
486+
"left-2"
414487
)}
415488
data-testid={`branch-badge-${feature.id}`}
416489
>
@@ -432,11 +505,11 @@ export const KanbanCard = memo(function KanbanCard({
432505
className={cn(
433506
"p-3 pb-2 block", // Reset grid layout to block for custom kanban card layout
434507
// Add extra top padding when badges are present to prevent text overlap
435-
(feature.skipTests || feature.error || isJustFinished) && "pt-10",
436-
// Add even more top padding when both badges and branch are shown
437-
hasWorktree &&
438-
(feature.skipTests || feature.error || isJustFinished) &&
439-
"pt-14"
508+
// Calculate padding based on number of badges
509+
totalBadgeCount === 1 && "pt-10",
510+
totalBadgeCount === 2 && "pt-14",
511+
totalBadgeCount === 3 && "pt-20",
512+
totalBadgeCount >= 4 && "pt-24"
440513
)}
441514
>
442515
{isCurrentAutoTask && (

0 commit comments

Comments
 (0)