mirror of
https://github.com/leonvanzyl/autocoder.git
synced 2026-03-16 18:33:08 +00:00
Agents can now request structured human input when they encounter genuine blockers (API keys, design choices, external configs). The request is displayed in the UI with a dynamic form, and the human's response is stored and made available when the agent resumes. Changes span 21 files + 1 new component: - Database: 3 new columns (needs_human_input, human_input_request, human_input_response) with migration - MCP: new feature_request_human_input tool + guards on existing tools - API: new resolve-human-input endpoint, 4th feature bucket - Orchestrator: skip needs_human_input features in scheduling - Progress: 4-tuple return from count_passing_tests - WebSocket: needs_human_input count in progress messages - UI: conditional 4th Kanban column, HumanInputForm component, amber status indicators, dependency graph support Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
93 lines
3.1 KiB
TypeScript
93 lines
3.1 KiB
TypeScript
import { KanbanColumn } from './KanbanColumn'
|
|
import type { Feature, FeatureListResponse, ActiveAgent } from '../lib/types'
|
|
import { Card, CardContent } from '@/components/ui/card'
|
|
|
|
interface KanbanBoardProps {
|
|
features: FeatureListResponse | undefined
|
|
onFeatureClick: (feature: Feature) => void
|
|
onAddFeature?: () => void
|
|
onExpandProject?: () => void
|
|
activeAgents?: ActiveAgent[]
|
|
onCreateSpec?: () => void
|
|
hasSpec?: boolean
|
|
}
|
|
|
|
export function KanbanBoard({ features, onFeatureClick, onAddFeature, onExpandProject, activeAgents = [], onCreateSpec, hasSpec = true }: KanbanBoardProps) {
|
|
const hasFeatures = features && (features.pending.length + features.in_progress.length + features.done.length + (features.needs_human_input?.length || 0)) > 0
|
|
|
|
// Combine all features for dependency status calculation
|
|
const allFeatures = features
|
|
? [...features.pending, ...features.in_progress, ...features.done, ...(features.needs_human_input || [])]
|
|
: []
|
|
|
|
const needsInputCount = features?.needs_human_input?.length || 0
|
|
const showNeedsInput = needsInputCount > 0
|
|
|
|
if (!features) {
|
|
return (
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
{['Pending', 'In Progress', 'Done'].map(title => (
|
|
<Card key={title} className="py-4">
|
|
<CardContent className="p-4">
|
|
<div className="h-8 bg-muted animate-pulse rounded mb-4" />
|
|
<div className="space-y-3">
|
|
{[1, 2, 3].map(i => (
|
|
<div key={i} className="h-24 bg-muted animate-pulse rounded" />
|
|
))}
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
))}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className={`grid grid-cols-1 ${showNeedsInput ? 'md:grid-cols-4' : 'md:grid-cols-3'} gap-6`}>
|
|
<KanbanColumn
|
|
title="Pending"
|
|
count={features.pending.length}
|
|
features={features.pending}
|
|
allFeatures={allFeatures}
|
|
activeAgents={activeAgents}
|
|
color="pending"
|
|
onFeatureClick={onFeatureClick}
|
|
onAddFeature={onAddFeature}
|
|
onExpandProject={onExpandProject}
|
|
showExpandButton={hasFeatures && hasSpec}
|
|
onCreateSpec={onCreateSpec}
|
|
showCreateSpec={!hasSpec && !hasFeatures}
|
|
/>
|
|
<KanbanColumn
|
|
title="In Progress"
|
|
count={features.in_progress.length}
|
|
features={features.in_progress}
|
|
allFeatures={allFeatures}
|
|
activeAgents={activeAgents}
|
|
color="progress"
|
|
onFeatureClick={onFeatureClick}
|
|
/>
|
|
{showNeedsInput && (
|
|
<KanbanColumn
|
|
title="Needs Input"
|
|
count={needsInputCount}
|
|
features={features.needs_human_input}
|
|
allFeatures={allFeatures}
|
|
activeAgents={activeAgents}
|
|
color="human_input"
|
|
onFeatureClick={onFeatureClick}
|
|
/>
|
|
)}
|
|
<KanbanColumn
|
|
title="Done"
|
|
count={features.done.length}
|
|
features={features.done}
|
|
allFeatures={allFeatures}
|
|
activeAgents={activeAgents}
|
|
color="done"
|
|
onFeatureClick={onFeatureClick}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|