From 4025f88ee93fc8fd1ffd283f7a35f9215cbd2e79 Mon Sep 17 00:00:00 2001 From: sean-eskerium Date: Sat, 25 Oct 2025 16:29:53 -0400 Subject: [PATCH] Updates to get Docker working and adding Claude OAUTH token variable, and finish of the style guide mockup. --- .env.example | 5 +- .../layouts/AgentWorkOrderExample.tsx | 4 + .../layouts/AgentWorkOrderLayoutExample.tsx | 270 +++++++++++------- .../components/ExecutionLogsExample.tsx | 212 ++++++++++++++ .../components/RealTimeStatsExample.tsx | 151 ++++++++++ .../features/style-guide/tabs/LayoutsTab.tsx | 1 - docker-compose.yml | 1 + python/Dockerfile.agent-work-orders | 4 + 8 files changed, 550 insertions(+), 98 deletions(-) create mode 100644 archon-ui-main/src/features/style-guide/layouts/components/ExecutionLogsExample.tsx create mode 100644 archon-ui-main/src/features/style-guide/layouts/components/RealTimeStatsExample.tsx diff --git a/.env.example b/.env.example index 2218e2a2..1b68847e 100644 --- a/.env.example +++ b/.env.example @@ -31,6 +31,9 @@ LOG_LEVEL=INFO # Get your API key from: https://console.anthropic.com/ # Required for the agent work orders service to execute Claude CLI commands ANTHROPIC_API_KEY= +# Generate an OAUTH token in terminal and it will use your Claude OAUTH token from your subscription. +CLAUDE_CODE_OAUTH_TOKEN= + # GitHub Personal Access Token (Required for Agent Work Orders PR creation) # Get your token from: https://github.com/settings/tokens @@ -55,7 +58,7 @@ ARCHON_DOCS_PORT=3838 # Default: false (feature disabled) # Set to "true" to enable: ENABLE_AGENT_WORK_ORDERS=true # When enabled, requires Claude API key and GitHub PAT (see above) -ENABLE_AGENT_WORK_ORDERS=false +ENABLE_AGENT_WORK_ORDERS=true # Agent Work Orders Service Configuration (Optional) # Only needed if ENABLE_AGENT_WORK_ORDERS=true diff --git a/archon-ui-main/src/features/style-guide/layouts/AgentWorkOrderExample.tsx b/archon-ui-main/src/features/style-guide/layouts/AgentWorkOrderExample.tsx index a00a403a..d050451e 100644 --- a/archon-ui-main/src/features/style-guide/layouts/AgentWorkOrderExample.tsx +++ b/archon-ui-main/src/features/style-guide/layouts/AgentWorkOrderExample.tsx @@ -5,6 +5,7 @@ import { Button } from "@/features/ui/primitives/button"; import { Card } from "@/features/ui/primitives/card"; import { cn } from "@/features/ui/primitives/styles"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/features/ui/primitives/tooltip"; +import { RealTimeStatsExample } from "./components/RealTimeStatsExample"; import { StepHistoryCard } from "./components/StepHistoryCard"; import { WorkflowStepButton } from "./components/WorkflowStepButton"; @@ -116,6 +117,9 @@ export const AgentWorkOrderExample = () => { collapsible history, and integrated document editing for human-in-the-loop approval.

