feat(HAI-005): add auto-merge functionality for in-review tasks

- Add autoMerge boolean to Settings model with default false
- Add auto-merge toggle UI to the In Review column header
- Implement server-side auto-merge behavior in dashboard command
- Fix pre-existing build errors in types, executor, and dashboard server
- Update README with auto-merge documentation
This commit is contained in:
Dustin Byrne
2026-03-25 20:48:34 -04:00
10 changed files with 105 additions and 7 deletions

View File

@@ -13,9 +13,11 @@ interface BoardProps {
onCancelCreate: () => void;
onCreateTask: (input: TaskCreateInput) => Promise<Task>;
onNewTask: () => void;
autoMerge: boolean;
onToggleAutoMerge: () => void;
}
export function Board({ tasks, maxConcurrent, onMoveTask, onOpenDetail, addToast, isCreating, onCancelCreate, onCreateTask, onNewTask }: BoardProps) {
export function Board({ tasks, maxConcurrent, onMoveTask, onOpenDetail, addToast, isCreating, onCancelCreate, onCreateTask, onNewTask, autoMerge, onToggleAutoMerge }: BoardProps) {
return (
<main className="board" id="board">
{COLUMNS.map((col) => (
@@ -29,6 +31,7 @@ export function Board({ tasks, maxConcurrent, onMoveTask, onOpenDetail, addToast
onOpenDetail={onOpenDetail}
addToast={addToast}
{...(col === "triage" ? { isCreating, onCancelCreate, onCreateTask, onNewTask } : {})}
{...(col === "in-review" ? { autoMerge, onToggleAutoMerge } : {})}
/>
))}
</main>

View File

@@ -19,9 +19,11 @@ interface ColumnProps {
onCancelCreate?: () => void;
onCreateTask?: (input: TaskCreateInput) => Promise<Task>;
onNewTask?: () => void;
autoMerge?: boolean;
onToggleAutoMerge?: () => void;
}
export function Column({ column, tasks, allTasks, maxConcurrent, onMoveTask, onOpenDetail, addToast, isCreating, onCancelCreate, onCreateTask, onNewTask }: ColumnProps) {
export function Column({ column, tasks, allTasks, maxConcurrent, onMoveTask, onOpenDetail, addToast, isCreating, onCancelCreate, onCreateTask, onNewTask, autoMerge, onToggleAutoMerge }: ColumnProps) {
const [dragOver, setDragOver] = useState(false);
const handleDragOver = useCallback((e: React.DragEvent) => {
@@ -62,6 +64,17 @@ export function Column({ column, tasks, allTasks, maxConcurrent, onMoveTask, onO
<div className={`column-dot dot-${column}`} />
<h2>{COLUMN_LABELS[column]}</h2>
<span className="column-count">{tasks.length}</span>
{column === "in-review" && onToggleAutoMerge && (
<label className="auto-merge-toggle" title={autoMerge ? "Auto-merge enabled" : "Auto-merge disabled"}>
<input
type="checkbox"
checked={!!autoMerge}
onChange={onToggleAutoMerge}
/>
<span className="toggle-slider" />
<span className="toggle-label">Auto-merge</span>
</label>
)}
{onNewTask && (
<button className="btn btn-primary btn-sm" onClick={onNewTask}>
+ New Task

View File

@@ -9,7 +9,7 @@ interface SettingsModalProps {
}
export function SettingsModal({ onClose, addToast }: SettingsModalProps) {
const [form, setForm] = useState<Settings>({ maxConcurrent: 2, maxWorktrees: 4, pollIntervalMs: 15000, groupOverlappingFiles: false });
const [form, setForm] = useState<Settings>({ maxConcurrent: 2, maxWorktrees: 4, pollIntervalMs: 15000, groupOverlappingFiles: false, autoMerge: false });
const [loading, setLoading] = useState(true);
useEffect(() => {