fix: make name/emoji/persona optional, auto-skip empty steps, back button

- Make name, emoji, persona params optional in dedicated-channel-agent
- Auto-skip steps whose template args resolve to empty strings
- Move "Back to Recipes" to a back arrow next to the title

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
zhixian
2026-02-17 23:18:20 +09:00
parent dbf1e69937
commit a373a7c068
3 changed files with 30 additions and 13 deletions

View File

@@ -11,9 +11,9 @@
{ "id": "agent_id", "label": "Agent ID", "type": "string", "required": true, "placeholder": "e.g. my-bot" },
{ "id": "guild_id", "label": "Guild", "type": "discord_guild", "required": true },
{ "id": "channel_id", "label": "Channel", "type": "discord_channel", "required": true },
{ "id": "name", "label": "Display Name", "type": "string", "required": true, "placeholder": "e.g. MyBot" },
{ "id": "name", "label": "Display Name", "type": "string", "required": false, "placeholder": "e.g. MyBot" },
{ "id": "emoji", "label": "Emoji", "type": "string", "required": false, "placeholder": "e.g. \ud83e\udd16" },
{ "id": "persona", "label": "Persona", "type": "textarea", "required": true, "placeholder": "You are..." }
{ "id": "persona", "label": "Persona", "type": "textarea", "required": false, "placeholder": "You are..." }
],
"steps": [
{ "action": "create_agent", "label": "Create independent agent", "args": { "agentId": "{{agent_id}}", "independent": true } },

View File

@@ -81,6 +81,7 @@ export interface ResolvedStep {
label: string;
args: Record<string, unknown>;
description: string;
skippable: boolean;
}
export function resolveSteps(
@@ -92,6 +93,14 @@ export function resolveSteps(
if (step.action === "config_patch") {
resolved.params = params;
}
// A step is skippable if any of its template args resolved to empty string
const skippable = Object.entries(step.args).some(([key, origValue]) => {
if (typeof origValue === "string" && origValue.includes("{{")) {
const rv = resolved[key];
return typeof rv === "string" && rv.trim() === "";
}
return false;
});
const actionDef = getAction(step.action);
const description = actionDef?.describe(resolved) || step.label;
return {
@@ -100,6 +109,7 @@ export function resolveSteps(
label: step.label,
args: resolved,
description: description || step.label,
skippable,
};
});
}

View File

@@ -48,9 +48,10 @@ export function Cook({
const handleNext = () => {
const steps = resolveSteps(recipe.steps, params);
setResolvedStepList(steps);
setStepStatuses(steps.map(() => "pending"));
// Auto-skip steps whose template args resolved to empty
setStepStatuses(steps.map((s) => (s.skippable ? "skipped" : "pending")));
setStepErrors({});
setHasConfigPatch(steps.some((s) => s.action === "config_patch"));
setHasConfigPatch(steps.some((s) => !s.skippable && s.action === "config_patch"));
setPhase("confirm");
};
@@ -75,8 +76,7 @@ export function Cook({
const handleExecute = () => {
setPhase("execute");
const statuses: StepStatus[] = resolvedStepList.map(() => "pending");
setStepStatuses([...statuses]);
const statuses = [...stepStatuses];
runFrom(0, statuses);
};
@@ -131,7 +131,12 @@ export function Cook({
return (
<section>
<h2 className="text-2xl font-bold mb-4">{recipe.name}</h2>
<div className="flex items-center gap-2 mb-4">
<Button variant="ghost" size="sm" className="px-2" onClick={onDone}>
&larr;
</Button>
<h2 className="text-2xl font-bold">{recipe.name}</h2>
</div>
{phase === "params" && (
<ParamForm
@@ -149,13 +154,18 @@ export function Cook({
<CardContent>
<div className="space-y-3">
{resolvedStepList.map((step, i) => (
<div key={i} className="flex items-start gap-3">
<div key={i} className={cn("flex items-start gap-3", stepStatuses[i] === "skipped" && "opacity-50")}>
<span className={cn("text-lg font-mono w-5 text-center", statusColor(stepStatuses[i]))}>
{statusIcon(stepStatuses[i])}
</span>
<div className="flex-1">
<div className="text-sm font-medium">{step.label}</div>
{step.description !== step.label && (
<div className="text-sm font-medium">
{step.label}
{stepStatuses[i] === "skipped" && phase === "confirm" && (
<span className="text-xs text-muted-foreground ml-2">(skipped empty params)</span>
)}
</div>
{step.description !== step.label && stepStatuses[i] !== "skipped" && (
<div className="text-xs text-muted-foreground">{step.description}</div>
)}
{stepErrors[i] && (
@@ -198,9 +208,6 @@ export function Cook({
Use "Apply Changes" in the sidebar to restart the gateway and activate config changes.
</p>
)}
<Button className="mt-4" onClick={onDone}>
Back to Recipes
</Button>
</CardContent>
</Card>
)}