Commit 0ae29181 authored by Vũ Hoàng Anh's avatar Vũ Hoàng Anh

feat(orca): integrate Orca Agent Fleet Git Worktrees into sidebar and clone repo

parent cbdbd078
Pipeline #3920 failed with stage
...@@ -155,6 +155,7 @@ import { SidebarDestination } from "./sidebar-destination"; ...@@ -155,6 +155,7 @@ import { SidebarDestination } from "./sidebar-destination";
import { SessionTitle } from "./session-title"; import { SessionTitle } from "./session-title";
import { ChannelNavigation } from "./channel-navigation"; import { ChannelNavigation } from "./channel-navigation";
import { WorkforceList } from "./workforce-list"; import { WorkforceList } from "./workforce-list";
import { OrcaFleetList } from "./orca-fleet-list";
import { MOCK_AGENTS, MOCK_CHANNELS, type ChannelItem } from "../../../../app/lib/mock-data"; import { MOCK_AGENTS, MOCK_CHANNELS, type ChannelItem } from "../../../../app/lib/mock-data";
import { mockOpencodeStore } from "../../../../app/lib/mock-opencode"; import { mockOpencodeStore } from "../../../../app/lib/mock-opencode";
...@@ -1261,6 +1262,7 @@ export function AppSidebar(props: AppSidebarProps) { ...@@ -1261,6 +1262,7 @@ export function AppSidebar(props: AppSidebarProps) {
<WorkforceList <WorkforceList
agents={workforceAgents} agents={workforceAgents}
/> />
<OrcaFleetList />
{pinnedSessions.length > 0 ? ( {pinnedSessions.length > 0 ? (
<GlobalPinnedSessions entries={pinnedSessions} /> <GlobalPinnedSessions entries={pinnedSessions} />
) : null} ) : null}
......
/** @jsxImportSource react */
import * as React from "react";
import {
GitBranch,
GitFork,
ChevronDown,
Plus,
Play,
CheckCircle2,
Clock,
Sparkles,
Layers,
Terminal,
ExternalLink,
} from "lucide-react";
import { cn } from "@/lib/utils";
import {
SidebarGroup,
SidebarGroupContent,
SidebarMenu,
SidebarMenuItem,
SidebarMenuButton,
} from "@/components/ui/sidebar";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";
import {
SIDEBAR_SECTION_LABEL,
SIDEBAR_SECTION_LANE,
SidebarGlyphSlot,
} from "./sidebar-lanes";
export interface OrcaWorktreeItem {
id: string;
branch: string;
agentName: string;
agentRole: string;
avatarColor: string;
taskTitle: string;
status: "running" | "ready" | "review";
progress: number;
commitHash: string;
}
export const DEFAULT_ORCA_WORKTREES: OrcaWorktreeItem[] = [
{
id: "wt-stripe-billing",
branch: "feat/stripe-billing-engine",
agentName: "Đức Anh",
agentRole: "Coder",
avatarColor: "from-emerald-500 to-green-600",
taskTitle: "Tích hợp Stripe Webhook & Xử lý Idempotency",
status: "running",
progress: 85,
commitHash: "9a2f1b4",
},
{
id: "wt-owasp-audit",
branch: "sec/zero-trust-gate",
agentName: "Thu Hà",
agentRole: "QA",
avatarColor: "from-rose-500 to-pink-600",
taskTitle: "Quét lỗ hổng SQL Injection & Rate Limiting",
status: "review",
progress: 100,
commitHash: "c4d8e20",
},
{
id: "wt-prd-spec",
branch: "spec/omnichannel-loyalty",
agentName: "Minh Quang",
agentRole: "PM",
avatarColor: "from-indigo-500 to-purple-600",
taskTitle: "Soạn thảo PRD luồng Đổi quà & Voucher",
status: "ready",
progress: 100,
commitHash: "f10a8b9",
},
];
export interface OrcaFleetListProps {
worktrees?: OrcaWorktreeItem[];
activeWorktreeId?: string | null;
onSelectWorktree?: (worktree: OrcaWorktreeItem) => void;
className?: string;
}
export function OrcaFleetList({
worktrees = DEFAULT_ORCA_WORKTREES,
activeWorktreeId,
onSelectWorktree,
className,
}: OrcaFleetListProps) {
const [expanded, setExpanded] = React.useState(true);
return (
<SidebarGroup data-orca-fleet className={cn("py-1", className)}>
<SidebarGroupContent>
<Collapsible open={expanded} onOpenChange={setExpanded} className="group/orca">
<div className={cn("flex h-6 items-center pe-2 ps-2.5", SIDEBAR_SECTION_LANE)}>
<CollapsibleTrigger
render={
<button
type="button"
className="flex flex-1 cursor-pointer items-center gap-1.5 text-left text-muted-foreground/80 hover:text-foreground"
>
<span className={SIDEBAR_SECTION_LABEL}>ORCA FLEET (WORKTREES)</span>
<span className="flex items-center gap-1 rounded bg-sidebar-accent px-1.5 py-0.2 text-[10px] font-mono tabular-nums text-cyan-400">
<span className="size-1.5 rounded-full bg-cyan-400 animate-pulse" />
{worktrees.length}
</span>
<ChevronDown className="ml-auto size-3.5 text-muted-foreground/60 transition-transform duration-200 group-data-open/orca:rotate-0 -rotate-90" />
</button>
}
/>
<button
type="button"
onClick={(e) => {
e.stopPropagation();
window.dispatchEvent(new CustomEvent("openwork-open-team"));
}}
title="Spawn New Git Worktree (Orca ADE)"
className="ml-1 flex size-5 cursor-pointer items-center justify-center rounded text-muted-foreground/60 transition hover:bg-sidebar-accent hover:text-foreground"
>
<Plus className="size-3.5" />
</button>
</div>
<CollapsibleContent className="mt-1">
<SidebarMenu>
{worktrees.map((wt) => {
const isSelected = activeWorktreeId === wt.id;
return (
<SidebarMenuItem key={wt.id}>
<SidebarMenuButton
isActive={isSelected}
onClick={() => onSelectWorktree?.(wt)}
className={cn(
"group/wt-btn relative h-auto min-h-8 w-full flex-col items-start gap-1 rounded-md px-2 py-1.5 text-[12px] font-normal transition-colors",
isSelected
? "bg-sidebar-accent font-medium text-sidebar-accent-foreground shadow-xs"
: "text-sidebar-foreground/80 hover:bg-sidebar-accent/50 hover:text-sidebar-foreground",
)}
title={`Branch: ${wt.branch}
Agent: ${wt.agentName} (${wt.agentRole})
Task: ${wt.taskTitle}`}
>
<div className="flex w-full items-center justify-between gap-1.5">
<div className="flex min-w-0 items-center gap-1.5 truncate font-mono text-[11px] text-foreground">
<GitBranch className="size-3.5 shrink-0 text-cyan-400" />
<span className="truncate">{wt.branch.replace("feat/", "").replace("sec/", "").replace("spec/", "")}</span>
</div>
<span
className={cn(
"shrink-0 rounded px-1.5 py-0.2 font-mono text-[9px] font-medium uppercase",
wt.status === "running" && "bg-cyan-500/15 text-cyan-400 border border-cyan-500/20",
wt.status === "review" && "bg-amber-500/15 text-amber-400 border border-amber-500/20",
wt.status === "ready" && "bg-emerald-500/15 text-emerald-400 border border-emerald-500/20",
)}
>
{wt.status}
</span>
</div>
<div className="flex w-full items-center justify-between text-[10px] text-muted-foreground/70">
<span className="truncate">
{wt.agentName.split(" ")[0]} · {wt.taskTitle}
</span>
<span className="font-mono text-[9px] text-muted-foreground/50 shrink-0">
{wt.commitHash}
</span>
</div>
</SidebarMenuButton>
</SidebarMenuItem>
);
})}
</SidebarMenu>
</CollapsibleContent>
</Collapsible>
</SidebarGroupContent>
</SidebarGroup>
);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment