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:
admin
2026-01-28 00:27:56 +04:00
Unverified
parent 3b128ba3bd
commit b52318eeae
1724 changed files with 351216 additions and 0 deletions

View File

@@ -0,0 +1,47 @@
// Non-React capture helpers for use in event handlers.
// These functions work outside of React context (e.g., in handlers.ts).
import posthog from 'posthog-js';
import type { LLMTokensConsumedEvent } from '@dexto/analytics';
/**
* Check if analytics is enabled by looking for the injected config.
*/
function isAnalyticsEnabled(): boolean {
if (typeof window === 'undefined') return false;
return !!(window as any).__DEXTO_ANALYTICS__;
}
/**
* Get base context properties included with every event.
*/
function getBaseContext() {
if (typeof window === 'undefined') {
return { app: 'dexto-webui' };
}
const config = (window as any).__DEXTO_ANALYTICS__;
return {
app: 'dexto-webui',
app_version: config?.appVersion ?? 'unknown',
};
}
/**
* Capture LLM token usage event.
* Called from event handlers (non-React context).
*
* @param params - Token usage data (source is automatically set to 'webui')
*/
export function captureTokenUsage(params: Omit<LLMTokensConsumedEvent, 'source'>): void {
if (!isAnalyticsEnabled()) return;
try {
posthog.capture('dexto_llm_tokens_consumed', {
...getBaseContext(),
source: 'webui',
...params,
});
} catch {
// Analytics should never break the app
}
}

View File

@@ -0,0 +1,38 @@
// WebUI analytics event definitions for PostHog.
// These events track user activation, retention, and feature usage.
//
// All events use unified names (dexto_*) with a `source` property to
// distinguish CLI vs WebUI. This enables simpler PostHog dashboards.
import type { SharedAnalyticsEventMap, FileAttachedEvent } from '@dexto/analytics';
/**
* Base context automatically included with every WebUI event.
* Populated by the AnalyticsProvider.
*/
export interface BaseEventContext {
app?: 'dexto-webui';
app_version?: string;
browser?: string;
browser_version?: string;
os?: string;
screen_width?: number;
screen_height?: number;
session_id?: string;
}
/**
* WebUI analytics event map extending shared events with WebUI-specific events.
*
* IMPORTANT: If an event is also tracked by CLI, move it to SharedAnalyticsEventMap
* in @dexto/analytics to avoid duplication.
*/
export interface WebUIAnalyticsEventMap extends SharedAnalyticsEventMap {
// WebUI-specific events (not supported by CLI)
dexto_file_attached: FileAttachedEvent;
}
export type WebUIAnalyticsEventName = keyof WebUIAnalyticsEventMap;
export type WebUIAnalyticsEventPayload<Name extends WebUIAnalyticsEventName> =
WebUIAnalyticsEventMap[Name];

View File

