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,181 @@
/**
* MCP Server Registry Service
*
* Provides access to the built-in registry of MCP servers.
* This is a shared service used by both CLI and WebUI.
*/
import type { ServerRegistryEntry, ServerRegistryFilter } from './types.js';
import builtinRegistryData from './server-registry-data.json' with { type: 'json' };
/**
* Normalize an ID for comparison
*/
function normalizeId(s: string): string {
return s
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '');
}
/**
* MCP Server Registry Service
*
* Manages a registry of available MCP servers that can be quickly added to agents.
* The built-in registry data is loaded from an external JSON file.
*/
export class ServerRegistryService {
private static instance: ServerRegistryService;
private registryEntries: ServerRegistryEntry[] = [];
private isInitialized = false;
private constructor() {
// Private constructor for singleton
}
static getInstance(): ServerRegistryService {
if (!ServerRegistryService.instance) {
ServerRegistryService.instance = new ServerRegistryService();
}
return ServerRegistryService.instance;
}
/**
* Initialize the registry with default entries
*/
async initialize(): Promise<void> {
if (this.isInitialized) return;
// Load built-in registry entries from JSON file
this.registryEntries = builtinRegistryData as ServerRegistryEntry[];
this.isInitialized = true;
}
/**
* Get all registry entries with optional filtering
*/
async getEntries(filter?: ServerRegistryFilter): Promise<ServerRegistryEntry[]> {
await this.initialize();
let filtered = [...this.registryEntries];
if (filter?.category) {
filtered = filtered.filter((entry) => entry.category === filter.category);
}
if (filter?.tags?.length) {
filtered = filtered.filter((entry) =>
filter.tags!.some((tag) => entry.tags.includes(tag))
);
}
if (filter?.search) {
const searchLower = filter.search.toLowerCase();
filtered = filtered.filter(
(entry) =>
entry.name.toLowerCase().includes(searchLower) ||
entry.description.toLowerCase().includes(searchLower) ||
entry.tags.some((tag) => tag.toLowerCase().includes(searchLower))
);
}
if (filter?.installed !== undefined) {
filtered = filtered.filter((entry) => entry.isInstalled === filter.installed);
}
if (filter?.official !== undefined) {
filtered = filtered.filter((entry) => entry.isOfficial === filter.official);
}
return filtered.sort((a, b) => {
// Sort by: installed first, then official, then name
if (a.isInstalled !== b.isInstalled) {
return a.isInstalled ? -1 : 1;
}
if (a.isOfficial !== b.isOfficial) {
return a.isOfficial ? -1 : 1;
}
return a.name.localeCompare(b.name);
});
}
/**
* Update an existing registry entry's state
*/
async updateEntry(id: string, updates: Partial<ServerRegistryEntry>): Promise<boolean> {
await this.initialize();
const entry = this.registryEntries.find((e) => e.id === id);
if (!entry) return false;
// Use Object.assign to merge partial updates
Object.assign(entry, updates);
return true;
}
/**
* Mark a server as installed/uninstalled
*/
async setInstalled(id: string, installed: boolean): Promise<boolean> {
return this.updateEntry(id, { isInstalled: installed });
}
/**
* Sync registry installed status with a list of connected server IDs
*/
async syncInstalledStatus(connectedServerIds: string[]): Promise<void> {
await this.initialize();
const normalizedIds = new Set(connectedServerIds.map(normalizeId));
for (const entry of this.registryEntries) {
const aliases = [entry.id, entry.name, ...(entry.matchIds || [])]
.filter(Boolean)
.map((x) => normalizeId(String(x)));
const isInstalled = aliases.some((alias) => normalizedIds.has(alias));
if (entry.isInstalled !== isInstalled) {
entry.isInstalled = isInstalled;
}
}
}
/**
* Get a single server configuration by ID
*/
async getServerConfig(id: string): Promise<ServerRegistryEntry | null> {
await this.initialize();
return this.registryEntries.find((entry) => entry.id === id) || null;
}
/**
* Get all available categories
*/
async getCategories(): Promise<string[]> {
await this.initialize();
const categories = new Set(this.registryEntries.map((entry) => entry.category));
return Array.from(categories).sort();
}
/**
* Get all available tags
*/
async getTags(): Promise<string[]> {
await this.initialize();
const tags = new Set(this.registryEntries.flatMap((entry) => entry.tags));
return Array.from(tags).sort();
}
}
/**
* Get the singleton registry instance
*/
export function getServerRegistry(): ServerRegistryService {
return ServerRegistryService.getInstance();
}
/**
* Export singleton instance for convenience
*/
export const serverRegistry = ServerRegistryService.getInstance();