feat(cli): TUI slash command parsing + local commands (P8-009) (#176)
All checks were successful
ci/woodpecker/push/ci Pipeline was successful

Co-authored-by: Jason Woltje <jason@diversecanvas.com>
Co-committed-by: Jason Woltje <jason@diversecanvas.com>
This commit was merged in pull request #176.
This commit is contained in:
2026-03-16 01:58:56 +00:00
committed by jason.woltje
parent 5a1991924c
commit f0741e045f
11 changed files with 328 additions and 7 deletions

View File

@@ -1,4 +1,4 @@
import { useState, useEffect, useRef, useCallback } from 'react';
import { type MutableRefObject, useState, useEffect, useRef, useCallback } from 'react';
import { io, type Socket } from 'socket.io-client';
import type {
ServerToClientEvents,
@@ -11,7 +11,9 @@ import type {
ToolEndPayload,
SessionInfoPayload,
ErrorPayload,
CommandManifestPayload,
} from '@mosaic/types';
import { commandRegistry } from '../commands/index.js';
export interface ToolCall {
toolCallId: string;
@@ -20,7 +22,7 @@ export interface ToolCall {
}
export interface Message {
role: 'user' | 'assistant' | 'thinking' | 'tool';
role: 'user' | 'assistant' | 'thinking' | 'tool' | 'system';
content: string;
timestamp: Date;
toolCalls?: ToolCall[];
@@ -46,6 +48,8 @@ export interface UseSocketOptions {
agentId?: string;
}
type TypedSocket = Socket<ServerToClientEvents, ClientToServerEvents>;
export interface UseSocketReturn {
connected: boolean;
connecting: boolean;
@@ -61,14 +65,14 @@ export interface UseSocketReturn {
thinkingLevel: string;
availableThinkingLevels: string[];
sendMessage: (content: string) => void;
addSystemMessage: (content: string) => void;
setThinkingLevel: (level: string) => void;
switchConversation: (id: string) => void;
clearMessages: () => void;
connectionError: string | null;
socketRef: MutableRefObject<TypedSocket | null>;
}
type TypedSocket = Socket<ServerToClientEvents, ClientToServerEvents>;
const EMPTY_USAGE: TokenUsage = {
input: 0,
output: 0,
@@ -222,6 +226,10 @@ export function useSocket(opts: UseSocketOptions): UseSocketReturn {
setIsStreaming(false);
});
socket.on('commands:manifest', (data: CommandManifestPayload) => {
commandRegistry.updateManifest(data.manifest);
});
return () => {
socket.disconnect();
};
@@ -245,6 +253,10 @@ export function useSocket(opts: UseSocketOptions): UseSocketReturn {
[conversationId, isStreaming],
);
const addSystemMessage = useCallback((content: string) => {
setMessages((msgs) => [...msgs, { role: 'system', content, timestamp: new Date() }]);
}, []);
const setThinkingLevel = useCallback((level: string) => {
const cid = conversationIdRef.current;
if (!socketRef.current?.connected || !cid) return;
@@ -285,9 +297,11 @@ export function useSocket(opts: UseSocketOptions): UseSocketReturn {
thinkingLevel,
availableThinkingLevels,
sendMessage,
addSystemMessage,
setThinkingLevel,
switchConversation,
clearMessages,
connectionError,
socketRef,
};
}