mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-02 20:43:36 +00:00
Merge origin/main into feature/shared-packages
This commit is contained in:
@@ -12,7 +12,7 @@ import { fileURLToPath } from "url";
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
// Resolve workspace root (apps/app/scripts -> workspace root)
|
||||
// Resolve workspace root (apps/ui/scripts -> workspace root)
|
||||
const WORKSPACE_ROOT = path.resolve(__dirname, "../../..");
|
||||
const FIXTURE_PATH = path.join(WORKSPACE_ROOT, "test/fixtures/projectA");
|
||||
const SPEC_FILE_PATH = path.join(FIXTURE_PATH, ".automaker/app_spec.txt");
|
||||
|
||||
@@ -2,7 +2,6 @@ import { useState, useMemo, useEffect, useCallback, useRef } from "react";
|
||||
import { useNavigate, useLocation } from "@tanstack/react-router";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useAppStore, formatShortcut, type ThemeMode } from "@/store/app-store";
|
||||
import { CoursePromoBadge } from "@/components/ui/course-promo-badge";
|
||||
import {
|
||||
FolderOpen,
|
||||
Plus,
|
||||
@@ -1942,8 +1941,6 @@ export function Sidebar() {
|
||||
"bg-gradient-to-t from-background/10 via-sidebar/50 to-transparent"
|
||||
)}
|
||||
>
|
||||
{/* Course Promo Badge */}
|
||||
<CoursePromoBadge sidebarOpen={sidebarOpen} />
|
||||
{/* Wiki Link */}
|
||||
{!hideWiki && (
|
||||
<div className="p-2 pb-0">
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
|
||||
import * as React from "react";
|
||||
import { Sparkles, X } from "lucide-react";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip";
|
||||
import { useAppStore } from "@/store/app-store";
|
||||
|
||||
interface CoursePromoBadgeProps {
|
||||
sidebarOpen?: boolean;
|
||||
}
|
||||
|
||||
export function CoursePromoBadge({ sidebarOpen = true }: CoursePromoBadgeProps) {
|
||||
const [dismissed, setDismissed] = React.useState(false);
|
||||
const hideMarketingContent = useAppStore((state) => state.hideMarketingContent);
|
||||
|
||||
// If marketing content is hidden globally or dismissed locally, don't render
|
||||
if (hideMarketingContent || dismissed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Collapsed state - show only icon with tooltip
|
||||
if (!sidebarOpen) {
|
||||
return (
|
||||
<div className="p-2 pb-0 flex justify-center">
|
||||
<TooltipProvider delayDuration={300}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<a
|
||||
href="https://agenticjumpstart.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="group cursor-pointer flex items-center justify-center w-10 h-10 bg-primary/10 text-primary rounded-lg hover:bg-primary/20 transition-all border border-primary/30"
|
||||
data-testid="course-promo-badge-collapsed"
|
||||
>
|
||||
<Sparkles className="size-4 shrink-0" />
|
||||
</a>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="right" className="flex items-center gap-2">
|
||||
<span>Become a 10x Dev</span>
|
||||
<span
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setDismissed(true);
|
||||
}}
|
||||
className="p-0.5 rounded-full hover:bg-primary/30 transition-colors cursor-pointer"
|
||||
aria-label="Dismiss"
|
||||
>
|
||||
<X className="size-3" />
|
||||
</span>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Expanded state - show full badge
|
||||
return (
|
||||
<div className="p-2 pb-0">
|
||||
<a
|
||||
href="https://agenticjumpstart.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="group cursor-pointer flex items-center justify-between w-full px-2 lg:px-3 py-2.5 bg-primary/10 text-primary rounded-lg font-medium text-sm hover:bg-primary/20 transition-all border border-primary/30"
|
||||
data-testid="course-promo-badge"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<Sparkles className="size-4 shrink-0" />
|
||||
<span className="hidden lg:block">Become a 10x Dev</span>
|
||||
</div>
|
||||
<span
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setDismissed(true);
|
||||
}}
|
||||
className="hidden lg:block p-1 rounded-full hover:bg-primary/30 transition-colors cursor-pointer"
|
||||
aria-label="Dismiss"
|
||||
>
|
||||
<X className="size-3.5" />
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -37,8 +37,6 @@ export function SettingsView() {
|
||||
setShowProfilesOnly,
|
||||
muteDoneSound,
|
||||
setMuteDoneSound,
|
||||
hideMarketingContent,
|
||||
setHideMarketingContent,
|
||||
currentProject,
|
||||
moveProjectToTrash,
|
||||
defaultPlanningMode,
|
||||
@@ -104,9 +102,7 @@ export function SettingsView() {
|
||||
<AppearanceSection
|
||||
effectiveTheme={effectiveTheme}
|
||||
currentProject={settingsProject}
|
||||
hideMarketingContent={hideMarketingContent}
|
||||
onThemeChange={handleSetTheme}
|
||||
onHideMarketingContentChange={setHideMarketingContent}
|
||||
/>
|
||||
);
|
||||
case "keyboard":
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Palette, Megaphone } from "lucide-react";
|
||||
import { Palette } from "lucide-react";
|
||||
import { themeOptions } from "@/config/theme-options";
|
||||
import { cn } from "@/lib/utils";
|
||||
import type { Theme, Project } from "../shared/types";
|
||||
@@ -8,17 +7,13 @@ import type { Theme, Project } from "../shared/types";
|
||||
interface AppearanceSectionProps {
|
||||
effectiveTheme: Theme;
|
||||
currentProject: Project | null;
|
||||
hideMarketingContent: boolean;
|
||||
onThemeChange: (theme: Theme) => void;
|
||||
onHideMarketingContentChange: (hide: boolean) => void;
|
||||
}
|
||||
|
||||
export function AppearanceSection({
|
||||
effectiveTheme,
|
||||
currentProject,
|
||||
hideMarketingContent,
|
||||
onThemeChange,
|
||||
onHideMarketingContentChange,
|
||||
}: AppearanceSectionProps) {
|
||||
return (
|
||||
<div
|
||||
@@ -85,35 +80,6 @@ export function AppearanceSection({
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Separator */}
|
||||
<div className="border-t border-border/30 my-4" />
|
||||
|
||||
{/* Hide Marketing Content Setting */}
|
||||
<div className="group flex items-start space-x-3 p-3 rounded-xl hover:bg-accent/30 transition-colors duration-200 -mx-3">
|
||||
<Checkbox
|
||||
id="hide-marketing-content"
|
||||
checked={hideMarketingContent}
|
||||
onCheckedChange={(checked) =>
|
||||
onHideMarketingContentChange(checked === true)
|
||||
}
|
||||
className="mt-1"
|
||||
data-testid="hide-marketing-content-checkbox"
|
||||
/>
|
||||
<div className="space-y-1.5">
|
||||
<Label
|
||||
htmlFor="hide-marketing-content"
|
||||
className="text-foreground cursor-pointer font-medium flex items-center gap-2"
|
||||
>
|
||||
<Megaphone className="w-4 h-4 text-brand-500" />
|
||||
Hide marketing content
|
||||
</Label>
|
||||
<p className="text-xs text-muted-foreground/80 leading-relaxed">
|
||||
When enabled, hides promotional content like the "Become a 10x Dev" badge
|
||||
in the sidebar. This setting persists across sessions.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import { useState } from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
@@ -53,7 +52,9 @@ function CollapsibleSection({
|
||||
<div className="flex items-center justify-center w-8 h-8 rounded-lg bg-brand-500/10 text-brand-500">
|
||||
<Icon className="w-4 h-4" />
|
||||
</div>
|
||||
<span className="flex-1 font-medium text-foreground">{section.title}</span>
|
||||
<span className="flex-1 font-medium text-foreground">
|
||||
{section.title}
|
||||
</span>
|
||||
{isOpen ? (
|
||||
<ChevronDown className="w-4 h-4 text-muted-foreground" />
|
||||
) : (
|
||||
@@ -86,19 +87,30 @@ function CodeBlock({ children, title }: { children: string; title?: string }) {
|
||||
);
|
||||
}
|
||||
|
||||
function FeatureList({ items }: { items: { icon: React.ElementType; title: string; description: string }[] }) {
|
||||
function FeatureList({
|
||||
items,
|
||||
}: {
|
||||
items: { icon: React.ElementType; title: string; description: string }[];
|
||||
}) {
|
||||
return (
|
||||
<div className="grid gap-3 mt-3">
|
||||
{items.map((item, index) => {
|
||||
const ItemIcon = item.icon;
|
||||
return (
|
||||
<div key={index} className="flex items-start gap-3 p-3 rounded-lg bg-muted/30 border border-border/50">
|
||||
<div
|
||||
key={index}
|
||||
className="flex items-start gap-3 p-3 rounded-lg bg-muted/30 border border-border/50"
|
||||
>
|
||||
<div className="flex items-center justify-center w-6 h-6 rounded bg-brand-500/10 text-brand-500 shrink-0 mt-0.5">
|
||||
<ItemIcon className="w-3.5 h-3.5" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-medium text-foreground text-sm">{item.title}</div>
|
||||
<div className="text-xs text-muted-foreground mt-0.5">{item.description}</div>
|
||||
<div className="font-medium text-foreground text-sm">
|
||||
{item.title}
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground mt-0.5">
|
||||
{item.description}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -108,7 +120,9 @@ function FeatureList({ items }: { items: { icon: React.ElementType; title: strin
|
||||
}
|
||||
|
||||
export function WikiView() {
|
||||
const [openSections, setOpenSections] = useState<Set<string>>(new Set(["overview"]));
|
||||
const [openSections, setOpenSections] = useState<Set<string>>(
|
||||
new Set(["overview"])
|
||||
);
|
||||
|
||||
const toggleSection = (id: string) => {
|
||||
setOpenSections((prev) => {
|
||||
@@ -138,14 +152,21 @@ export function WikiView() {
|
||||
content: (
|
||||
<div className="space-y-3">
|
||||
<p>
|
||||
<strong className="text-foreground">Automaker</strong> is an autonomous AI development studio that helps developers build software faster using AI agents.
|
||||
<strong className="text-foreground">Automaker</strong> is an
|
||||
autonomous AI development studio that helps developers build
|
||||
software faster using AI agents.
|
||||
</p>
|
||||
<p>
|
||||
At its core, Automaker provides a visual Kanban board to manage features. When you're ready, AI agents automatically implement those features in your codebase, complete with git worktree isolation for safe parallel development.
|
||||
At its core, Automaker provides a visual Kanban board to manage
|
||||
features. When you're ready, AI agents automatically implement those
|
||||
features in your codebase, complete with git worktree isolation for
|
||||
safe parallel development.
|
||||
</p>
|
||||
<div className="p-3 rounded-lg bg-brand-500/10 border border-brand-500/20 mt-4">
|
||||
<p className="text-brand-400 text-sm">
|
||||
Think of it as having a team of AI developers that can work on multiple features simultaneously while you focus on the bigger picture.
|
||||
Think of it as having a team of AI developers that can work on
|
||||
multiple features simultaneously while you focus on the bigger
|
||||
picture.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -160,17 +181,21 @@ export function WikiView() {
|
||||
<p>Automaker is built as a monorepo with two main applications:</p>
|
||||
<ul className="list-disc list-inside space-y-2 ml-2">
|
||||
<li>
|
||||
<strong className="text-foreground">apps/app</strong> - Next.js + Electron frontend for the desktop application
|
||||
<strong className="text-foreground">apps/ui</strong> - Next.js +
|
||||
Electron frontend for the desktop application
|
||||
</li>
|
||||
<li>
|
||||
<strong className="text-foreground">apps/server</strong> - Express backend handling API requests and agent orchestration
|
||||
<strong className="text-foreground">apps/server</strong> - Express
|
||||
backend handling API requests and agent orchestration
|
||||
</li>
|
||||
</ul>
|
||||
<div className="mt-4 space-y-2">
|
||||
<p className="font-medium text-foreground">Key Technologies:</p>
|
||||
<ul className="list-disc list-inside space-y-1 ml-2">
|
||||
<li>Electron wraps Next.js for cross-platform desktop support</li>
|
||||
<li>Real-time communication via WebSocket for live agent updates</li>
|
||||
<li>
|
||||
Real-time communication via WebSocket for live agent updates
|
||||
</li>
|
||||
<li>State management with Zustand for reactive UI updates</li>
|
||||
<li>Claude Agent SDK for AI capabilities</li>
|
||||
</ul>
|
||||
@@ -189,42 +214,50 @@ export function WikiView() {
|
||||
{
|
||||
icon: LayoutGrid,
|
||||
title: "Kanban Board",
|
||||
description: "4 columns: Backlog, In Progress, Waiting Approval, Verified. Drag and drop to manage feature lifecycle.",
|
||||
description:
|
||||
"4 columns: Backlog, In Progress, Waiting Approval, Verified. Drag and drop to manage feature lifecycle.",
|
||||
},
|
||||
{
|
||||
icon: Bot,
|
||||
title: "AI Agent Integration",
|
||||
description: "Powered by Claude via the Agent SDK with full file, bash, and git access.",
|
||||
description:
|
||||
"Powered by Claude via the Agent SDK with full file, bash, and git access.",
|
||||
},
|
||||
{
|
||||
icon: Cpu,
|
||||
title: "Multi-Model Support",
|
||||
description: "Claude Haiku/Sonnet/Opus models. Choose the right model for each task.",
|
||||
description:
|
||||
"Claude Haiku/Sonnet/Opus models. Choose the right model for each task.",
|
||||
},
|
||||
{
|
||||
icon: Brain,
|
||||
title: "Extended Thinking",
|
||||
description: "Configurable thinking levels (none, low, medium, high, ultrathink) for complex tasks.",
|
||||
description:
|
||||
"Configurable thinking levels (none, low, medium, high, ultrathink) for complex tasks.",
|
||||
},
|
||||
{
|
||||
icon: Zap,
|
||||
title: "Real-time Streaming",
|
||||
description: "Watch AI agents work in real-time with live output streaming.",
|
||||
description:
|
||||
"Watch AI agents work in real-time with live output streaming.",
|
||||
},
|
||||
{
|
||||
icon: GitBranch,
|
||||
title: "Git Worktree Isolation",
|
||||
description: "Each feature runs in its own git worktree for safe parallel development.",
|
||||
description:
|
||||
"Each feature runs in its own git worktree for safe parallel development.",
|
||||
},
|
||||
{
|
||||
icon: Users,
|
||||
title: "AI Profiles",
|
||||
description: "Pre-configured model + thinking level combinations for different task types.",
|
||||
description:
|
||||
"Pre-configured model + thinking level combinations for different task types.",
|
||||
},
|
||||
{
|
||||
icon: Terminal,
|
||||
title: "Integrated Terminal",
|
||||
description: "Built-in terminal with tab support and split panes.",
|
||||
description:
|
||||
"Built-in terminal with tab support and split panes.",
|
||||
},
|
||||
{
|
||||
icon: Keyboard,
|
||||
@@ -234,7 +267,8 @@ export function WikiView() {
|
||||
{
|
||||
icon: Palette,
|
||||
title: "14 Themes",
|
||||
description: "From light to dark, retro to synthwave - pick your style.",
|
||||
description:
|
||||
"From light to dark, retro to synthwave - pick your style.",
|
||||
},
|
||||
{
|
||||
icon: Image,
|
||||
@@ -244,7 +278,8 @@ export function WikiView() {
|
||||
{
|
||||
icon: TestTube,
|
||||
title: "Test Integration",
|
||||
description: "Automatic test running and TDD support for quality assurance.",
|
||||
description:
|
||||
"Automatic test running and TDD support for quality assurance.",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
@@ -257,39 +292,63 @@ export function WikiView() {
|
||||
icon: GitBranch,
|
||||
content: (
|
||||
<div className="space-y-3">
|
||||
<p>Here's what happens when you use Automaker to implement a feature:</p>
|
||||
<p>
|
||||
Here's what happens when you use Automaker to implement a feature:
|
||||
</p>
|
||||
<ol className="list-decimal list-inside space-y-3 ml-2 mt-4">
|
||||
<li className="text-foreground">
|
||||
<strong>Create Feature</strong>
|
||||
<p className="text-muted-foreground ml-5 mt-1">Add a new feature card to the Kanban board with description and steps</p>
|
||||
<p className="text-muted-foreground ml-5 mt-1">
|
||||
Add a new feature card to the Kanban board with description and
|
||||
steps
|
||||
</p>
|
||||
</li>
|
||||
<li className="text-foreground">
|
||||
<strong>Feature Saved</strong>
|
||||
<p className="text-muted-foreground ml-5 mt-1">Feature saved to <code className="px-1 py-0.5 bg-muted rounded text-xs">.automaker/features/{id}/feature.json</code></p>
|
||||
<p className="text-muted-foreground ml-5 mt-1">
|
||||
Feature saved to{" "}
|
||||
<code className="px-1 py-0.5 bg-muted rounded text-xs">
|
||||
.automaker/features/{id}/feature.json
|
||||
</code>
|
||||
</p>
|
||||
</li>
|
||||
<li className="text-foreground">
|
||||
<strong>Start Work</strong>
|
||||
<p className="text-muted-foreground ml-5 mt-1">Drag to "In Progress" or enable auto mode to start implementation</p>
|
||||
<p className="text-muted-foreground ml-5 mt-1">
|
||||
Drag to "In Progress" or enable auto mode to start
|
||||
implementation
|
||||
</p>
|
||||
</li>
|
||||
<li className="text-foreground">
|
||||
<strong>Git Worktree Created</strong>
|
||||
<p className="text-muted-foreground ml-5 mt-1">Backend AutoModeService creates isolated git worktree (if enabled)</p>
|
||||
<p className="text-muted-foreground ml-5 mt-1">
|
||||
Backend AutoModeService creates isolated git worktree (if
|
||||
enabled)
|
||||
</p>
|
||||
</li>
|
||||
<li className="text-foreground">
|
||||
<strong>Agent Executes</strong>
|
||||
<p className="text-muted-foreground ml-5 mt-1">Claude Agent SDK runs with file/bash/git tool access</p>
|
||||
<p className="text-muted-foreground ml-5 mt-1">
|
||||
Claude Agent SDK runs with file/bash/git tool access
|
||||
</p>
|
||||
</li>
|
||||
<li className="text-foreground">
|
||||
<strong>Progress Streamed</strong>
|
||||
<p className="text-muted-foreground ml-5 mt-1">Real-time updates via WebSocket as agent works</p>
|
||||
<p className="text-muted-foreground ml-5 mt-1">
|
||||
Real-time updates via WebSocket as agent works
|
||||
</p>
|
||||
</li>
|
||||
<li className="text-foreground">
|
||||
<strong>Completion</strong>
|
||||
<p className="text-muted-foreground ml-5 mt-1">On success, feature moves to "waiting_approval" for your review</p>
|
||||
<p className="text-muted-foreground ml-5 mt-1">
|
||||
On success, feature moves to "waiting_approval" for your review
|
||||
</p>
|
||||
</li>
|
||||
<li className="text-foreground">
|
||||
<strong>Verify</strong>
|
||||
<p className="text-muted-foreground ml-5 mt-1">Review changes and move to "verified" when satisfied</p>
|
||||
<p className="text-muted-foreground ml-5 mt-1">
|
||||
Review changes and move to "verified" when satisfied
|
||||
</p>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
@@ -301,9 +360,11 @@ export function WikiView() {
|
||||
icon: FolderTree,
|
||||
content: (
|
||||
<div>
|
||||
<p className="mb-3">The Automaker codebase is organized as follows:</p>
|
||||
<p className="mb-3">
|
||||
The Automaker codebase is organized as follows:
|
||||
</p>
|
||||
<CodeBlock title="Directory Structure">
|
||||
{`/automaker/
|
||||
{`/automaker/
|
||||
├── apps/
|
||||
│ ├── app/ # Frontend (Next.js + Electron)
|
||||
│ │ ├── electron/ # Electron main process
|
||||
@@ -332,18 +393,46 @@ export function WikiView() {
|
||||
<p>The main UI components that make up Automaker:</p>
|
||||
<div className="grid gap-2 mt-4">
|
||||
{[
|
||||
{ file: "sidebar.tsx", desc: "Main navigation with project picker and view switching" },
|
||||
{ file: "board-view.tsx", desc: "Kanban board with drag-and-drop cards" },
|
||||
{ file: "agent-view.tsx", desc: "AI chat interface for conversational development" },
|
||||
{
|
||||
file: "sidebar.tsx",
|
||||
desc: "Main navigation with project picker and view switching",
|
||||
},
|
||||
{
|
||||
file: "board-view.tsx",
|
||||
desc: "Kanban board with drag-and-drop cards",
|
||||
},
|
||||
{
|
||||
file: "agent-view.tsx",
|
||||
desc: "AI chat interface for conversational development",
|
||||
},
|
||||
{ file: "spec-view.tsx", desc: "Project specification editor" },
|
||||
{ file: "context-view.tsx", desc: "Context file manager for AI context" },
|
||||
{ file: "terminal-view.tsx", desc: "Integrated terminal with splits and tabs" },
|
||||
{ file: "profiles-view.tsx", desc: "AI profile management (model + thinking presets)" },
|
||||
{ file: "app-store.ts", desc: "Central Zustand state management" },
|
||||
{
|
||||
file: "context-view.tsx",
|
||||
desc: "Context file manager for AI context",
|
||||
},
|
||||
{
|
||||
file: "terminal-view.tsx",
|
||||
desc: "Integrated terminal with splits and tabs",
|
||||
},
|
||||
{
|
||||
file: "profiles-view.tsx",
|
||||
desc: "AI profile management (model + thinking presets)",
|
||||
},
|
||||
{
|
||||
file: "app-store.ts",
|
||||
desc: "Central Zustand state management",
|
||||
},
|
||||
].map((item) => (
|
||||
<div key={item.file} className="flex items-center gap-3 p-2 rounded bg-muted/30 border border-border/50">
|
||||
<code className="text-xs font-mono text-brand-400 bg-brand-500/10 px-2 py-0.5 rounded">{item.file}</code>
|
||||
<span className="text-xs text-muted-foreground">{item.desc}</span>
|
||||
<div
|
||||
key={item.file}
|
||||
className="flex items-center gap-3 p-2 rounded bg-muted/30 border border-border/50"
|
||||
>
|
||||
<code className="text-xs font-mono text-brand-400 bg-brand-500/10 px-2 py-0.5 rounded">
|
||||
{item.file}
|
||||
</code>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{item.desc}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -356,21 +445,45 @@ export function WikiView() {
|
||||
icon: Settings,
|
||||
content: (
|
||||
<div className="space-y-3">
|
||||
<p>Automaker stores project configuration in the <code className="px-1 py-0.5 bg-muted rounded text-xs">.automaker/</code> directory:</p>
|
||||
<p>
|
||||
Automaker stores project configuration in the{" "}
|
||||
<code className="px-1 py-0.5 bg-muted rounded text-xs">
|
||||
.automaker/
|
||||
</code>{" "}
|
||||
directory:
|
||||
</p>
|
||||
<div className="grid gap-2 mt-4">
|
||||
{[
|
||||
{ file: "app_spec.txt", desc: "Project specification describing your app for AI context" },
|
||||
{ file: "context/", desc: "Additional context files (docs, examples) for AI" },
|
||||
{ file: "features/", desc: "Feature definitions with descriptions and steps" },
|
||||
{
|
||||
file: "app_spec.txt",
|
||||
desc: "Project specification describing your app for AI context",
|
||||
},
|
||||
{
|
||||
file: "context/",
|
||||
desc: "Additional context files (docs, examples) for AI",
|
||||
},
|
||||
{
|
||||
file: "features/",
|
||||
desc: "Feature definitions with descriptions and steps",
|
||||
},
|
||||
].map((item) => (
|
||||
<div key={item.file} className="flex items-center gap-3 p-2 rounded bg-muted/30 border border-border/50">
|
||||
<code className="text-xs font-mono text-brand-400 bg-brand-500/10 px-2 py-0.5 rounded">{item.file}</code>
|
||||
<span className="text-xs text-muted-foreground">{item.desc}</span>
|
||||
<div
|
||||
key={item.file}
|
||||
className="flex items-center gap-3 p-2 rounded bg-muted/30 border border-border/50"
|
||||
>
|
||||
<code className="text-xs font-mono text-brand-400 bg-brand-500/10 px-2 py-0.5 rounded">
|
||||
{item.file}
|
||||
</code>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{item.desc}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-4 p-3 rounded-lg bg-muted/30 border border-border/50">
|
||||
<p className="text-sm text-foreground font-medium mb-2">Tip: App Spec Best Practices</p>
|
||||
<p className="text-sm text-foreground font-medium mb-2">
|
||||
Tip: App Spec Best Practices
|
||||
</p>
|
||||
<ul className="list-disc list-inside space-y-1 text-xs text-muted-foreground">
|
||||
<li>Include your tech stack and key dependencies</li>
|
||||
<li>Describe the project structure and conventions</li>
|
||||
@@ -391,39 +504,68 @@ export function WikiView() {
|
||||
<ol className="list-decimal list-inside space-y-4 ml-2 mt-4">
|
||||
<li className="text-foreground">
|
||||
<strong>Create or Open a Project</strong>
|
||||
<p className="text-muted-foreground ml-5 mt-1">Use the sidebar to create a new project or open an existing folder</p>
|
||||
<p className="text-muted-foreground ml-5 mt-1">
|
||||
Use the sidebar to create a new project or open an existing
|
||||
folder
|
||||
</p>
|
||||
</li>
|
||||
<li className="text-foreground">
|
||||
<strong>Write an App Spec</strong>
|
||||
<p className="text-muted-foreground ml-5 mt-1">Go to Spec Editor and describe your project. This helps AI understand your codebase.</p>
|
||||
<p className="text-muted-foreground ml-5 mt-1">
|
||||
Go to Spec Editor and describe your project. This helps AI
|
||||
understand your codebase.
|
||||
</p>
|
||||
</li>
|
||||
<li className="text-foreground">
|
||||
<strong>Add Context (Optional)</strong>
|
||||
<p className="text-muted-foreground ml-5 mt-1">Add relevant documentation or examples to the Context view for better AI results</p>
|
||||
<p className="text-muted-foreground ml-5 mt-1">
|
||||
Add relevant documentation or examples to the Context view for
|
||||
better AI results
|
||||
</p>
|
||||
</li>
|
||||
<li className="text-foreground">
|
||||
<strong>Create Features</strong>
|
||||
<p className="text-muted-foreground ml-5 mt-1">Add feature cards to your Kanban board with clear descriptions and implementation steps</p>
|
||||
<p className="text-muted-foreground ml-5 mt-1">
|
||||
Add feature cards to your Kanban board with clear descriptions
|
||||
and implementation steps
|
||||
</p>
|
||||
</li>
|
||||
<li className="text-foreground">
|
||||
<strong>Configure AI Profile</strong>
|
||||
<p className="text-muted-foreground ml-5 mt-1">Choose an AI profile or customize model/thinking settings per feature</p>
|
||||
<p className="text-muted-foreground ml-5 mt-1">
|
||||
Choose an AI profile or customize model/thinking settings per
|
||||
feature
|
||||
</p>
|
||||
</li>
|
||||
<li className="text-foreground">
|
||||
<strong>Start Implementation</strong>
|
||||
<p className="text-muted-foreground ml-5 mt-1">Drag features to "In Progress" or enable auto mode to let AI work</p>
|
||||
<p className="text-muted-foreground ml-5 mt-1">
|
||||
Drag features to "In Progress" or enable auto mode to let AI
|
||||
work
|
||||
</p>
|
||||
</li>
|
||||
<li className="text-foreground">
|
||||
<strong>Review and Verify</strong>
|
||||
<p className="text-muted-foreground ml-5 mt-1">Check completed features, review changes, and mark as verified</p>
|
||||
<p className="text-muted-foreground ml-5 mt-1">
|
||||
Check completed features, review changes, and mark as verified
|
||||
</p>
|
||||
</li>
|
||||
</ol>
|
||||
<div className="mt-6 p-4 rounded-lg bg-brand-500/10 border border-brand-500/20">
|
||||
<p className="text-brand-400 text-sm font-medium mb-2">Pro Tips:</p>
|
||||
<ul className="list-disc list-inside space-y-1 text-xs text-brand-400/80">
|
||||
<li>Use keyboard shortcuts for faster navigation (press <code className="px-1 py-0.5 bg-brand-500/20 rounded">?</code> to see all)</li>
|
||||
<li>Enable git worktree isolation for parallel feature development</li>
|
||||
<li>Start with "Quick Edit" profile for simple tasks, use "Heavy Task" for complex work</li>
|
||||
<li>
|
||||
Use keyboard shortcuts for faster navigation (press{" "}
|
||||
<code className="px-1 py-0.5 bg-brand-500/20 rounded">?</code>{" "}
|
||||
to see all)
|
||||
</li>
|
||||
<li>
|
||||
Enable git worktree isolation for parallel feature development
|
||||
</li>
|
||||
<li>
|
||||
Start with "Quick Edit" profile for simple tasks, use "Heavy
|
||||
Task" for complex work
|
||||
</li>
|
||||
<li>Keep your app spec up to date as your project evolves</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -459,9 +459,6 @@ export interface AppState {
|
||||
// Audio Settings
|
||||
muteDoneSound: boolean; // When true, mute the notification sound when agents complete (default: false)
|
||||
|
||||
// Marketing Settings
|
||||
hideMarketingContent: boolean; // When true, hide marketing content like the "Become a 10x Dev" badge (default: false)
|
||||
|
||||
// Enhancement Model Settings
|
||||
enhancementModel: AgentModel; // Model used for feature enhancement (default: sonnet)
|
||||
|
||||
@@ -673,9 +670,6 @@ export interface AppActions {
|
||||
// Audio Settings actions
|
||||
setMuteDoneSound: (muted: boolean) => void;
|
||||
|
||||
// Marketing Settings actions
|
||||
setHideMarketingContent: (hide: boolean) => void;
|
||||
|
||||
// Enhancement Model actions
|
||||
setEnhancementModel: (model: AgentModel) => void;
|
||||
|
||||
@@ -830,7 +824,6 @@ const initialState: AppState = {
|
||||
showProfilesOnly: false, // Default to showing all options (not profiles only)
|
||||
keyboardShortcuts: DEFAULT_KEYBOARD_SHORTCUTS, // Default keyboard shortcuts
|
||||
muteDoneSound: false, // Default to sound enabled (not muted)
|
||||
hideMarketingContent: false, // Default to showing marketing content
|
||||
enhancementModel: "sonnet", // Default to sonnet for feature enhancement
|
||||
aiProfiles: DEFAULT_AI_PROFILES,
|
||||
projectAnalysis: null,
|
||||
@@ -1494,9 +1487,6 @@ export const useAppStore = create<AppState & AppActions>()(
|
||||
// Audio Settings actions
|
||||
setMuteDoneSound: (muted) => set({ muteDoneSound: muted }),
|
||||
|
||||
// Marketing Settings actions
|
||||
setHideMarketingContent: (hide) => set({ hideMarketingContent: hide }),
|
||||
|
||||
// Enhancement Model actions
|
||||
setEnhancementModel: (model) => set({ enhancementModel: model }),
|
||||
|
||||
@@ -2341,7 +2331,6 @@ export const useAppStore = create<AppState & AppActions>()(
|
||||
showProfilesOnly: state.showProfilesOnly,
|
||||
keyboardShortcuts: state.keyboardShortcuts,
|
||||
muteDoneSound: state.muteDoneSound,
|
||||
hideMarketingContent: state.hideMarketingContent,
|
||||
enhancementModel: state.enhancementModel,
|
||||
// Profiles and sessions
|
||||
aiProfiles: state.aiProfiles,
|
||||
|
||||
@@ -110,8 +110,8 @@ test.describe("Kanban Responsive Scaling Tests", () => {
|
||||
expect(Math.abs(columnWidth - baseWidth)).toBeLessThan(2);
|
||||
}
|
||||
|
||||
// Column width should be within expected bounds (240px min, 360px max)
|
||||
expect(baseWidth).toBeGreaterThanOrEqual(240);
|
||||
// Column width should be within expected bounds (280px min, 360px max)
|
||||
expect(baseWidth).toBeGreaterThanOrEqual(280);
|
||||
expect(baseWidth).toBeLessThanOrEqual(360);
|
||||
|
||||
// Columns should not overlap (check x positions)
|
||||
@@ -149,9 +149,30 @@ test.describe("Kanban Responsive Scaling Tests", () => {
|
||||
expect(verifiedBox).not.toBeNull();
|
||||
|
||||
if (backlogBox && verifiedBox) {
|
||||
// Calculate the left and right margins
|
||||
const leftMargin = backlogBox.x;
|
||||
const rightMargin = 1600 - (verifiedBox.x + verifiedBox.width);
|
||||
// Get the actual container width (accounting for sidebar)
|
||||
// The board-view container is inside a flex container that accounts for sidebar
|
||||
const containerWidth = await page.evaluate(() => {
|
||||
const boardView = document.querySelector('[data-testid="board-view"]');
|
||||
if (!boardView) return window.innerWidth;
|
||||
const parent = boardView.parentElement;
|
||||
return parent ? parent.clientWidth : window.innerWidth;
|
||||
});
|
||||
|
||||
// Calculate the left and right margins relative to the container
|
||||
// The bounding box x is relative to the viewport, so we need to find where
|
||||
// the container starts relative to the viewport
|
||||
const containerLeft = await page.evaluate(() => {
|
||||
const boardView = document.querySelector('[data-testid="board-view"]');
|
||||
if (!boardView) return 0;
|
||||
const parent = boardView.parentElement;
|
||||
if (!parent) return 0;
|
||||
const rect = parent.getBoundingClientRect();
|
||||
return rect.left;
|
||||
});
|
||||
|
||||
// Calculate margins relative to the container
|
||||
const leftMargin = backlogBox.x - containerLeft;
|
||||
const rightMargin = containerWidth - (verifiedBox.x + verifiedBox.width - containerLeft);
|
||||
|
||||
// The margins should be roughly equal (columns are centered)
|
||||
// Allow for some tolerance due to padding and gaps
|
||||
|
||||
@@ -1,184 +0,0 @@
|
||||
/**
|
||||
* Settings Marketing Content Toggle Tests
|
||||
*
|
||||
* Tests for the "Hide marketing content" setting in the Appearance section.
|
||||
*/
|
||||
|
||||
import { test, expect } from "@playwright/test";
|
||||
import * as fs from "fs";
|
||||
|
||||
import {
|
||||
waitForNetworkIdle,
|
||||
createTestGitRepo,
|
||||
cleanupTempDir,
|
||||
createTempDirPath,
|
||||
setupProjectWithPathNoWorktrees,
|
||||
navigateToSettings,
|
||||
} from "./utils";
|
||||
|
||||
// Create unique temp dir for this test run
|
||||
const TEST_TEMP_DIR = createTempDirPath("settings-marketing-tests");
|
||||
|
||||
interface TestRepo {
|
||||
path: string;
|
||||
cleanup: () => Promise<void>;
|
||||
}
|
||||
|
||||
// Configure all tests to run serially
|
||||
test.describe.configure({ mode: "serial" });
|
||||
|
||||
test.describe("Settings Marketing Content Tests", () => {
|
||||
let testRepo: TestRepo;
|
||||
|
||||
test.beforeAll(async () => {
|
||||
// Create test temp directory
|
||||
if (!fs.existsSync(TEST_TEMP_DIR)) {
|
||||
fs.mkdirSync(TEST_TEMP_DIR, { recursive: true });
|
||||
}
|
||||
});
|
||||
|
||||
test.beforeEach(async () => {
|
||||
// Create a fresh test repo for each test
|
||||
testRepo = await createTestGitRepo(TEST_TEMP_DIR);
|
||||
});
|
||||
|
||||
test.afterEach(async () => {
|
||||
// Cleanup test repo after each test
|
||||
if (testRepo) {
|
||||
await testRepo.cleanup();
|
||||
}
|
||||
});
|
||||
|
||||
test.afterAll(async () => {
|
||||
// Cleanup temp directory
|
||||
cleanupTempDir(TEST_TEMP_DIR);
|
||||
});
|
||||
|
||||
test("should show course promo badge by default", async ({ page }) => {
|
||||
// Setup project without worktrees for simpler testing
|
||||
await setupProjectWithPathNoWorktrees(page, testRepo.path);
|
||||
await page.goto("/");
|
||||
await waitForNetworkIdle(page);
|
||||
|
||||
// Wait for sidebar to load
|
||||
await expect(page.locator('[data-testid="sidebar"]')).toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
// Course promo badge should be visible by default
|
||||
const promoBadge = page.locator('[data-testid="course-promo-badge"]');
|
||||
await expect(promoBadge).toBeVisible({ timeout: 5000 });
|
||||
});
|
||||
|
||||
test("should hide course promo badge when setting is enabled", async ({
|
||||
page,
|
||||
}) => {
|
||||
// Setup project
|
||||
await setupProjectWithPathNoWorktrees(page, testRepo.path);
|
||||
await page.goto("/");
|
||||
await waitForNetworkIdle(page);
|
||||
|
||||
// Navigate to settings
|
||||
await navigateToSettings(page);
|
||||
|
||||
// Click on Appearance tab in settings navigation
|
||||
const appearanceTab = page.getByRole("button", { name: /appearance/i });
|
||||
await appearanceTab.click();
|
||||
|
||||
// Find and click the hide marketing content checkbox
|
||||
const hideMarketingCheckbox = page.locator(
|
||||
'[data-testid="hide-marketing-content-checkbox"]'
|
||||
);
|
||||
await expect(hideMarketingCheckbox).toBeVisible({ timeout: 5000 });
|
||||
await hideMarketingCheckbox.click();
|
||||
|
||||
// Navigate back to board to see the sidebar
|
||||
await page.goto("/board");
|
||||
await waitForNetworkIdle(page);
|
||||
|
||||
// Wait for Zustand store to rehydrate from localStorage
|
||||
await page.waitForFunction(() => {
|
||||
const storage = localStorage.getItem('automaker-storage');
|
||||
if (!storage) return false;
|
||||
const parsed = JSON.parse(storage);
|
||||
return parsed.state?.hideMarketingContent === true;
|
||||
});
|
||||
|
||||
// Course promo badge should now be hidden
|
||||
const promoBadge = page.locator('[data-testid="course-promo-badge"]');
|
||||
await expect(promoBadge).not.toBeVisible({ timeout: 5000 });
|
||||
});
|
||||
|
||||
test("should persist hide marketing setting across page reloads", async ({
|
||||
page,
|
||||
}) => {
|
||||
// Setup project
|
||||
await setupProjectWithPathNoWorktrees(page, testRepo.path);
|
||||
await page.goto("/");
|
||||
await waitForNetworkIdle(page);
|
||||
|
||||
// Navigate to settings and enable hide marketing
|
||||
await navigateToSettings(page);
|
||||
|
||||
const appearanceTab = page.getByRole("button", { name: /appearance/i });
|
||||
await appearanceTab.click();
|
||||
|
||||
const hideMarketingCheckbox = page.locator(
|
||||
'[data-testid="hide-marketing-content-checkbox"]'
|
||||
);
|
||||
await hideMarketingCheckbox.click();
|
||||
|
||||
// Reload the page
|
||||
await page.reload();
|
||||
await waitForNetworkIdle(page);
|
||||
|
||||
// Course promo badge should still be hidden after reload
|
||||
const promoBadge = page.locator('[data-testid="course-promo-badge"]');
|
||||
await expect(promoBadge).not.toBeVisible({ timeout: 5000 });
|
||||
});
|
||||
|
||||
test("should show course promo badge again when setting is disabled", async ({
|
||||
page,
|
||||
}) => {
|
||||
// Setup project with hide marketing already enabled via localStorage
|
||||
await page.addInitScript(() => {
|
||||
const state = {
|
||||
state: {
|
||||
hideMarketingContent: true,
|
||||
projects: [],
|
||||
currentProject: null,
|
||||
theme: "dark",
|
||||
sidebarOpen: true,
|
||||
},
|
||||
version: 2,
|
||||
};
|
||||
localStorage.setItem("automaker-storage", JSON.stringify(state));
|
||||
});
|
||||
|
||||
await setupProjectWithPathNoWorktrees(page, testRepo.path);
|
||||
await page.goto("/");
|
||||
await waitForNetworkIdle(page);
|
||||
|
||||
// Verify promo is hidden initially
|
||||
const promoBadge = page.locator('[data-testid="course-promo-badge"]');
|
||||
await expect(promoBadge).not.toBeVisible({ timeout: 5000 });
|
||||
|
||||
// Navigate to settings and disable hide marketing
|
||||
await navigateToSettings(page);
|
||||
|
||||
const appearanceTab = page.getByRole("button", { name: /appearance/i });
|
||||
await appearanceTab.click();
|
||||
|
||||
const hideMarketingCheckbox = page.locator(
|
||||
'[data-testid="hide-marketing-content-checkbox"]'
|
||||
);
|
||||
await hideMarketingCheckbox.click(); // Uncheck
|
||||
|
||||
// Navigate back to board
|
||||
await page.goto("/board");
|
||||
await waitForNetworkIdle(page);
|
||||
|
||||
// Course promo badge should now be visible again
|
||||
await expect(promoBadge).toBeVisible({ timeout: 5000 });
|
||||
});
|
||||
});
|
||||
@@ -799,10 +799,14 @@ test.describe("Worktree Integration Tests", () => {
|
||||
await clickAddFeature(page);
|
||||
|
||||
// Fill in the feature details with the new branch
|
||||
await fillAddFeatureDialog(page, "Feature that should auto-create worktree", {
|
||||
branch: branchName,
|
||||
category: "Testing",
|
||||
});
|
||||
await fillAddFeatureDialog(
|
||||
page,
|
||||
"Feature that should auto-create worktree",
|
||||
{
|
||||
branch: branchName,
|
||||
category: "Testing",
|
||||
}
|
||||
);
|
||||
|
||||
// Confirm
|
||||
await confirmAddFeature(page);
|
||||
@@ -835,7 +839,7 @@ test.describe("Worktree Integration Tests", () => {
|
||||
|
||||
const featureFilePath = path.join(featuresDir, featureDir!, "feature.json");
|
||||
const featureData = JSON.parse(fs.readFileSync(featureFilePath, "utf-8"));
|
||||
|
||||
|
||||
// Verify branch name is stored
|
||||
expect(featureData.branchName).toBe(branchName);
|
||||
|
||||
@@ -900,7 +904,7 @@ test.describe("Worktree Integration Tests", () => {
|
||||
|
||||
let featureFilePath = path.join(featuresDir, featureDir!, "feature.json");
|
||||
let featureData = JSON.parse(fs.readFileSync(featureFilePath, "utf-8"));
|
||||
|
||||
|
||||
// Verify feature was created with the branch name stored
|
||||
expect(featureData.branchName).toBe(branchName);
|
||||
// Verify worktreePath is NOT set (worktrees are created at execution time, not when adding)
|
||||
@@ -1084,7 +1088,9 @@ test.describe("Worktree Integration Tests", () => {
|
||||
// When a worktree is selected, "Use current selected branch" should be selected
|
||||
// and the branch name should be shown in the label
|
||||
const currentBranchLabel = page.locator('label[for="feature-current"]');
|
||||
await expect(currentBranchLabel).toContainText(branchName, { timeout: 5000 });
|
||||
await expect(currentBranchLabel).toContainText(branchName, {
|
||||
timeout: 5000,
|
||||
});
|
||||
|
||||
// Close dialog
|
||||
await page.keyboard.press("Escape");
|
||||
@@ -1275,11 +1281,7 @@ test.describe("Worktree Integration Tests", () => {
|
||||
expect(featureDir).toBeDefined();
|
||||
|
||||
// Read the feature data
|
||||
const featureFilePath = path.join(
|
||||
featuresDir,
|
||||
featureDir!,
|
||||
"feature.json"
|
||||
);
|
||||
const featureFilePath = path.join(featuresDir, featureDir!, "feature.json");
|
||||
const featureData = JSON.parse(fs.readFileSync(featureFilePath, "utf-8"));
|
||||
expect(featureData.status).toBe("backlog");
|
||||
|
||||
@@ -1296,9 +1298,7 @@ test.describe("Worktree Integration Tests", () => {
|
||||
|
||||
// Wait for the feature to move to in_progress column
|
||||
await expect(async () => {
|
||||
const updatedData = JSON.parse(
|
||||
fs.readFileSync(featureFilePath, "utf-8")
|
||||
);
|
||||
const updatedData = JSON.parse(fs.readFileSync(featureFilePath, "utf-8"));
|
||||
expect(updatedData.status).toBe("in_progress");
|
||||
}).toPass({ timeout: 10000 });
|
||||
|
||||
@@ -1911,7 +1911,10 @@ test.describe("Worktree Integration Tests", () => {
|
||||
await apiCreateWorktree(page, testRepo.path, branchName);
|
||||
|
||||
// Add a file and commit in the worktree
|
||||
fs.writeFileSync(path.join(worktreePath, "merge-file.txt"), "merge content");
|
||||
fs.writeFileSync(
|
||||
path.join(worktreePath, "merge-file.txt"),
|
||||
"merge content"
|
||||
);
|
||||
await execAsync("git add merge-file.txt", { cwd: worktreePath });
|
||||
await execAsync('git commit -m "Add file for merge test"', {
|
||||
cwd: worktreePath,
|
||||
@@ -2065,9 +2068,9 @@ test.describe("Worktree Integration Tests", () => {
|
||||
|
||||
// Verify the worktree has the file from develop
|
||||
const worktreePath = getWorktreePath(testRepo.path, "feature/from-develop");
|
||||
expect(
|
||||
fs.existsSync(path.join(worktreePath, "develop-only.txt"))
|
||||
).toBe(true);
|
||||
expect(fs.existsSync(path.join(worktreePath, "develop-only.txt"))).toBe(
|
||||
true
|
||||
);
|
||||
const content = fs.readFileSync(
|
||||
path.join(worktreePath, "develop-only.txt"),
|
||||
"utf-8"
|
||||
@@ -2100,10 +2103,9 @@ test.describe("Worktree Integration Tests", () => {
|
||||
|
||||
// Verify the worktree starts from the same commit as main
|
||||
const worktreePath = getWorktreePath(testRepo.path, "feature/from-head");
|
||||
const { stdout: worktreeHash } = await execAsync(
|
||||
"git rev-parse HEAD~0",
|
||||
{ cwd: worktreePath }
|
||||
);
|
||||
const { stdout: worktreeHash } = await execAsync("git rev-parse HEAD~0", {
|
||||
cwd: worktreePath,
|
||||
});
|
||||
|
||||
// The worktree's initial commit should be the same as main's HEAD
|
||||
// (Since it was just created, we check the parent commit)
|
||||
@@ -2395,9 +2397,9 @@ test.describe("Worktree Integration Tests", () => {
|
||||
let featureData = JSON.parse(fs.readFileSync(featureFilePath, "utf-8"));
|
||||
|
||||
// Initially, the feature should be on main or have no branch set
|
||||
expect(
|
||||
!featureData.branchName || featureData.branchName === "main"
|
||||
).toBe(true);
|
||||
expect(!featureData.branchName || featureData.branchName === "main").toBe(
|
||||
true
|
||||
);
|
||||
|
||||
// The new branch we want to assign
|
||||
const newBranchName = "feature/edited-branch";
|
||||
@@ -2428,7 +2430,7 @@ test.describe("Worktree Integration Tests", () => {
|
||||
await page.waitForTimeout(300);
|
||||
|
||||
// Type the new branch name
|
||||
const commandInput = page.locator('[cmdk-input]');
|
||||
const commandInput = page.locator("[cmdk-input]");
|
||||
await commandInput.fill(newBranchName);
|
||||
|
||||
// Press Enter to select/create the branch
|
||||
@@ -2519,7 +2521,7 @@ test.describe("Worktree Integration Tests", () => {
|
||||
await page.waitForTimeout(300);
|
||||
|
||||
// Type "main" to change to main branch
|
||||
const commandInput = page.locator('[cmdk-input]');
|
||||
const commandInput = page.locator("[cmdk-input]");
|
||||
await commandInput.fill("main");
|
||||
await commandInput.press("Enter");
|
||||
await page.waitForTimeout(200);
|
||||
@@ -2577,7 +2579,7 @@ test.describe("Worktree Integration Tests", () => {
|
||||
await branchInput.click();
|
||||
await page.waitForTimeout(300);
|
||||
|
||||
const commandInput = page.locator('[cmdk-input]');
|
||||
const commandInput = page.locator("[cmdk-input]");
|
||||
await commandInput.fill(existingBranch);
|
||||
await commandInput.press("Enter");
|
||||
await page.waitForTimeout(200);
|
||||
|
||||
Reference in New Issue
Block a user