+ {/* Real-Time Execution Stats */} + + {/* Workflow Progress Bar */}
diff --git a/archon-ui-main/src/features/style-guide/layouts/AgentWorkOrderLayoutExample.tsx b/archon-ui-main/src/features/style-guide/layouts/AgentWorkOrderLayoutExample.tsx index 49eabd9d..5540ed6a 100644 --- a/archon-ui-main/src/features/style-guide/layouts/AgentWorkOrderLayoutExample.tsx +++ b/archon-ui-main/src/features/style-guide/layouts/AgentWorkOrderLayoutExample.tsx @@ -1,6 +1,8 @@ import { Activity, CheckCircle2, + ChevronDown, + ChevronUp, Clock, Copy, Eye, @@ -10,6 +12,7 @@ import { Pin, Play, Plus, + Search, Trash2, } from "lucide-react"; import { useState } from "react"; @@ -24,6 +27,7 @@ import { SelectableCard } from "@/features/ui/primitives/selectable-card"; import { cn } from "@/features/ui/primitives/styles"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/features/ui/primitives/tooltip"; import { AgentWorkOrderExample } from "./AgentWorkOrderExample"; +import { RealTimeStatsExample } from "./components/RealTimeStatsExample"; const MOCK_REPOSITORIES = [ { @@ -69,7 +73,7 @@ interface WorkOrder { const MOCK_WORK_ORDERS: WorkOrder[] = [ { - id: "wo-1", + id: "wo-1dc27d9e", repositoryId: "1", repositoryName: "archon-frontend", request: "Add dark mode toggle to settings page", @@ -78,7 +82,7 @@ const MOCK_WORK_ORDERS: WorkOrder[] = [ createdAt: "2024-01-15T10:30:00Z", }, { - id: "wo-2", + id: "wo-2af8b3c1", repositoryId: "1", repositoryName: "archon-frontend", request: "Refactor navigation component to use new design system", @@ -87,7 +91,7 @@ const MOCK_WORK_ORDERS: WorkOrder[] = [ createdAt: "2024-01-15T09:15:00Z", }, { - id: "wo-3", + id: "wo-4e372af3", repositoryId: "2", repositoryName: "archon-backend", request: "Implement caching layer for API responses", @@ -96,7 +100,7 @@ const MOCK_WORK_ORDERS: WorkOrder[] = [ createdAt: "2024-01-14T16:45:00Z", }, { - id: "wo-4", + id: "wo-8b91f2d6", repositoryId: "2", repositoryName: "archon-backend", request: "Add rate limiting to authentication endpoints", @@ -105,7 +109,7 @@ const MOCK_WORK_ORDERS: WorkOrder[] = [ createdAt: "2024-01-14T14:20:00Z", }, { - id: "wo-5", + id: "wo-5c7d4a89", repositoryId: "1", repositoryName: "archon-frontend", request: "Fix responsive layout issues on mobile devices", @@ -114,7 +118,7 @@ const MOCK_WORK_ORDERS: WorkOrder[] = [ createdAt: "2024-01-13T11:00:00Z", }, { - id: "wo-6", + id: "wo-9f3e1b5a", repositoryId: "3", repositoryName: "archon-docs", request: "Update API documentation with new endpoints", @@ -126,7 +130,7 @@ const MOCK_WORK_ORDERS: WorkOrder[] = [ export const AgentWorkOrderLayoutExample = () => { const [selectedRepositoryId, setSelectedRepositoryId] = useState("1"); - const [layoutMode, setLayoutMode] = useState<"horizontal" | "sidebar">("horizontal"); + const [layoutMode, setLayoutMode] = useState<"horizontal" | "sidebar">("sidebar"); const [sidebarExpanded, setSidebarExpanded] = useState(true); const [showAddRepoModal, setShowAddRepoModal] = useState(false); const [showNewWorkOrderModal, setShowNewWorkOrderModal] = useState(false); @@ -134,6 +138,7 @@ export const AgentWorkOrderLayoutExample = () => { const [activeTab, setActiveTab] = useState("all"); const [showDetailView, setShowDetailView] = useState(false); const [selectedWorkOrderId, setSelectedWorkOrderId] = useState(null); + const [searchQuery, setSearchQuery] = useState(""); const selectedRepository = MOCK_REPOSITORIES.find((r) => r.id === selectedRepositoryId); const selectedWorkOrder = workOrders.find((wo) => wo.id === selectedWorkOrderId); @@ -183,9 +188,42 @@ export const AgentWorkOrderLayoutExample = () => { return (
- {/* Layout Mode Toggle */} -
-
+ {/* Header Section */} +
+ {/* Title */} +

Agent Work Orders

+ + {/* Search Bar */} +
+
+ + {/* View Toggle - Sidebar is default/primary */} +
+ -
+ + {/* New Repo Button */} +
+ {/* Add Repository Modal */} + + {layoutMode === "horizontal" ? ( <> {/* Horizontal Repository Cards - ONLY cards scroll, not whole page */} @@ -233,8 +267,6 @@ export const AgentWorkOrderLayoutExample = () => { }} /> ))} - {/* Add Repository Button */} -
@@ -756,8 +788,9 @@ const WorkOrdersTableView = ({ - - Work Order ID + WO ID + + Repository Request Summary @@ -783,7 +816,7 @@ const WorkOrdersTableView = ({ ); }; -// Work Order Row with status-based styling +// Work Order Row with status-based styling and expandable real-time stats const WorkOrderRow = ({ workOrder, index, @@ -795,103 +828,165 @@ const WorkOrderRow = ({ onStart: () => void; onViewDetails: () => void; }) => { + const [isExpanded, setIsExpanded] = useState(false); + // Status colors - STATIC lookup with all properties const statusColors: Record< WorkOrderStatus, - { color: "pink" | "cyan" | "blue" | "orange" | "purple" | "green"; edge: string; glow: string; label: string } + { + color: "pink" | "cyan" | "blue" | "orange" | "purple" | "green"; + edge: string; + glow: string; + label: string; + stepNumber: number; + } > = { pending: { color: "pink", edge: "bg-pink-500", glow: "rgba(236,72,153,0.5)", label: "Pending", + stepNumber: 0, }, create_branch: { color: "cyan", edge: "bg-cyan-500", glow: "rgba(34,211,238,0.5)", label: "+ Branch", + stepNumber: 1, }, plan: { color: "blue", edge: "bg-blue-500", glow: "rgba(59,130,246,0.5)", label: "Planning", + stepNumber: 2, }, execute: { color: "orange", edge: "bg-orange-500", glow: "rgba(249,115,22,0.5)", label: "Executing", + stepNumber: 3, }, commit: { color: "purple", edge: "bg-purple-500", glow: "rgba(168,85,247,0.5)", label: "Commit", + stepNumber: 4, }, create_pr: { color: "green", edge: "bg-green-500", glow: "rgba(34,197,94,0.5)", label: "Create PR", + stepNumber: 5, }, }; const colors = statusColors[workOrder.status]; + const canExpand = workOrder.status !== "pending"; + + const handleStart = () => { + setIsExpanded(true); // Auto-expand when started + onStart(); + }; return ( - - {/* Status indicator - glowing circle */} - -
-
-
- - - {/* Work Order ID */} - - {workOrder.id} - - - {/* Request Summary */} - -

{workOrder.request}

- - - {/* Status Badge - using StatPill */} - - - - - {/* Actions */} - - {workOrder.status === "pending" ? ( - - ) : ( - + <> + - + > + {/* Status indicator - glowing circle with optional collapse button */} + +
+ {canExpand && ( + + )} +
+
+ + + {/* Work Order ID */} + + {workOrder.id} + + + {/* Repository */} + + {workOrder.repositoryName} + + + {/* Request Summary */} + +

{workOrder.request}

+ + + {/* Status Badge - using StatPill */} + + + + + {/* Actions */} + + {workOrder.status === "pending" ? ( + + ) : ( + + )} + + + + {/* Expanded row with real-time stats */} + {isExpanded && canExpand && ( + + + + + + )} + ); }; @@ -926,23 +1021,6 @@ const AddRepositoryModal = ({ open, onOpenChange }: { open: boolean; onOpenChang return ( - - - Add Repository diff --git a/archon-ui-main/src/features/style-guide/layouts/components/ExecutionLogsExample.tsx b/archon-ui-main/src/features/style-guide/layouts/components/ExecutionLogsExample.tsx new file mode 100644 index 00000000..e9945ca7 --- /dev/null +++ b/archon-ui-main/src/features/style-guide/layouts/components/ExecutionLogsExample.tsx @@ -0,0 +1,212 @@ +import { Trash2 } from "lucide-react"; +import { useState } from "react"; +import { Button } from "@/features/ui/primitives/button"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/features/ui/primitives/select"; +import { cn } from "@/features/ui/primitives/styles"; +import { Switch } from "@/features/ui/primitives/switch"; + +interface ExecutionLogsExampleProps { + /** Work order status to generate appropriate mock logs */ + status: string; +} + +interface MockLog { + timestamp: string; + level: "info" | "warning" | "error" | "debug"; + event: string; + step?: string; + progress?: string; +} + +/** + * Get color class for log level badge - STATIC lookup + */ +const logLevelColors: Record = { + info: "bg-blue-500/20 text-blue-600 dark:text-blue-400 border-blue-400/30", + warning: "bg-yellow-500/20 text-yellow-600 dark:text-yellow-400 border-yellow-400/30", + error: "bg-red-500/20 text-red-600 dark:text-red-400 border-red-400/30", + debug: "bg-gray-500/20 text-gray-600 dark:text-gray-400 border-gray-400/30", +}; + +/** + * Format timestamp to relative time + */ +function formatRelativeTime(timestamp: string): string { + const now = Date.now(); + const logTime = new Date(timestamp).getTime(); + const diffSeconds = Math.floor((now - logTime) / 1000); + + if (diffSeconds < 60) return `${diffSeconds}s ago`; + if (diffSeconds < 3600) return `${Math.floor(diffSeconds / 60)}m ago`; + return `${Math.floor(diffSeconds / 3600)}h ago`; +} + +/** + * Individual log entry component + */ +function LogEntryRow({ log }: { log: MockLog }) { + const colorClass = logLevelColors[log.level] || logLevelColors.debug; + + return ( +
+ + {formatRelativeTime(log.timestamp)} + + + {log.level} + + {log.step && [{log.step}]} + {log.event} + {log.progress && ( + {log.progress} + )} +
+ ); +} + +export function ExecutionLogsExample({ status }: ExecutionLogsExampleProps) { + const [autoScroll, setAutoScroll] = useState(true); + const [levelFilter, setLevelFilter] = useState("all"); + + // Generate mock logs based on status + const generateMockLogs = (): MockLog[] => { + const now = Date.now(); + const baseTime = now - 300000; // 5 minutes ago + + const logs: MockLog[] = [ + { timestamp: new Date(baseTime).toISOString(), level: "info", event: "workflow_started" }, + { timestamp: new Date(baseTime + 1000).toISOString(), level: "info", event: "sandbox_setup_started" }, + { + timestamp: new Date(baseTime + 3000).toISOString(), + level: "info", + event: "repository_cloned", + step: "setup", + }, + { timestamp: new Date(baseTime + 5000).toISOString(), level: "info", event: "sandbox_setup_completed" }, + ]; + + if (status !== "pending") { + logs.push( + { + timestamp: new Date(baseTime + 10000).toISOString(), + level: "info", + event: "step_started", + step: "create-branch", + progress: "1/5", + }, + { + timestamp: new Date(baseTime + 12000).toISOString(), + level: "info", + event: "agent_command_started", + step: "create-branch", + }, + { + timestamp: new Date(baseTime + 45000).toISOString(), + level: "info", + event: "branch_created", + step: "create-branch", + }, + ); + } + + if (status === "plan" || status === "execute" || status === "commit" || status === "create_pr") { + logs.push( + { + timestamp: new Date(baseTime + 60000).toISOString(), + level: "info", + event: "step_started", + step: "planning", + progress: "2/5", + }, + { + timestamp: new Date(baseTime + 120000).toISOString(), + level: "debug", + event: "analyzing_codebase", + step: "planning", + }, + ); + } + + return logs; + }; + + const mockLogs = generateMockLogs(); + const filteredLogs = levelFilter === "all" ? mockLogs : mockLogs.filter((log) => log.level === levelFilter); + + return ( +
+ {/* Header with controls */} +
+
+ Execution Logs + + {/* Live indicator */} +
+
+ Live +
+ + ({filteredLogs.length} entries) +
+ + {/* Controls */} +
+ {/* Level filter using proper Select primitive */} + + + {/* Auto-scroll toggle using Switch primitive */} +
+ + + + {autoScroll ? "ON" : "OFF"} + +
+ + {/* Clear logs button */} + +
+
+ + {/* Log content - scrollable area */} +
+ {filteredLogs.length === 0 ? ( +
+

No logs match the current filter

+
+ ) : ( +
+ {filteredLogs.map((log, index) => ( + + ))} +
+ )} +
+
+ ); +} diff --git a/archon-ui-main/src/features/style-guide/layouts/components/RealTimeStatsExample.tsx b/archon-ui-main/src/features/style-guide/layouts/components/RealTimeStatsExample.tsx new file mode 100644 index 00000000..aa554701 --- /dev/null +++ b/archon-ui-main/src/features/style-guide/layouts/components/RealTimeStatsExample.tsx @@ -0,0 +1,151 @@ +import { Activity, ChevronDown, ChevronUp, Clock, TrendingUp } from "lucide-react"; +import { useState } from "react"; +import { Button } from "@/features/ui/primitives/button"; +import { ExecutionLogsExample } from "./ExecutionLogsExample"; + +interface RealTimeStatsExampleProps { + /** Work order status for determining progress */ + status: string; + /** Step number (1-5) */ + stepNumber: number; +} + +/** + * Format elapsed seconds to human-readable duration + */ +function formatDuration(seconds: number): string { + const hours = Math.floor(seconds / 3600); + const minutes = Math.floor((seconds % 3600) / 60); + const secs = seconds % 60; + + if (hours > 0) { + return `${hours}h ${minutes}m ${secs}s`; + } + if (minutes > 0) { + return `${minutes}m ${secs}s`; + } + return `${secs}s`; +} + +export function RealTimeStatsExample({ status, stepNumber }: RealTimeStatsExampleProps) { + const [showLogs, setShowLogs] = useState(false); + + // Mock data based on status + const stepNames: Record = { + create_branch: "create-branch", + plan: "planning", + execute: "execute", + commit: "commit", + create_pr: "create-pr", + }; + + const currentStep = stepNames[status] || "initializing"; + const progressPct = (stepNumber / 5) * 100; + const mockElapsedSeconds = stepNumber * 120; // 2 minutes per step + + const activities: Record = { + create_branch: "Creating new branch for work order...", + plan: "Analyzing codebase and generating implementation plan...", + execute: "Writing code and applying changes...", + commit: "Committing changes to branch...", + create_pr: "Creating pull request on GitHub...", + }; + + const currentActivity = activities[status] || "Initializing workflow..."; + + return ( +
+
+

+

+ +
+ {/* Current Step */} +
+
Current Step
+
+ {currentStep} + ({stepNumber}/5) +
+
+ + {/* Progress */} +
+
+
+
+
+
+
+
+ {progressPct}% +
+
+
+ + {/* Elapsed Time */} +
+
+
+
+ {formatDuration(mockElapsedSeconds)} +
+
+
+ + {/* Latest Activity with Status Indicator - at top */} +
+
+
+
+ Latest Activity: +
+
{currentActivity}
+
+ {/* Status Indicator - right side of Latest Activity */} +
+
+ Running +
+
+
+ + {/* Show Execution Logs button - at bottom */} +
+ +
+
+ + {/* Collapsible Execution Logs */} + {showLogs && } +
+ ); +} diff --git a/archon-ui-main/src/features/style-guide/tabs/LayoutsTab.tsx b/archon-ui-main/src/features/style-guide/tabs/LayoutsTab.tsx index b07deb84..a380abb7 100644 --- a/archon-ui-main/src/features/style-guide/tabs/LayoutsTab.tsx +++ b/archon-ui-main/src/features/style-guide/tabs/LayoutsTab.tsx @@ -1,6 +1,5 @@ import { Briefcase, Database, FileText, FolderKanban, Navigation, Settings } from "lucide-react"; import { useState } from "react"; -import { AgentWorkOrderExample } from "../layouts/AgentWorkOrderExample"; import { AgentWorkOrderLayoutExample } from "../layouts/AgentWorkOrderLayoutExample"; import { DocumentBrowserExample } from "../layouts/DocumentBrowserExample"; import { KnowledgeLayoutExample } from "../layouts/KnowledgeLayoutExample"; diff --git a/docker-compose.yml b/docker-compose.yml index 68fdffb7..f985da73 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -172,6 +172,7 @@ services: - SUPABASE_SERVICE_KEY=${SUPABASE_SERVICE_KEY} - OPENAI_API_KEY=${OPENAI_API_KEY:-} - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-} + - CLAUDE_CODE_OAUTH_TOKEN=${CLAUDE_CODE_OAUTH_TOKEN:-} - LOGFIRE_TOKEN=${LOGFIRE_TOKEN:-} - LOG_LEVEL=${LOG_LEVEL:-INFO} - AGENT_WORK_ORDERS_PORT=${AGENT_WORK_ORDERS_PORT:-8053} diff --git a/python/Dockerfile.agent-work-orders b/python/Dockerfile.agent-work-orders index 72dc2ebc..d7da368d 100644 --- a/python/Dockerfile.agent-work-orders +++ b/python/Dockerfile.agent-work-orders @@ -59,6 +59,10 @@ RUN mkdir -p /repos /tmp/agent-work-orders && \ USER agentuser RUN curl -fsSL https://claude.ai/install.sh | bash +# Configure git to use gh CLI for GitHub authentication +# This allows git clone to authenticate using GH_TOKEN environment variable +RUN git config --global credential.helper '!gh auth git-credential' + # Set environment variables ENV PYTHONPATH="/app:$PYTHONPATH" ENV PYTHONUNBUFFERED=1