From 8cfc71139e5946688c137989170e90c221114091 Mon Sep 17 00:00:00 2001 From: catlog22 Date: Tue, 24 Mar 2026 21:14:35 +0800 Subject: [PATCH] feat: expand batch controls with full claude-only advanced settings Batch now has a gear toggle for Claude advanced fields: - Quick settings: color, permission, memory, maxTurns, background, isolation - Tools row: tools, disallowedTools, skills - When target=All, note indicates claude-only fields apply to claude agents only - When target=Codex, advanced section hidden entirely Co-Authored-By: Claude Opus 4.6 --- .../settings/AgentDefinitionsSection.tsx | 146 +++++++++++++++--- 1 file changed, 124 insertions(+), 22 deletions(-) diff --git a/ccw/frontend/src/components/settings/AgentDefinitionsSection.tsx b/ccw/frontend/src/components/settings/AgentDefinitionsSection.tsx index 8b96a5d4..3cff07bc 100644 --- a/ccw/frontend/src/components/settings/AgentDefinitionsSection.tsx +++ b/ccw/frontend/src/components/settings/AgentDefinitionsSection.tsx @@ -625,6 +625,14 @@ export function AgentDefinitionsSection() { const [batchType, setBatchType] = useState<'all' | 'codex' | 'claude'>('all'); const [batchColor, setBatchColor] = useState(''); const [batchPermission, setBatchPermission] = useState(''); + const [batchMemory, setBatchMemory] = useState(''); + const [batchMaxTurns, setBatchMaxTurns] = useState(''); + const [batchBackground, setBatchBackground] = useState<'' | 'true' | 'false'>(''); + const [batchIsolation, setBatchIsolation] = useState(''); + const [batchTools, setBatchTools] = useState(''); + const [batchDisallowedTools, setBatchDisallowedTools] = useState(''); + const [batchSkills, setBatchSkills] = useState(''); + const [batchShowAdvanced, setBatchShowAdvanced] = useState(false); const [batchSaving, setBatchSaving] = useState(false); const loadAgents = useCallback(async () => { @@ -657,8 +665,21 @@ export function AgentDefinitionsSection() { ? CLAUDE_EFFORTS : ['', 'low', 'medium', 'high', 'max (claude only)']; + // Collect all claude-only batch values into a single object for easier checking + const claudeBatchFields: Record = {}; + if (batchColor) claudeBatchFields.color = batchColor; + if (batchPermission) claudeBatchFields.permissionMode = batchPermission; + if (batchMemory) claudeBatchFields.memory = batchMemory; + if (batchMaxTurns) claudeBatchFields.maxTurns = batchMaxTurns; + if (batchBackground) claudeBatchFields.background = batchBackground; + if (batchIsolation) claudeBatchFields.isolation = batchIsolation; + if (batchTools) claudeBatchFields.tools = batchTools; + if (batchDisallowedTools) claudeBatchFields.disallowedTools = batchDisallowedTools; + if (batchSkills) claudeBatchFields.skills = batchSkills; + const hasClaudeBatchFields = Object.keys(claudeBatchFields).length > 0; + const handleBatchApply = useCallback(async () => { - const hasAnyValue = batchModel || batchEffort || batchColor || batchPermission; + const hasAnyValue = batchModel || batchEffort || hasClaudeBatchFields; if (!hasAnyValue) { toast.error('Set at least one value first'); return; @@ -670,7 +691,7 @@ export function AgentDefinitionsSection() { setBatchSaving(true); try { - // For model/effort, use the batch endpoint + // For model/effort, use the batch endpoint (works for both codex and claude) if (batchModel || batchEffort) { const targets = batchTargets.map(a => ({ filePath: a.filePath, type: a.type })); const result = await batchUpdateAgentDefinitions({ @@ -681,15 +702,16 @@ export function AgentDefinitionsSection() { 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) { + // For claude-only fields, update each claude agent individually + if (hasClaudeBatchFields && 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; + const body: { filePath: string; [key: string]: string | undefined } = { + filePath: agent.filePath, + ...claudeBatchFields, + }; await updateAgentDefinition(agent.type, agent.name, body); updated++; } catch { /* skip failed */ } @@ -703,7 +725,7 @@ export function AgentDefinitionsSection() { } finally { setBatchSaving(false); } - }, [batchTargets, batchHasClaudeTargets, batchModel, batchEffort, batchColor, batchPermission, loadAgents]); + }, [batchTargets, batchHasClaudeTargets, batchModel, batchEffort, hasClaudeBatchFields, claudeBatchFields, loadAgents]); return ( @@ -720,6 +742,7 @@ export function AgentDefinitionsSection() { {/* Batch Controls */}
+ {/* Row 1: shared fields (all agent types) */}
Batch:
@@ -756,9 +779,37 @@ export function AgentDefinitionsSection() { })}
- {/* Claude-only batch fields */} + {/* Advanced toggle (claude targets only) */} {batchType !== 'codex' && ( - <> + + )} + +
+ + {/* Row 2: Claude-only advanced batch fields */} + {batchShowAdvanced && batchType !== 'codex' && ( +
+ Claude-only fields {batchType === 'all' && '(applied to claude agents only)'}: + {/* Quick settings */} +
Color:
- - )} - -
+
+ Memory: + +
+
+ MaxTurns: + setBatchMaxTurns(e.target.value)} + placeholder="—" + /> +
+
+ Background: + +
+
+ Isolation: + +
+
+ {/* Tools row */} +
+
+ Tools: + setBatchTools(e.target.value)} + placeholder="Read, Write, Bash, Glob, Grep" + /> +
+
+ Disallowed: + setBatchDisallowedTools(e.target.value)} + placeholder="e.g. Edit" + /> +
+
+ Skills: + setBatchSkills(e.target.value)} + placeholder="e.g. api-conventions" + /> +
+
+
+ )} {/* Agent list */}