fix: address review findings (#5139)

This commit is contained in:
Jinjing
2026-06-10 17:54:33 -07:00
committed by GitHub
parent 26aa473bca
commit 0f774610cc
11 changed files with 211 additions and 41 deletions
@@ -164,6 +164,61 @@ describe('CommitMessageAiPane', () => {
expect(getAgentCatalogForAction('fixChecks', null).map((agent) => agent.id)).toContain('aider')
})
it('explains which agents are supported for text-generation recipes', () => {
const markup = renderPane(
buildSettings({
sourceControlAi: {
enabled: true,
agentId: null,
selectedModelByAgent: {},
selectedModelByAgentByHost: {},
discoveredModelsByAgent: {},
discoveredModelsByAgentByHost: {},
selectedThinkingByModel: {},
instructionsByOperation: {},
customAgentCommand: '',
actions: {},
prCreationDefaults: {},
launchActionDefaults: {}
}
})
)
expect(markup).toContain('Supported agents for this recipe:')
expect(markup).toContain('Claude, Codex')
expect(markup).toContain('Custom command')
})
it('marks an unsupported saved text-recipe agent with the supported alternatives', () => {
const markup = renderPane(
buildSettings({
sourceControlAi: {
enabled: true,
agentId: null,
selectedModelByAgent: {},
selectedModelByAgentByHost: {},
discoveredModelsByAgent: {},
discoveredModelsByAgentByHost: {},
selectedThinkingByModel: {},
instructionsByOperation: {},
customAgentCommand: '',
actions: {
commitMessage: {
agentId: 'aider'
}
},
prCreationDefaults: {},
launchActionDefaults: {}
}
})
)
expect(markup).toContain(
'Aider cannot run this text-generation recipe. Pick one of the supported agents below.'
)
expect(markup).toContain('Supported agents for this recipe:')
})
it('keeps action agent selectors constrained for long labels', () => {
const markup = renderPane(
buildSettings({
@@ -20,6 +20,8 @@ import {
getActionDescriptions,
SOURCE_CONTROL_TEXT_ACTION_ID_SET,
getAgentCatalogForAction,
getSourceControlActionAgentSupportText,
getSourceControlActionAgentWarningText,
getSourceControlAgentArgsPlaceholder
} from './source-control-action-recipe-options'
import {
@@ -86,6 +88,8 @@ export function RepositorySourceControlAiActionRows({
resolveAgentArgsPlaceholderAgent(effectiveAgent, source, actionId, defaultTuiAgent)
)
const agentOptions = getAgentCatalogForAction(actionId, effectiveAgent)
const agentWarningText = getSourceControlActionAgentWarningText(actionId, effectiveAgent)
const agentSupportText = getSourceControlActionAgentSupportText(actionId)
return (
<div key={actionId} className="space-y-3 rounded-md border border-border px-3 py-3">
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
@@ -180,6 +184,11 @@ export function RepositorySourceControlAiActionRows({
))}
</SelectContent>
</Select>
{agentWarningText ? (
<p className="text-[11px] text-destructive">{agentWarningText}</p>
) : agentSupportText ? (
<p className="text-[11px] text-muted-foreground">{agentSupportText}</p>
) : null}
<Label className="text-[11px] text-muted-foreground">
{translate(
'auto.components.settings.RepositorySourceControlAiActionRows.7a3a8e431d',
@@ -17,6 +17,8 @@ import {
getActionDescriptions,
SOURCE_CONTROL_TEXT_ACTION_ID_SET,
getAgentCatalogForAction,
getSourceControlActionAgentSupportText,
getSourceControlActionAgentWarningText,
getSourceControlAgentArgsPlaceholder
} from './source-control-action-recipe-options'
import { translate } from '@/i18n/i18n'
@@ -67,6 +69,8 @@ export function SourceControlActionRecipeRow({
resolveAgentArgsPlaceholderAgent(selectedAgent, defaultTuiAgent)
)
const agentOptions = getAgentCatalogForAction(actionId, selectedAgent)
const agentWarningText = getSourceControlActionAgentWarningText(actionId, selectedAgent)
const agentSupportText = getSourceControlActionAgentSupportText(actionId)
return (
<div className="rounded-md border border-border px-3 py-3">
@@ -77,44 +81,51 @@ export function SourceControlActionRecipeRow({
</p>
<p className="text-[11px] text-muted-foreground">{getActionDescriptions()[actionId]}</p>
</div>
<Select
value={selectedAgent ?? DEFAULT_AGENT_VALUE}
onValueChange={(value) => onAgentChange(actionId, value)}
>
<SelectTrigger size="sm" className="h-8 w-full shrink-0 text-xs sm:w-[220px]">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value={DEFAULT_AGENT_VALUE}>
<span className="flex items-center gap-2">
<Terminal className="size-3.5 text-muted-foreground" />
{translate(
'auto.components.settings.SourceControlAiActionRecipeDefaults.ee0e5c2a48',
'Use default agent'
)}
</span>
</SelectItem>
{SOURCE_CONTROL_TEXT_ACTION_ID_SET.has(actionId) ? (
<SelectItem value={CUSTOM_AGENT_ID}>
<div className="w-full shrink-0 space-y-1 sm:w-[220px]">
<Select
value={selectedAgent ?? DEFAULT_AGENT_VALUE}
onValueChange={(value) => onAgentChange(actionId, value)}
>
<SelectTrigger size="sm" className="h-8 w-full text-xs">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value={DEFAULT_AGENT_VALUE}>
<span className="flex items-center gap-2">
<Terminal className="size-3.5 text-muted-foreground" />
{translate(
'auto.components.settings.SourceControlAiActionRecipeDefaults.0740d30915',
'Custom command'
'auto.components.settings.SourceControlAiActionRecipeDefaults.ee0e5c2a48',
'Use default agent'
)}
</span>
</SelectItem>
) : null}
{agentOptions.map((agent) => (
<SelectItem key={agent.id} value={agent.id}>
<span className="flex items-center gap-2">
<AgentIcon agent={agent.id} size={14} />
{agent.label}
</span>
</SelectItem>
))}
</SelectContent>
</Select>
{SOURCE_CONTROL_TEXT_ACTION_ID_SET.has(actionId) ? (
<SelectItem value={CUSTOM_AGENT_ID}>
<span className="flex items-center gap-2">
<Terminal className="size-3.5 text-muted-foreground" />
{translate(
'auto.components.settings.SourceControlAiActionRecipeDefaults.0740d30915',
'Custom command'
)}
</span>
</SelectItem>
) : null}
{agentOptions.map((agent) => (
<SelectItem key={agent.id} value={agent.id}>
<span className="flex items-center gap-2">
<AgentIcon agent={agent.id} size={14} />
{agent.label}
</span>
</SelectItem>
))}
</SelectContent>
</Select>
{agentWarningText ? (
<p className="text-[11px] text-destructive">{agentWarningText}</p>
) : agentSupportText ? (
<p className="text-[11px] text-muted-foreground">{agentSupportText}</p>
) : null}
</div>
</div>
<div className="mt-3 grid gap-3 sm:grid-cols-[220px_1fr]">
<div className="space-y-2">
@@ -7,6 +7,7 @@ import {
CUSTOM_AGENT_ID,
type CustomAgentId,
getCommitMessageAgentCapability,
isCustomAgentId,
listCommitMessageAgentCapabilities
} from '../../../../shared/commit-message-agent-spec'
import { getAgentCatalog, type AgentCatalogEntry } from '@/lib/agent-catalog'
@@ -98,3 +99,49 @@ export function getAgentCatalogForAction(
(agent) => TEXT_GENERATION_AGENT_ID_SET.has(agent.id) || agent.id === selectedAgent
)
}
function formatSupportedAgentLabels(): string {
return [
...listCommitMessageAgentCapabilities().map((capability) => capability.label),
translate(
'auto.components.settings.source.control.action.recipe.options.customCommand',
'Custom command'
)
].join(', ')
}
export function getSourceControlActionAgentSupportText(
actionId: SourceControlActionId
): string | null {
if (!SOURCE_CONTROL_TEXT_ACTION_ID_SET.has(actionId)) {
return null
}
return translate(
'auto.components.settings.source.control.action.recipe.options.supportedAgents',
'Supported agents for this recipe: {{value0}}.',
{ value0: formatSupportedAgentLabels() }
)
}
export function getSourceControlActionAgentWarningText(
actionId: SourceControlActionId,
selectedAgent: TuiAgent | CustomAgentId | null | undefined
): string | null {
if (!SOURCE_CONTROL_TEXT_ACTION_ID_SET.has(actionId)) {
return null
}
if (selectedAgent && !isCustomAgentId(selectedAgent)) {
if (TEXT_GENERATION_AGENT_ID_SET.has(selectedAgent)) {
return null
}
const agentLabel = getAgentCatalog().find((agent) => agent.id === selectedAgent)?.label
return translate(
'auto.components.settings.source.control.action.recipe.options.unsupportedSavedAgent',
'{{value0}} cannot run this text-generation recipe. Pick one of the supported agents below.',
{ value0: agentLabel ?? selectedAgent }
)
}
return null
}
+4 -1
View File
@@ -7266,7 +7266,10 @@
"branchName": "Rename Orca-created branches from the initial agent task.",
"fixCommitFailure": "Start an agent when a commit hook or git commit fails.",
"fixChecks": "Start an agent from failed hosted-review checks.",
"resolveConflicts": "Start an agent for local or hosted-review merge conflicts."
"resolveConflicts": "Start an agent for local or hosted-review merge conflicts.",
"customCommand": "Custom command",
"supportedAgents": "Supported agents for this recipe: {{value0}}.",
"unsupportedSavedAgent": "{{value0}} cannot run this text-generation recipe. Pick one of the supported agents below."
}
}
}
+4 -1
View File
@@ -7230,7 +7230,10 @@
"branchName": "Cambie el nombre de las ramas creadas por Orca desde la tarea inicial del agente.",
"fixCommitFailure": "Inicie un agente cuando falle un enlace de confirmación o una confirmación de git.",
"fixChecks": "Inicie un agente a partir de comprobaciones fallidas de revisión alojada.",
"resolveConflicts": "Inicie un agente para conflictos de fusión de revisión local o alojada."
"resolveConflicts": "Inicie un agente para conflictos de fusión de revisión local o alojada.",
"customCommand": "Custom command",
"supportedAgents": "Supported agents for this recipe: {{value0}}.",
"unsupportedSavedAgent": "{{value0}} cannot run this text-generation recipe. Pick one of the supported agents below."
}
}
}
+4 -1
View File
@@ -7251,7 +7251,10 @@
"branchName": "Orca が最初のエージェント タスクから作成したブランチの名前を変更します。",
"fixCommitFailure": "コミットフックまたは git コミットが失敗したときにエージェントを開始します。",
"fixChecks": "失敗したホスト型レビュー チェックからエージェントを開始します。",
"resolveConflicts": "ローカルまたはホストされたレビューのマージ競合に対してエージェントを開始します。"
"resolveConflicts": "ローカルまたはホストされたレビューのマージ競合に対してエージェントを開始します。",
"customCommand": "Custom command",
"supportedAgents": "Supported agents for this recipe: {{value0}}.",
"unsupportedSavedAgent": "{{value0}} cannot run this text-generation recipe. Pick one of the supported agents below."
}
}
}
+4 -1
View File
@@ -7215,7 +7215,10 @@
"branchName": "초기 에이전트 작업에서 Orca가 생성한 브랜치의 이름을 바꿉니다.",
"fixCommitFailure": "커밋 후크 또는 git 커밋이 실패하면 에이전트를 시작합니다.",
"fixChecks": "실패한 호스팅 PR 체크에서 에이전트를 시작합니다.",
"resolveConflicts": "로컬 또는 호스팅 PR 병합 충돌에 대한 에이전트를 시작합니다."
"resolveConflicts": "로컬 또는 호스팅 PR 병합 충돌에 대한 에이전트를 시작합니다.",
"customCommand": "Custom command",
"supportedAgents": "Supported agents for this recipe: {{value0}}.",
"unsupportedSavedAgent": "{{value0}} cannot run this text-generation recipe. Pick one of the supported agents below."
}
}
}
+4 -1
View File
@@ -7215,7 +7215,10 @@
"branchName": "重命名 Orca 从初始代理任务创建的分支。",
"fixCommitFailure": "当提交挂钩或 git 提交失败时启动代理。",
"fixChecks": "从失败的托管评审检查中启动代理。",
"resolveConflicts": "启动用于解决本地或托管评审合并冲突的代理。"
"resolveConflicts": "启动用于解决本地或托管评审合并冲突的代理。",
"customCommand": "Custom command",
"supportedAgents": "Supported agents for this recipe: {{value0}}.",
"unsupportedSavedAgent": "{{value0}} cannot run this text-generation recipe. Pick one of the supported agents below."
}
}
}
@@ -403,4 +403,31 @@ describe('source-control AI action recipes', () => {
error: 'Command template is empty for commit messages.'
})
})
it('lists supported agents when a text action uses an unsupported saved agent', () => {
const base = settings()
base.sourceControlAi = {
...base.sourceControlAi!,
actions: {
...base.sourceControlAi!.actions,
commitMessage: {
agentId: 'aider',
commandInputTemplate: '{basePrompt}'
}
}
}
expect(
resolveSourceControlAiForOperation({
settings: base,
repo: null,
operation: 'commitMessage',
discoveryHostKey: 'local'
})
).toEqual({
ok: false,
error:
'Agent "aider" does not support Source Control AI commit messages. Supported agents: Claude, Codex, OpenCode, Pi, Amp, Cursor, Kimi, GitHub Copilot, Antigravity, or Custom command.'
})
})
})
+10 -4
View File
@@ -5,6 +5,7 @@ import {
CUSTOM_AGENT_ID,
getCommitMessageAgentSpec,
getCommitMessageModel,
listCommitMessageAgentCapabilities,
type CustomAgentId,
isCustomAgentId,
resolveCommitMessageAgentChoice
@@ -104,6 +105,12 @@ const PR_CREATION_DEFAULT_KEYS = [
'openAfterCreate'
] as const
function supportedSourceControlAiAgentSummary(): string {
return `Supported agents: ${listCommitMessageAgentCapabilities()
.map((capability) => capability.label)
.join(', ')}, or Custom command.`
}
function copyRecord<T>(value: T | undefined): T | undefined {
return value === undefined ? undefined : structuredClone(value)
}
@@ -1171,8 +1178,7 @@ export function resolveSourceControlAiForOperation(
if (!agentChoice) {
return {
ok: false,
error:
'Choose a supported Source Control AI agent for this action in Settings -> Git -> Source Control AI.'
error: `Choose a supported Source Control AI agent for this action in Settings -> Git -> Source Control AI. ${supportedSourceControlAiAgentSummary()}`
}
}
@@ -1220,14 +1226,14 @@ export function resolveSourceControlAiForOperation(
if (!resolvedActionAgentId || isCustomAgentId(resolvedActionAgentId)) {
return {
ok: false,
error: 'Choose a supported Source Control AI agent for this action.'
error: `Choose a supported Source Control AI agent for this action. ${supportedSourceControlAiAgentSummary()}`
}
}
const spec = getCommitMessageAgentSpec(resolvedActionAgentId)
if (!spec) {
return {
ok: false,
error: `Agent "${resolvedActionAgentId}" does not support Source Control AI ${OPERATION_LABEL[input.operation]}.`
error: `Agent "${resolvedActionAgentId}" does not support Source Control AI ${OPERATION_LABEL[input.operation]}. ${supportedSourceControlAiAgentSummary()}`
}
}