web+coder: segmented per-agent slash menu (agent commands + skills) + cross-agent skill execution
Coder / menu now shows two groups: the active agent's commands first (manifest + live ACP available_commands), BooCoder skills second. SlashCommandPicker gains an opt-in groups prop (flat items path unchanged -> BooChat byte-identical, parity verified); ChatInput takes slashGroups; CoderPane builds the groups. Skills run under the selected agent: coder skill_invoke accepts a provider and, when external, injects the server-side skill body into a dispatched task instead of native inference. Also folds in the initial-chat skill fix (handleLandingSkill: create chat -> assign to pane -> invoke, same transition as a text send) that resolves the landing-page blank screen. BooChat slash menu + skill invocation unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import { api } from '@/api/client';
|
||||
import { ChatInput } from '@/components/ChatInput';
|
||||
|
||||
interface Props {
|
||||
@@ -9,6 +8,10 @@ interface Props {
|
||||
agentId?: string | null;
|
||||
onAgentChange?: (agentId: string | null) => void | Promise<void>;
|
||||
onSend: (content: string) => void;
|
||||
// Slash-command (skill) send from the landing page. The parent creates the
|
||||
// chat, assigns it to the pane (so it transitions to ChatPane), and invokes
|
||||
// the skill — same transition the text send uses. See useSessionChats.
|
||||
onSkillInvoke: (skillName: string, userMessage: string | null) => void;
|
||||
createChat: () => Promise<{ id: string }>;
|
||||
}
|
||||
|
||||
@@ -18,6 +21,7 @@ export function SessionLandingPage({
|
||||
agentId,
|
||||
onAgentChange,
|
||||
onSend,
|
||||
onSkillInvoke,
|
||||
createChat,
|
||||
}: Props) {
|
||||
const [chatId, setChatId] = useState<string | null>(null);
|
||||
@@ -45,14 +49,13 @@ export function SessionLandingPage({
|
||||
}
|
||||
}, [ensureChat, onSend]);
|
||||
|
||||
const handleSlashCommand = useCallback(async (skillName: string, userMessage: string) => {
|
||||
try {
|
||||
const cid = await ensureChat();
|
||||
await api.chats.skillInvoke(cid, skillName, userMessage.length > 0 ? userMessage : null);
|
||||
} catch (err) {
|
||||
toast.error(err instanceof Error ? err.message : `/${skillName} failed`);
|
||||
}
|
||||
}, [ensureChat]);
|
||||
// Route to the parent, which creates the chat, assigns it to the pane (so the
|
||||
// pane transitions to ChatPane and subscribes to the stream), then invokes the
|
||||
// skill — mirroring the text-send transition. Doing the skill invoke locally
|
||||
// (without the pane assignment) left the landing pane stuck/blank.
|
||||
const handleSlashCommand = useCallback((skillName: string, userMessage: string) => {
|
||||
onSkillInvoke(skillName, userMessage.length > 0 ? userMessage : null);
|
||||
}, [onSkillInvoke]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full min-h-0">
|
||||
|
||||
Reference in New Issue
Block a user