feat(chat): align command, shell, and subtask UX (#444)

* feat: reload interface after skills operations

- Adds configurable delay before interface reload after skills changes
- Introduces polling to wait for application health after reload
- Updates UI to show reload message when installing or modifying skills

* feat: distinguish skills from commands in UI

- Displays skill badge for commands that are registered skills
- Prevents editing skills through command management interface
- Triggers interface reload after skill operations to reflect changes

* feat(chat): align command and subtask UX with opencode parity

Route commands/shell via parity paths, render delegated subtasks cleanly, and surface child-session permission/question prompts in parent chat.

* fix(ProjectEditDialog): improve layout consistency

* fix: update task icon and session handling in ToolPart

* feat(chat): add shell-mode input and collapse shell bridge output

Switch leading ! to shell mode UX and fold synthetic shell bridge assistant messages into the user shell bubble with inline output actions.

* fix: remove AI agent icon from file mention autocomplete

* fix: remove unused icon import from file mention component
This commit is contained in:
Bohdan Triapitsyn
2026-02-18 20:08:42 +02:00
committed by GitHub
parent e4a2486312
commit 85f21cb945
23 changed files with 1557 additions and 318 deletions
+36 -9
View File
@@ -2004,45 +2004,48 @@ export async function handleBridgeMessage(message: BridgeRequest, ctx?: BridgeCo
const scope: SkillScope | undefined = scopeValue === 'project' ? SKILL_SCOPE.PROJECT : scopeValue === 'user' ? SKILL_SCOPE.USER : undefined;
const normalizedSource = sourceValue === 'agents' ? 'agents' : 'opencode';
createSkill(skillName, { ...(body || {}), source: normalizedSource } as Record<string, unknown>, workingDirectory, scope);
// Skills are just files - OpenCode loads them on-demand, no restart needed
await ctx?.manager?.restart();
return {
id,
type,
success: true,
data: {
success: true,
requiresReload: false,
message: `Skill ${skillName} created successfully`,
requiresReload: true,
message: `Skill ${skillName} created successfully. Reloading interface…`,
reloadDelayMs: CLIENT_RELOAD_DELAY_MS,
},
};
}
if (normalizedMethod === 'PATCH') {
updateSkill(skillName, (body || {}) as Record<string, unknown>, workingDirectory);
// Skills are just files - OpenCode loads them on-demand, no restart needed
await ctx?.manager?.restart();
return {
id,
type,
success: true,
data: {
success: true,
requiresReload: false,
message: `Skill ${skillName} updated successfully`,
requiresReload: true,
message: `Skill ${skillName} updated successfully. Reloading interface…`,
reloadDelayMs: CLIENT_RELOAD_DELAY_MS,
},
};
}
if (normalizedMethod === 'DELETE') {
deleteSkill(skillName, workingDirectory);
// Skills are just files - OpenCode loads them on-demand, no restart needed
await ctx?.manager?.restart();
return {
id,
type,
success: true,
data: {
success: true,
requiresReload: false,
message: `Skill ${skillName} deleted successfully`,
requiresReload: true,
message: `Skill ${skillName} deleted successfully. Reloading interface…`,
reloadDelayMs: CLIENT_RELOAD_DELAY_MS,
},
};
}
@@ -2117,6 +2120,30 @@ export async function handleBridgeMessage(message: BridgeRequest, ctx?: BridgeCo
conflictDecisions: body.conflictDecisions,
});
if (data.ok) {
const installed = data.installed || [];
const skipped = data.skipped || [];
const requiresReload = installed.length > 0;
if (requiresReload) {
await ctx?.manager?.restart();
}
return {
id,
type,
success: true,
data: {
ok: true,
installed,
skipped,
requiresReload,
message: requiresReload ? 'Skills installed successfully. Reloading interface…' : 'No skills were installed',
reloadDelayMs: requiresReload ? CLIENT_RELOAD_DELAY_MS : undefined,
},
};
}
return { id, type, success: true, data };
}