@@ -0,0 +1,210 @@
// Convenience hook for tracking analytics events in WebUI components.
//
// Uses shared event names (dexto_*) with source: 'webui' for unified dashboards.
import { useCallback, useRef } from 'react';
import { useAnalyticsContext } from './provider.js';
import type {
MessageSentEvent,
SessionCreatedEvent,
SessionResetEvent,
ToolCalledEvent,
ToolResultEvent,
LLMSwitchedEvent,
SessionSwitchedEvent,
AgentSwitchedEvent,
FileAttachedEvent,
ImageAttachedEvent,
MCPServerConnectedEvent,
} from '@dexto/analytics';
export interface UseAnalyticsReturn {
capture: ReturnType<typeof useAnalyticsContext>['capture'];
enabled: boolean;
isReady: boolean;
// Convenience tracking methods (source: 'webui' added automatically)
trackMessageSent: (params: Omit<MessageSentEvent, 'messageCount' | 'source'>) => void;
trackSessionCreated: (params: Omit<SessionCreatedEvent, 'source'>) => void;
trackSessionSwitched: (params: Omit<SessionSwitchedEvent, 'source'>) => void;
trackSessionReset: (params: Omit<SessionResetEvent, 'source'>) => void;
trackAgentSwitched: (params: Omit<AgentSwitchedEvent, 'source'>) => void;
trackToolCalled: (params: Omit<ToolCalledEvent, 'source'>) => void;
trackToolResult: (params: Omit<ToolResultEvent, 'source'>) => void;
trackLLMSwitched: (params: Omit<LLMSwitchedEvent, 'source'>) => void;
trackFileAttached: (params: Omit<FileAttachedEvent, 'source'>) => void;
trackImageAttached: (params: Omit<ImageAttachedEvent, 'source'>) => void;
trackMCPServerConnected: (params: Omit<MCPServerConnectedEvent, 'source'>) => void;
}
/**
* Hook for tracking analytics events with convenience methods.
* Automatically handles message counting per session.
*/
export function useAnalytics(): UseAnalyticsReturn {
const { capture, enabled, isReady } = useAnalyticsContext();
// Track message count per session
const messageCountRef = useRef<Record<string, number>>({});
const trackMessageSent = useCallback(
(params: Omit<MessageSentEvent, 'messageCount' | 'source'>) => {
if (!enabled) return;
try {
// Increment message count for this session
const sessionId = params.sessionId;
messageCountRef.current[sessionId] = (messageCountRef.current[sessionId] || 0) + 1;
capture('dexto_message_sent', {
source: 'webui',
...params,
messageCount: messageCountRef.current[sessionId],
});
} catch (error) {
// Analytics should never break the app - fail silently
console.warn('Analytics tracking failed:', error);
}
},
[capture, enabled]
);
const trackSessionCreated = useCallback(
(params: Omit<SessionCreatedEvent, 'source'>) => {
if (!enabled) return;
try {
capture('dexto_session_created', { source: 'webui', ...params });
} catch (error) {
console.warn('Analytics tracking failed:', error);
}
},
[capture, enabled]
);
const trackSessionSwitched = useCallback(
(params: Omit<SessionSwitchedEvent, 'source'>) => {
if (!enabled) return;
try {
capture('dexto_session_switched', { source: 'webui', ...params });
} catch (error) {
console.warn('Analytics tracking failed:', error);
}
},
[capture, enabled]
);
const trackSessionReset = useCallback(
(params: Omit<SessionResetEvent, 'source'>) => {
if (!enabled) return;
try {
// Reset message count for this session
messageCountRef.current[params.sessionId] = 0;
capture('dexto_session_reset', { source: 'webui', ...params });
} catch (error) {
console.warn('Analytics tracking failed:', error);
}
},
[capture, enabled]
);
const trackAgentSwitched = useCallback(
(params: Omit<AgentSwitchedEvent, 'source'>) => {
if (!enabled) return;
try {
capture('dexto_agent_switched', { source: 'webui', ...params });
} catch (error) {
console.warn('Analytics tracking failed:', error);
}
},
[capture, enabled]
);
const trackToolCalled = useCallback(
(params: Omit<ToolCalledEvent, 'source'>) => {
if (!enabled) return;
try {
capture('dexto_tool_called', { source: 'webui', ...params });
} catch (error) {
console.warn('Analytics tracking failed:', error);
}
},
[capture, enabled]
);
const trackToolResult = useCallback(
(params: Omit<ToolResultEvent, 'source'>) => {
if (!enabled) return;
try {
capture('dexto_tool_result', { source: 'webui', ...params });
} catch (error) {
console.warn('Analytics tracking failed:', error);
}
},
[capture, enabled]
);
const trackLLMSwitched = useCallback(
(params: Omit<LLMSwitchedEvent, 'source'>) => {
if (!enabled) return;
try {
capture('dexto_llm_switched', { source: 'webui', ...params });
} catch (error) {
console.warn('Analytics tracking failed:', error);
}
},
[capture, enabled]
);
const trackFileAttached = useCallback(
(params: Omit<FileAttachedEvent, 'source'>) => {
if (!enabled) return;
try {
capture('dexto_file_attached', { source: 'webui', ...params });
} catch (error) {
console.warn('Analytics tracking failed:', error);
}
},
[capture, enabled]
);
const trackImageAttached = useCallback(
(params: Omit<ImageAttachedEvent, 'source'>) => {
if (!enabled) return;
try {
capture('dexto_image_attached', { source: 'webui', ...params });
} catch (error) {
console.warn('Analytics tracking failed:', error);
}
},
[capture, enabled]
);
const trackMCPServerConnected = useCallback(
(params: Omit<MCPServerConnectedEvent, 'source'>) => {
if (!enabled) return;
try {
capture('dexto_mcp_server_connected', { source: 'webui', ...params });
} catch (error) {
console.warn('Analytics tracking failed:', error);
}
},
[capture, enabled]
);
return {
capture,
enabled,
isReady,
trackMessageSent,
trackSessionCreated,
trackSessionSwitched,
trackSessionReset,
trackAgentSwitched,
trackToolCalled,
trackToolResult,
trackLLMSwitched,
trackFileAttached,
trackImageAttached,
trackMCPServerConnected,
};
}

