From c618174e60e4632d36d7352d83399508c72b2f42 Mon Sep 17 00:00:00 2001
From: soryu <soryu@soryu.co>
Date: Tue, 27 Jan 2026 11:04:20 +0000
Subject: Add Red Team CLI command and frontend UI (#39)

* Add Red Team CLI command and frontend UI

Backend additions:
- Add `makima red-team notify` CLI command for red team tasks
- Add RedTeamCommand enum with Notify subcommand
- Add red_team API client module for notify endpoint
- Add RedTeamNotifyArgs with severity, task, file, context options

Frontend additions:
- Add ContractCreateModal with red team toggle and prompt input
- Update ContractDetail with red-team tab for notifications
- Update ContractList with red team enabled badge
- Add TypeScript types for RedTeamNotification and related interfaces

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add CSS styles for Red Team frontend components

Add comprehensive styling for:
- Contract list and detail containers
- Red team badge styling with gradient backgrounds
- Tab navigation with red team specific styling
- Red team notifications panel with severity indicators
- Contract creation modal form elements
- Task badges for supervisor and red team roles

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix missing local_only field in TUI CreateContractRequest

Add the missing local_only field to the CreateContractRequest struct
initialization in the TUI contract creation handler.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* [WIP] Heartbeat checkpoint - 2026-01-27 03:07:28 UTC

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
---
 frontend/src/components/ContractList.tsx | 61 ++++++++++++++++++++++----------
 1 file changed, 43 insertions(+), 18 deletions(-)

(limited to 'frontend/src/components/ContractList.tsx')

diff --git a/frontend/src/components/ContractList.tsx b/frontend/src/components/ContractList.tsx
index 77012db..253b44f 100644
--- a/frontend/src/components/ContractList.tsx
+++ b/frontend/src/components/ContractList.tsx
@@ -1,5 +1,6 @@
-import React, { useEffect, useState } from 'react'
+import React, { useEffect, useState, useCallback } from 'react'
 import { Link } from 'react-router-dom'
+import { ContractCreateModal } from './ContractCreateModal'
 
 interface ContractSummary {
   id: string
@@ -12,32 +13,35 @@ interface ContractSummary {
   task_count: number
   repository_count: number
   created_at: string
+  // Red team fields
+  red_team_enabled?: boolean
 }
 
 export function ContractList() {
   const [contracts, setContracts] = useState<ContractSummary[]>([])
   const [loading, setLoading] = useState(true)
   const [error, setError] = useState<string | null>(null)
+  const [showCreateModal, setShowCreateModal] = useState(false)
 
-  useEffect(() => {
-    async function fetchContracts() {
-      try {
-        setLoading(true)
-        const response = await fetch('/api/v1/contracts')
-        if (!response.ok) {
-          throw new Error(`Failed to fetch contracts: ${response.statusText}`)
-        }
-        const data = await response.json()
-        setContracts(data.contracts || [])
-      } catch (err) {
-        setError(err instanceof Error ? err.message : 'Unknown error')
-      } finally {
-        setLoading(false)
+  const fetchContracts = useCallback(async () => {
+    try {
+      setLoading(true)
+      const response = await fetch('/api/v1/contracts')
+      if (!response.ok) {
+        throw new Error(`Failed to fetch contracts: ${response.statusText}`)
       }
+      const data = await response.json()
+      setContracts(data.contracts || [])
+    } catch (err) {
+      setError(err instanceof Error ? err.message : 'Unknown error')
+    } finally {
+      setLoading(false)
     }
+  }, [])
 
+  useEffect(() => {
     fetchContracts()
-  }, [])
+  }, [fetchContracts])
 
   if (loading) {
     return (
@@ -57,7 +61,15 @@ export function ContractList() {
 
   return (
     <div className="contract-list-container">
-      <h1>Contracts</h1>
+      <div className="contract-list-header">
+        <h1>Contracts</h1>
+        <button
+          className="create-contract-btn"
+          onClick={() => setShowCreateModal(true)}
+        >
+          + New Contract
+        </button>
+      </div>
       {contracts.length === 0 ? (
         <p>No contracts found</p>
       ) : (
@@ -65,7 +77,14 @@ export function ContractList() {
           {contracts.map((contract) => (
             <li key={contract.id} className="contract-item">
               <Link to={`/contracts/${contract.id}`}>
-                <h2>{contract.name}</h2>
+                <h2>
+                  {contract.name}
+                  {contract.red_team_enabled && (
+                    <span className="red-team-badge" title="Red Team monitoring enabled">
+                      🔍
+                    </span>
+                  )}
+                </h2>
                 {contract.description && <p>{contract.description}</p>}
                 <div className="contract-meta">
                   <span>Phase: {contract.phase}</span>
@@ -78,6 +97,12 @@ export function ContractList() {
           ))}
         </ul>
       )}
+
+      <ContractCreateModal
+        isOpen={showCreateModal}
+        onClose={() => setShowCreateModal(false)}
+        onCreated={fetchContracts}
+      />
     </div>
   )
 }
-- 
cgit v1.2.3

