feat: Add intelligent auto-router and enhanced integrations
- Add intelligent-router.sh hook for automatic agent routing - Add AUTO-TRIGGER-SUMMARY.md documentation - Add FINAL-INTEGRATION-SUMMARY.md documentation - Complete Prometheus integration (6 commands + 4 tools) - Complete Dexto integration (12 commands + 5 tools) - Enhanced Ralph with access to all agents - Fix /clawd command (removed disable-model-invocation) - Update hooks.json to v5 with intelligent routing - 291 total skills now available - All 21 commands with automatic routing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
38
dexto/packages/webui/src/layouts/RootLayout.tsx
Normal file
38
dexto/packages/webui/src/layouts/RootLayout.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import { Outlet } from '@tanstack/react-router';
|
||||
import { HelmetProvider, Helmet } from 'react-helmet-async';
|
||||
import { QueryProvider } from '@/components/providers/QueryProvider';
|
||||
import { AnalyticsProvider } from '@/lib/analytics/index';
|
||||
import { EventBusProvider } from '@/components/providers/EventBusProvider';
|
||||
import { ApprovalProvider } from '@/components/hooks/ApprovalContext';
|
||||
import { ChatProvider } from '@/components/hooks/ChatContext';
|
||||
import { SpeechReset } from '@/components/ui/speech-reset';
|
||||
import { ToastContainer } from '@/components/Toast/ToastContainer';
|
||||
|
||||
export function RootLayout() {
|
||||
return (
|
||||
<HelmetProvider>
|
||||
<Helmet>
|
||||
<title>Dexto Web UI</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Interactive playground for testing MCP tools and talking to AI agents"
|
||||
/>
|
||||
</Helmet>
|
||||
<QueryProvider>
|
||||
<AnalyticsProvider>
|
||||
<EventBusProvider>
|
||||
<ApprovalProvider>
|
||||
<ChatProvider>
|
||||
<SpeechReset />
|
||||
<div className="flex h-screen w-screen flex-col supports-[height:100svh]:h-[100svh] supports-[height:100dvh]:h-[100dvh]">
|
||||
<Outlet />
|
||||
</div>
|
||||
<ToastContainer />
|
||||
</ChatProvider>
|
||||
</ApprovalProvider>
|
||||
</EventBusProvider>
|
||||
</AnalyticsProvider>
|
||||
</QueryProvider>
|
||||
</HelmetProvider>
|
||||
);
|
||||
}
|
||||
11
dexto/packages/webui/src/main.tsx
Normal file
11
dexto/packages/webui/src/main.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import { RouterProvider } from '@tanstack/react-router';
|
||||
import { router } from './router';
|
||||
import './styles/globals.css';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
<RouterProvider router={router} />
|
||||
</React.StrictMode>
|
||||
);
|
||||
17
dexto/packages/webui/src/pages/ChatPage.tsx
Normal file
17
dexto/packages/webui/src/pages/ChatPage.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { useParams } from '@tanstack/react-router';
|
||||
import { Helmet } from 'react-helmet-async';
|
||||
import ChatApp from '@/components/ChatApp';
|
||||
|
||||
export function ChatPage() {
|
||||
const { sessionId } = useParams({ from: '/chat/$sessionId' });
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>Chat · Dexto</title>
|
||||
<meta name="description" content={`Chat session ${sessionId}`} />
|
||||
</Helmet>
|
||||
<ChatApp sessionId={sessionId} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
5
dexto/packages/webui/src/pages/HomePage.tsx
Normal file
5
dexto/packages/webui/src/pages/HomePage.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import ChatApp from '@/components/ChatApp';
|
||||
|
||||
export function HomePage() {
|
||||
return <ChatApp />;
|
||||
}
|
||||
20
dexto/packages/webui/src/pages/NotFoundPage.tsx
Normal file
20
dexto/packages/webui/src/pages/NotFoundPage.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import { Link } from '@tanstack/react-router';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Home } from 'lucide-react';
|
||||
|
||||
export function NotFoundPage() {
|
||||
return (
|
||||
<div className="flex h-screen items-center justify-center bg-background">
|
||||
<div className="text-center space-y-6">
|
||||
<h1 className="text-6xl font-bold text-foreground">404</h1>
|
||||
<p className="text-xl text-muted-foreground">Page not found</p>
|
||||
<Button variant="default" className="gap-2" asChild>
|
||||
<Link to="/">
|
||||
<Home className="h-4 w-4" />
|
||||
Return Home
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
14
dexto/packages/webui/src/pages/PlaygroundPage.tsx
Normal file
14
dexto/packages/webui/src/pages/PlaygroundPage.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Helmet } from 'react-helmet-async';
|
||||
import PlaygroundView from '@/components/Playground/PlaygroundView';
|
||||
|
||||
export function PlaygroundPage() {
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>Playground · Dexto</title>
|
||||
<meta name="description" content="Test MCP tools in an interactive playground" />
|
||||
</Helmet>
|
||||
<PlaygroundView />
|
||||
</>
|
||||
);
|
||||
}
|
||||
42
dexto/packages/webui/src/router.tsx
Normal file
42
dexto/packages/webui/src/router.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import { createRouter, createRootRoute, createRoute } from '@tanstack/react-router';
|
||||
import { RootLayout } from './layouts/RootLayout';
|
||||
import { HomePage } from './pages/HomePage';
|
||||
import { ChatPage } from './pages/ChatPage';
|
||||
import { PlaygroundPage } from './pages/PlaygroundPage';
|
||||
import { NotFoundPage } from './pages/NotFoundPage';
|
||||
|
||||
const rootRoute = createRootRoute({
|
||||
component: RootLayout,
|
||||
notFoundComponent: NotFoundPage,
|
||||
});
|
||||
|
||||
const homeRoute = createRoute({
|
||||
getParentRoute: () => rootRoute,
|
||||
path: '/',
|
||||
component: HomePage,
|
||||
});
|
||||
|
||||
const chatRoute = createRoute({
|
||||
getParentRoute: () => rootRoute,
|
||||
path: '/chat/$sessionId',
|
||||
component: ChatPage,
|
||||
});
|
||||
|
||||
const playgroundRoute = createRoute({
|
||||
getParentRoute: () => rootRoute,
|
||||
path: '/playground',
|
||||
component: PlaygroundPage,
|
||||
});
|
||||
|
||||
const routeTree = rootRoute.addChildren([homeRoute, chatRoute, playgroundRoute]);
|
||||
|
||||
export const router = createRouter({
|
||||
routeTree,
|
||||
defaultPreload: 'intent',
|
||||
});
|
||||
|
||||
declare module '@tanstack/react-router' {
|
||||
interface Register {
|
||||
router: typeof router;
|
||||
}
|
||||
}
|
||||
287
dexto/packages/webui/src/styles/globals.css
Normal file
287
dexto/packages/webui/src/styles/globals.css
Normal file
@@ -0,0 +1,287 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@200;300;400;500;600;700;800&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap');
|
||||
|
||||
@import "tailwindcss";
|
||||
|
||||
@custom-variant dark (&:where(.dark, .dark *));
|
||||
|
||||
@theme inline {
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--font-sans: 'Nunito Sans', system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
--font-mono: 'JetBrains Mono', ui-monospace, monospace;
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
--color-sidebar-accent: var(--sidebar-accent);
|
||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||
--color-sidebar-primary: var(--sidebar-primary);
|
||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||
--color-sidebar: var(--sidebar);
|
||||
--color-chart-5: var(--chart-5);
|
||||
--color-chart-4: var(--chart-4);
|
||||
--color-chart-3: var(--chart-3);
|
||||
--color-chart-2: var(--chart-2);
|
||||
--color-chart-1: var(--chart-1);
|
||||
--color-ring: var(--ring);
|
||||
--color-input: var(--input);
|
||||
--color-border: var(--border);
|
||||
--color-destructive: var(--destructive);
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
--color-accent: var(--accent);
|
||||
--color-muted-foreground: var(--muted-foreground);
|
||||
--color-muted: var(--muted);
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
--color-secondary: var(--secondary);
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
--color-primary: var(--primary);
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
--color-popover: var(--popover);
|
||||
--color-card-foreground: var(--card-foreground);
|
||||
--color-card: var(--card);
|
||||
--radius-sm: calc(var(--radius) - 2px);
|
||||
--radius-md: var(--radius);
|
||||
--radius-lg: calc(var(--radius) + 2px);
|
||||
--radius-xl: calc(var(--radius) + 6px);
|
||||
}
|
||||
|
||||
:root {
|
||||
/* Tell browsers we handle light/dark modes ourselves */
|
||||
color-scheme: light dark;
|
||||
|
||||
--radius: 0.5rem;
|
||||
/* Slightly warmer off-white background for better contrast */
|
||||
--background: #faf9f7;
|
||||
--foreground: #1a1a1a;
|
||||
/* Better card contrast - pure white stands out more */
|
||||
--card: #ffffff;
|
||||
--card-foreground: #1a1a1a;
|
||||
--popover: #ffffff;
|
||||
--popover-foreground: #1a1a1a;
|
||||
--primary: #1a1a1a;
|
||||
--primary-foreground: #fafafa;
|
||||
/* More differentiated secondary/muted colors */
|
||||
--secondary: #f0efed;
|
||||
--secondary-foreground: #1a1a1a;
|
||||
--muted: #f0efed;
|
||||
--muted-foreground: #636363;
|
||||
--accent: #f0efed;
|
||||
--accent-foreground: #1a1a1a;
|
||||
--destructive: #ef4444;
|
||||
--destructive-foreground: #fafafa;
|
||||
/* Subtle warm borders */
|
||||
--border: #e5e4e1;
|
||||
--input: #ffffff;
|
||||
--ring: #1a1a1a;
|
||||
--chart-1: #3b82f6;
|
||||
--chart-2: #10b981;
|
||||
--chart-3: #f59e0b;
|
||||
--chart-4: #ef4444;
|
||||
--chart-5: #8b5cf6;
|
||||
/* Sidebar slightly different for depth */
|
||||
--sidebar: #f5f4f2;
|
||||
--sidebar-foreground: #1a1a1a;
|
||||
--sidebar-primary: #1a1a1a;
|
||||
--sidebar-primary-foreground: #fafafa;
|
||||
--sidebar-accent: #eaeae8;
|
||||
--sidebar-accent-foreground: #1a1a1a;
|
||||
--sidebar-border: #e5e4e1;
|
||||
--sidebar-ring: #1a1a1a;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: #0f0f0f;
|
||||
--foreground: #fafafa;
|
||||
--card: #141414;
|
||||
--card-foreground: #fafafa;
|
||||
--popover: #141414;
|
||||
--popover-foreground: #fafafa;
|
||||
--primary: #fafafa;
|
||||
--primary-foreground: #0f0f0f;
|
||||
--secondary: #262626;
|
||||
--secondary-foreground: #fafafa;
|
||||
--muted: #262626;
|
||||
--muted-foreground: #a1a1aa;
|
||||
--accent: #262626;
|
||||
--accent-foreground: #fafafa;
|
||||
--destructive: #dc2626;
|
||||
--destructive-foreground: #fafafa;
|
||||
--border: #525252;
|
||||
--input: #262626;
|
||||
--ring: #d4d4d8;
|
||||
--chart-1: #3b82f6;
|
||||
--chart-2: #10b981;
|
||||
--chart-3: #f59e0b;
|
||||
--chart-4: #ef4444;
|
||||
--chart-5: #8b5cf6;
|
||||
--sidebar: #141414;
|
||||
--sidebar-foreground: #fafafa;
|
||||
--sidebar-primary: #fafafa;
|
||||
--sidebar-primary-foreground: #0f0f0f;
|
||||
--sidebar-accent: #262626;
|
||||
--sidebar-accent-foreground: #fafafa;
|
||||
--sidebar-border: #525252;
|
||||
--sidebar-ring: #d4d4d8;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
html {
|
||||
/* Use Nunito Sans with system fallback */
|
||||
font-family: 'Nunito Sans', system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
|
||||
font-weight: 400;
|
||||
font-feature-settings: 'cv11', 'ss01';
|
||||
}
|
||||
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
|
||||
body {
|
||||
@apply bg-background text-foreground antialiased;
|
||||
font-feature-settings: 'rlig' 1, 'calt' 1;
|
||||
}
|
||||
|
||||
/* Improved focus states */
|
||||
*:focus-visible {
|
||||
@apply outline-none ring-2 ring-ring ring-offset-2 ring-offset-background;
|
||||
}
|
||||
|
||||
/* Custom scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
@apply bg-transparent;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
@apply bg-border rounded-full;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
@apply bg-muted-foreground/50;
|
||||
}
|
||||
|
||||
/* Smooth animations */
|
||||
.animate-in {
|
||||
animation: animateIn 200ms ease;
|
||||
}
|
||||
|
||||
.animate-out {
|
||||
animation: animateOut 150ms ease;
|
||||
}
|
||||
|
||||
@keyframes animateIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes animateOut {
|
||||
from {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
}
|
||||
|
||||
/* Typography improvements */
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
@apply font-medium tracking-tight;
|
||||
}
|
||||
|
||||
/* Better code font */
|
||||
code, pre, .font-mono {
|
||||
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
||||
font-feature-settings: 'liga' 1, 'calt' 1;
|
||||
}
|
||||
|
||||
/* Minimal button hover states */
|
||||
.btn-minimal-hover {
|
||||
@apply transition-colors duration-150 hover:bg-muted/50;
|
||||
}
|
||||
|
||||
/* Glass effect for cards */
|
||||
.glass-card {
|
||||
@apply backdrop-blur-xl bg-background/80 border border-border/50;
|
||||
}
|
||||
|
||||
/* Subtle shadows */
|
||||
.shadow-minimal {
|
||||
box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||||
}
|
||||
|
||||
.shadow-minimal-lg {
|
||||
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
}
|
||||
|
||||
/* Dark mode improvements */
|
||||
.dark .shadow-minimal {
|
||||
box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.3);
|
||||
}
|
||||
|
||||
.dark .shadow-minimal-lg {
|
||||
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.4), 0 2px 4px -2px rgb(0 0 0 / 0.2);
|
||||
}
|
||||
|
||||
/* Slide-up animation for new content */
|
||||
.animate-slide-up {
|
||||
animation: slide-up 0.3s ease-out;
|
||||
}
|
||||
|
||||
@keyframes slide-up {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(8px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Fade-in for smooth reveals */
|
||||
.animate-fade-in {
|
||||
animation: fade-in 0.2s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fade-in {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
/* GPU-accelerated utility */
|
||||
.gpu {
|
||||
transform: translateZ(0);
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
/* Thin scrollbar utility */
|
||||
.scrollbar-thin::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar-track {
|
||||
@apply bg-transparent;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar-thumb {
|
||||
@apply bg-muted-foreground/20 rounded-full;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar-thumb:hover {
|
||||
@apply bg-muted-foreground/30;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user