mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-03-25 19:48:33 +08:00
feat: add type filter and claude-only fields to batch controls
Batch controls now distinguish codex vs claude agents: - Type selector (All/Codex/Claude) filters targets and shows count - Effort options adapt per type (no 'max' for codex-only) - Color and Permission batch fields shown when claude agents targeted - Claude-only fields applied individually per agent via PUT endpoint Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -622,6 +622,9 @@ export function AgentDefinitionsSection() {
|
|||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [batchModel, setBatchModel] = useState('');
|
const [batchModel, setBatchModel] = useState('');
|
||||||
const [batchEffort, setBatchEffort] = useState('');
|
const [batchEffort, setBatchEffort] = useState('');
|
||||||
|
const [batchType, setBatchType] = useState<'all' | 'codex' | 'claude'>('all');
|
||||||
|
const [batchColor, setBatchColor] = useState('');
|
||||||
|
const [batchPermission, setBatchPermission] = useState('');
|
||||||
const [batchSaving, setBatchSaving] = useState(false);
|
const [batchSaving, setBatchSaving] = useState(false);
|
||||||
|
|
||||||
const loadAgents = useCallback(async () => {
|
const loadAgents = useCallback(async () => {
|
||||||
@@ -646,28 +649,61 @@ export function AgentDefinitionsSection() {
|
|||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
|
const batchTargets = agents.filter(a => batchType === 'all' || a.type === batchType);
|
||||||
|
const batchHasClaudeTargets = batchTargets.some(a => a.type === 'claude');
|
||||||
|
const batchEffortOptions = batchType === 'codex'
|
||||||
|
? CODEX_EFFORTS
|
||||||
|
: batchType === 'claude'
|
||||||
|
? CLAUDE_EFFORTS
|
||||||
|
: ['', 'low', 'medium', 'high', 'max (claude only)'];
|
||||||
|
|
||||||
const handleBatchApply = useCallback(async () => {
|
const handleBatchApply = useCallback(async () => {
|
||||||
if (!batchModel && !batchEffort) {
|
const hasAnyValue = batchModel || batchEffort || batchColor || batchPermission;
|
||||||
toast.error('Set a model or effort value first');
|
if (!hasAnyValue) {
|
||||||
|
toast.error('Set at least one value first');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (batchTargets.length === 0) {
|
||||||
|
toast.error('No agents match the selected type');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setBatchSaving(true);
|
setBatchSaving(true);
|
||||||
try {
|
try {
|
||||||
const targets = agents.map(a => ({ filePath: a.filePath, type: a.type }));
|
// For model/effort, use the batch endpoint
|
||||||
const result = await batchUpdateAgentDefinitions({
|
if (batchModel || batchEffort) {
|
||||||
targets,
|
const targets = batchTargets.map(a => ({ filePath: a.filePath, type: a.type }));
|
||||||
model: batchModel || undefined,
|
const result = await batchUpdateAgentDefinitions({
|
||||||
effort: batchEffort || undefined,
|
targets,
|
||||||
});
|
model: batchModel || undefined,
|
||||||
toast.success(`Updated ${result.updated}/${result.total} agents`);
|
effort: batchEffort || undefined,
|
||||||
|
});
|
||||||
|
toast.success(`Updated model/effort: ${result.updated}/${result.total} agents`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For claude-only fields (color, permissionMode), update each claude agent individually
|
||||||
|
if ((batchColor || batchPermission) && batchHasClaudeTargets) {
|
||||||
|
const claudeTargets = batchTargets.filter(a => a.type === 'claude');
|
||||||
|
let updated = 0;
|
||||||
|
for (const agent of claudeTargets) {
|
||||||
|
try {
|
||||||
|
const body: { filePath: string; [key: string]: string | undefined } = { filePath: agent.filePath };
|
||||||
|
if (batchColor) body.color = batchColor;
|
||||||
|
if (batchPermission) body.permissionMode = batchPermission;
|
||||||
|
await updateAgentDefinition(agent.type, agent.name, body);
|
||||||
|
updated++;
|
||||||
|
} catch { /* skip failed */ }
|
||||||
|
}
|
||||||
|
toast.success(`Updated claude fields: ${updated}/${claudeTargets.length} agents`);
|
||||||
|
}
|
||||||
|
|
||||||
loadAgents();
|
loadAgents();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toast.error(`Batch update failed: ${(err as Error).message}`);
|
toast.error(`Batch update failed: ${(err as Error).message}`);
|
||||||
} finally {
|
} finally {
|
||||||
setBatchSaving(false);
|
setBatchSaving(false);
|
||||||
}
|
}
|
||||||
}, [agents, batchModel, batchEffort, loadAgents]);
|
}, [batchTargets, batchHasClaudeTargets, batchModel, batchEffort, batchColor, batchPermission, loadAgents]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="p-6">
|
<Card className="p-6">
|
||||||
@@ -683,40 +719,70 @@ export function AgentDefinitionsSection() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Batch Controls */}
|
{/* Batch Controls */}
|
||||||
<div className="flex items-center gap-3 p-3 mb-4 rounded-md border border-border bg-muted/30">
|
<div className="p-3 mb-4 rounded-md border border-border bg-muted/30 space-y-2">
|
||||||
<span className="text-xs font-medium text-muted-foreground shrink-0">Batch:</span>
|
<div className="flex items-center gap-3 flex-wrap">
|
||||||
<div className="flex items-center gap-1">
|
<span className="text-xs font-medium text-muted-foreground shrink-0">Batch:</span>
|
||||||
<span className="text-xs text-muted-foreground">Model:</span>
|
<div className="flex items-center gap-1">
|
||||||
<Input
|
<span className={labelClass}>Target:</span>
|
||||||
className="h-7 text-xs w-[160px]"
|
<select
|
||||||
value={batchModel}
|
className={selectClass}
|
||||||
onChange={(e) => setBatchModel(e.target.value)}
|
value={batchType}
|
||||||
placeholder="model for all"
|
onChange={(e) => setBatchType(e.target.value as 'all' | 'codex' | 'claude')}
|
||||||
/>
|
>
|
||||||
</div>
|
<option value="all">All ({agents.length})</option>
|
||||||
<div className="flex items-center gap-1">
|
<option value="codex">Codex ({agents.filter(a => a.type === 'codex').length})</option>
|
||||||
<span className="text-xs text-muted-foreground">Effort:</span>
|
<option value="claude">Claude ({agents.filter(a => a.type === 'claude').length})</option>
|
||||||
<select
|
</select>
|
||||||
className="h-7 text-xs rounded border border-input bg-background px-2"
|
</div>
|
||||||
value={batchEffort}
|
<div className="flex items-center gap-1">
|
||||||
onChange={(e) => setBatchEffort(e.target.value)}
|
<span className={labelClass}>Model:</span>
|
||||||
|
<Input
|
||||||
|
className="h-7 text-xs w-[140px]"
|
||||||
|
value={batchModel}
|
||||||
|
onChange={(e) => setBatchModel(e.target.value)}
|
||||||
|
placeholder="model"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<span className={labelClass}>Effort:</span>
|
||||||
|
<select
|
||||||
|
className={selectClass}
|
||||||
|
value={batchEffort}
|
||||||
|
onChange={(e) => setBatchEffort(e.target.value)}
|
||||||
|
>
|
||||||
|
{batchEffortOptions.map(e => {
|
||||||
|
const val = e.startsWith('max') ? 'max' : e;
|
||||||
|
return <option key={e} value={val}>{e || '—'}</option>;
|
||||||
|
})}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
{/* Claude-only batch fields */}
|
||||||
|
{batchType !== 'codex' && (
|
||||||
|
<>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<span className={labelClass}>Color:</span>
|
||||||
|
<select className={selectClass} value={batchColor} onChange={e => setBatchColor(e.target.value)}>
|
||||||
|
{COLOR_OPTIONS.map(c => <option key={c} value={c}>{c || '—'}</option>)}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<span className={labelClass}>Permission:</span>
|
||||||
|
<select className={selectClass} value={batchPermission} onChange={e => setBatchPermission(e.target.value)}>
|
||||||
|
{PERMISSION_MODES.map(p => <option key={p} value={p}>{p || '—'}</option>)}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
variant="default"
|
||||||
|
size="sm"
|
||||||
|
className="h-7"
|
||||||
|
disabled={batchSaving || (!batchModel && !batchEffort && !batchColor && !batchPermission)}
|
||||||
|
onClick={handleBatchApply}
|
||||||
>
|
>
|
||||||
<option value="">—</option>
|
{batchSaving ? 'Applying...' : `Apply to ${batchType === 'all' ? 'All' : batchType} (${batchTargets.length})`}
|
||||||
<option value="low">low</option>
|
</Button>
|
||||||
<option value="medium">medium</option>
|
|
||||||
<option value="high">high</option>
|
|
||||||
<option value="max">max (claude only)</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
<Button
|
|
||||||
variant="default"
|
|
||||||
size="sm"
|
|
||||||
className="h-7"
|
|
||||||
disabled={batchSaving || (!batchModel && !batchEffort)}
|
|
||||||
onClick={handleBatchApply}
|
|
||||||
>
|
|
||||||
{batchSaving ? 'Applying...' : 'Apply to All'}
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Agent list */}
|
{/* Agent list */}
|
||||||
|
|||||||
Reference in New Issue
Block a user