View File

@@ -0,0 +1,15 @@
// Main exports for WebUI analytics library.
//
// All event types should be imported directly from @dexto/analytics.
export { AnalyticsProvider, useAnalyticsContext } from './provider.js';
export { useAnalytics } from './hook.js';
export { captureTokenUsage } from './capture.js';
// WebUI-specific type maps (for type-safe capture)
export type {
WebUIAnalyticsEventName,
WebUIAnalyticsEventPayload,
WebUIAnalyticsEventMap,
BaseEventContext,
} from './events.js';

View File

@@ -0,0 +1,145 @@
// packages/webui/lib/analytics/provider.tsx
// React Context Provider for WebUI analytics using PostHog JS SDK.
import React, { createContext, useContext, useEffect, useState, type ReactNode } from 'react';
import posthog from 'posthog-js';
import type {
WebUIAnalyticsEventName,
WebUIAnalyticsEventPayload,
BaseEventContext,
} from './events.js';
interface AnalyticsConfig {
distinctId: string;
posthogKey: string;
posthogHost: string;
appVersion: string;
}
interface AnalyticsContextType {
capture: <Name extends WebUIAnalyticsEventName>(
event: Name,
properties?: WebUIAnalyticsEventPayload<Name>
) => void;
enabled: boolean;
isReady: boolean;
}
const AnalyticsContext = createContext<AnalyticsContextType | undefined>(undefined);
interface AnalyticsProviderProps {
children: ReactNode;
}
/**
* Get analytics config injected during app initialization.
* Returns null if analytics disabled or config not available.
*/
function getAnalyticsConfig(): AnalyticsConfig | null {
if (typeof window === 'undefined') return null;
return (window as any).__DEXTO_ANALYTICS__ ?? null;
}
/**
* Get base context properties included with every event.
*/
function getBaseContext(): BaseEventContext {
if (typeof window === 'undefined') {
return {
app: 'dexto-webui',
};
}
return {
app: 'dexto-webui',
app_version: getAnalyticsConfig()?.appVersion ?? 'unknown',
browser: navigator.userAgent.split(' ').pop()?.split('/')[0],
browser_version: navigator.userAgent.split(' ').pop()?.split('/')[1],
os: navigator.platform,
screen_width: window.screen.width,
screen_height: window.screen.height,
// session_id will be managed by PostHog automatically
};
}
export function AnalyticsProvider({ children }: AnalyticsProviderProps) {
const [enabled, setEnabled] = useState(false);
const [isReady, setIsReady] = useState(false);
useEffect(() => {
const config = getAnalyticsConfig();
if (!config) {
// Analytics disabled or config not available
setEnabled(false);
setIsReady(true);
return;
}
try {
// Initialize PostHog
posthog.init(config.posthogKey, {
api_host: config.posthogHost,
person_profiles: 'identified_only', // Only create profiles for identified users
loaded: (posthogInstance) => {
// Use the distinct ID from CLI (unified tracking)
posthogInstance.identify(config.distinctId);
setEnabled(true);
setIsReady(true);
},
autocapture: false, // Disable automatic event capture
capture_pageview: false, // We'll manually track page views for better control
disable_session_recording: true, // Disable session replay (privacy)
disable_surveys: true, // Disable surveys
opt_out_capturing_by_default: false,
});
} catch (error) {
console.error('Failed to initialize analytics:', error);
setEnabled(false);
setIsReady(true);
}
// Cleanup on unmount - always reset to clear identity
return () => {
try {
posthog.reset(); // Always clear identity on unmount
} catch {
// Ignore errors if PostHog wasn't initialized
}
};
}, []); // Run once on mount
const capture = <Name extends WebUIAnalyticsEventName>(
event: Name,
properties?: WebUIAnalyticsEventPayload<Name>
) => {
if (!enabled || !isReady) return;
try {
posthog.capture(event, {
...getBaseContext(),
...properties,
});
} catch (error) {
console.error('Failed to capture analytics event:', error);
}
};
return (
<AnalyticsContext.Provider value={{ capture, enabled, isReady }}>
{children}
</AnalyticsContext.Provider>
);
}
/**
* Hook to access analytics from any component.
* Must be used within an AnalyticsProvider.
*/
export function useAnalyticsContext(): AnalyticsContextType {
const context = useContext(AnalyticsContext);
if (!context) {
throw new Error('useAnalyticsContext must be used within an AnalyticsProvider');
}
return context;
}