agents.js•19.2 kB
// Agent Directory Browser JavaScript
let allAgents = [];
let filteredAgents = [];
let currentCategory = 'all';
// Load agents data
async function loadAgents() {
try {
const response = await fetch('data/agents.json');
const data = await response.json();
allAgents = data.agents;
filteredAgents = [...allAgents];
// Initialize UI
renderCategoryTabs(data.categories);
updateAgentCount();
renderAgents();
} catch (error) {
console.error('Failed to load agents:', error);
showError('Failed to load agents. Please try again later.');
}
}
// Render category tabs
function renderCategoryTabs(categories) {
const tabsContainer = document.getElementById('categoryTabs');
if (!tabsContainer) return;
// Add "All Agents" tab
const allTab = document.createElement('button');
allTab.className = 'category-tab active';
allTab.dataset.category = 'all';
allTab.textContent = 'All Agents';
allTab.addEventListener('click', () => filterByCategory('all'));
tabsContainer.appendChild(allTab);
// Add category tabs
categories.forEach(category => {
const tab = document.createElement('button');
tab.className = 'category-tab';
tab.dataset.category = category;
tab.innerHTML = `${getCategoryIcon(category)} ${formatCategoryName(category)}`;
tab.addEventListener('click', () => filterByCategory(category));
tabsContainer.appendChild(tab);
});
}
// Filter by category
function filterByCategory(category) {
currentCategory = category;
// Update active tab
const tabs = document.querySelectorAll('.category-tab');
tabs.forEach(tab => {
const isActive = tab.dataset.category === category;
tab.classList.toggle('active', isActive);
// Scroll active tab into view
if (isActive) {
tab.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' });
}
});
applyFilters();
}
// Apply all filters
function applyFilters() {
const searchInput = document.getElementById('agentSearch');
const searchTerm = searchInput ? searchInput.value.toLowerCase() : '';
filteredAgents = allAgents.filter(agent => {
// Category filter
const categoryMatch = currentCategory === 'all' || agent.category === currentCategory;
// Search filter
const searchMatch = !searchTerm ||
agent.name.toLowerCase().includes(searchTerm) ||
agent.description.toLowerCase().includes(searchTerm) ||
agent.capabilities.primary.some(cap => cap.toLowerCase().includes(searchTerm)) ||
agent.capabilities.secondary.some(cap => cap.toLowerCase().includes(searchTerm)) ||
(agent.tags && agent.tags.some(tag => tag.toLowerCase().includes(searchTerm)));
return categoryMatch && searchMatch;
});
updateAgentCount();
renderAgents();
}
// Update agent count display
function updateAgentCount() {
const countText = document.getElementById('agentCountText');
if (countText) {
const total = allAgents.length;
const filtered = filteredAgents.length;
if (filtered === total) {
countText.textContent = `Showing all ${total} agents`;
} else {
countText.textContent = `Showing ${filtered} of ${total} agents`;
}
}
}
// Render agents grid
function renderAgents() {
const grid = document.getElementById('agentGrid');
const noResults = document.getElementById('noResults');
if (!grid) return;
// Clear grid
grid.innerHTML = '';
if (filteredAgents.length === 0) {
grid.style.display = 'none';
if (noResults) noResults.style.display = 'block';
return;
}
grid.style.display = 'grid';
if (noResults) noResults.style.display = 'none';
filteredAgents.forEach(agent => {
const card = createAgentCard(agent);
grid.appendChild(card);
});
}
// Create agent card
function createAgentCard(agent) {
const card = document.createElement('div');
card.className = 'agent-card';
card.addEventListener('click', () => showAgentDetails(agent));
const primaryCapabilities = agent.capabilities.primary.slice(0, 3);
card.innerHTML = `
<div class="agent-card-header">
<div class="agent-icon">${getCategoryIcon(agent.category)}</div>
<div class="agent-card-title">
<h3>${agent.name}</h3>
<span class="agent-category">${formatCategoryName(agent.category)}</span>
</div>
</div>
<p class="agent-description">${agent.description}</p>
<div class="agent-capabilities">
${primaryCapabilities.map(cap => `
<span class="capability-tag">${cap}</span>
`).join('')}
${agent.capabilities.primary.length > 3 ? `<span class="capability-tag">+${agent.capabilities.primary.length - 3} more</span>` : ''}
</div>
<div class="agent-version">v${agent.version}</div>
`;
return card;
}
// Show agent details modal
function showAgentDetails(agent) {
const modal = document.getElementById('agentModal');
const modalContent = modal.querySelector('.modal-content');
if (!modal || !modalContent) return;
// Prepare coordination info
const coordination = agent.coordination || {};
const worksWellWith = coordination.worksWellWith || [];
modalContent.innerHTML = `
<div class="modal-header">
<div class="modal-title">
<div class="agent-icon">${getCategoryIcon(agent.category)}</div>
<div class="modal-title-content">
<h2>${agent.name}</h2>
<div class="modal-meta">
<div class="modal-meta-item">
<strong>Category:</strong> ${formatCategoryName(agent.category)}
</div>
<div class="modal-meta-item">
<strong>Version:</strong> ${agent.version}
</div>
${agent.quality && agent.quality.enterpriseGrade ? `
<div class="modal-meta-item" style="color: var(--success-color);">
<strong>✓ Enterprise Grade</strong>
</div>
` : ''}
</div>
</div>
</div>
<button class="modal-close" aria-label="Close modal">×</button>
</div>
<div class="modal-body">
<div class="modal-section">
<p><strong>${agent.description}</strong></p>
</div>
<div class="modal-section">
<h3>Primary Capabilities</h3>
<div class="capabilities-grid">
${agent.capabilities.primary.map(cap => `
<div class="capability-item">${cap}</div>
`).join('')}
</div>
</div>
${agent.capabilities.secondary && agent.capabilities.secondary.length > 0 ? `
<div class="modal-section">
<h3>Secondary Capabilities</h3>
<div class="capabilities-grid">
${agent.capabilities.secondary.map(cap => `
<div class="capability-item">${cap}</div>
`).join('')}
</div>
</div>
` : ''}
${agent.capabilities.frameworks && agent.capabilities.frameworks.length > 0 ? `
<div class="modal-section">
<h3>Frameworks & Methodologies</h3>
<div class="capabilities-grid">
${agent.capabilities.frameworks.map(fw => `
<div class="capability-item">${fw}</div>
`).join('')}
</div>
</div>
` : ''}
${worksWellWith.length > 0 ? `
<div class="modal-section">
<h3>Agent Coordination</h3>
<p>This agent coordinates effectively with the following agents for complex multi-agent workflows:</p>
<div class="capabilities-grid">
${worksWellWith.map(agentName => `
<div class="capability-item">${agentName}</div>
`).join('')}
</div>
</div>
` : ''}
${agent.tags && agent.tags.length > 0 ? `
<div class="modal-section">
<h3>Tags</h3>
<div class="capabilities-grid">
${agent.tags.map(tag => `
<div class="capability-item">${tag}</div>
`).join('')}
</div>
</div>
` : ''}
<div class="modal-section">
<h3>Usage Example</h3>
<p>Access this agent through Claude Desktop using the MCP protocol:</p>
<div class="code-block-wrapper">
<pre><code>// Direct request in Claude Desktop:
"Activate ${agent.name} to help me with [your task]"
// Or use the MCP tool interface:
activate_agent({
"agent_name": "${agent.name}",
"request": "[your specific task or question]"
})</code></pre>
<button class="copy-code-btn" data-code='// Direct request in Claude Desktop:
"Activate ${agent.name} to help me with [your task]"
// Or use the MCP tool interface:
activate_agent({
"agent_name": "${agent.name}",
"request": "[your specific task or question]"
})'>
<svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"/>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
</svg>
Copy
</button>
</div>
<p style="margin-top: 1rem;"><strong>Note:</strong> Agent activations typically take 500-2000ms. Coordination modes are available for multi-agent workflows.</p>
</div>
</div>
`;
// Setup modal close handlers
const closeBtn = modalContent.querySelector('.modal-close');
if (closeBtn) {
closeBtn.addEventListener('click', closeModal);
}
// Setup copy button handler
const copyBtn = modalContent.querySelector('.copy-code-btn');
if (copyBtn) {
copyBtn.addEventListener('click', async function() {
const code = this.getAttribute('data-code');
try {
await navigator.clipboard.writeText(code);
// Show success state
this.classList.add('copied');
const originalHTML = this.innerHTML;
this.innerHTML = '<svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg> Copied!';
// Reset after 2 seconds
setTimeout(() => {
this.classList.remove('copied');
this.innerHTML = originalHTML;
}, 2000);
} catch (err) {
console.error('Failed to copy:', err);
}
});
}
modal.classList.add('active');
document.body.style.overflow = 'hidden';
}
// Close modal
function closeModal() {
const modal = document.getElementById('agentModal');
if (modal) {
modal.classList.remove('active');
document.body.style.overflow = '';
}
}
// Show error message
function showError(message) {
const grid = document.getElementById('agentGrid');
if (grid) {
grid.innerHTML = `
<div style="grid-column: 1 / -1; text-align: center; padding: 4rem; color: var(--danger-color);">
<svg width="64" height="64" fill="none" stroke="currentColor" viewBox="0 0 24 24" style="margin: 0 auto 1rem;">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
<h3 style="font-size: 1.5rem; margin-bottom: 0.5rem;">Error Loading Agents</h3>
<p>${message}</p>
</div>
`;
}
}
// Initialize
document.addEventListener('DOMContentLoaded', () => {
// Load agents
loadAgents();
// Setup search
const searchInput = document.getElementById('agentSearch');
const clearSearchBtn = document.getElementById('clearSearch');
if (searchInput) {
searchInput.addEventListener('input', (e) => {
applyFilters();
// Show/hide clear button
if (clearSearchBtn) {
clearSearchBtn.style.display = e.target.value ? 'flex' : 'none';
}
});
}
// Setup clear search button
if (clearSearchBtn) {
clearSearchBtn.addEventListener('click', () => {
if (searchInput) {
searchInput.value = '';
clearSearchBtn.style.display = 'none';
applyFilters();
searchInput.focus();
}
});
}
// Setup modal close
const modal = document.getElementById('agentModal');
if (modal) {
const overlay = modal.querySelector('.modal-overlay');
const closeBtn = modal.querySelector('.modal-close');
if (overlay) overlay.addEventListener('click', closeModal);
if (closeBtn) closeBtn.addEventListener('click', closeModal);
// Close on Escape key
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && modal.classList.contains('active')) {
closeModal();
}
});
}
// Check for category in URL
const urlParams = new URLSearchParams(window.location.search);
const categoryParam = urlParams.get('category');
if (categoryParam) {
setTimeout(() => filterByCategory(categoryParam), 100);
}
});
// Helper functions (from main.js)
function formatCategoryName(category) {
return category
.split('-')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
}
function getCategoryIcon(category) {
const icons = {
'software-engineering': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>',
'security-operations': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="5" y="11" width="14" height="10" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>',
'elite-research': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>',
'data-engineering': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/></svg>',
'cloud-infrastructure': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 10h-1.26A8 8 0 1 0 9 20h9a5 5 0 0 0 0-10z"/></svg>',
'c-suite-executives': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>',
'ai-machine-learning': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="7" width="20" height="14" rx="2"/><path d="M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16"/></svg>',
'board-of-directors': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 9h18v10a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V9Z"/><path d="M3 9V7a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v2"/></svg>',
'business-strategy': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="7" width="20" height="14" rx="2"/><path d="M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16"/></svg>',
'consultancy-agents': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2"/></svg>',
'content-documentation': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>',
'customer-engagement': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>',
'devops-automation': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M12 1v6m0 6v6M5.64 5.64l4.24 4.24m4.24 4.24l4.24 4.24M1 12h6m6 0h6M5.64 18.36l4.24-4.24m4.24-4.24l4.24-4.24"/></svg>',
'distributed-systems': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>',
'investor-panel': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="1" x2="12" y2="23"/><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/></svg>',
'performance-reliability': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>',
'programming-languages': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/></svg>',
'software-architecture': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>'
};
return icons[category] || '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/></svg>';
}