- {agents.map((agent) => (
-
- ))}
+
+
+
# agents
+
+ Browse specialized AI workers for code analysis, travel planner feeds, data cleaning, and email summaries.
+
+
+ {/* Filter and Search Panel */}
+
+
+ {/* Search bar */}
+
+
+
+
+ setSearchQuery(e.target.value)}
+ className="form-input"
+ style={{ width: '100%', paddingLeft: '2.5rem' }}
+ />
+
+
+ {/* Sort dropdown */}
+
+ Sort By
+ setSortBy(e.target.value)}
+ className="form-input"
+ style={{ background: 'var(--bg-secondary)', cursor: 'pointer' }}
+ >
+ ~ trending
+ ~ top-rated
+ ~ downloads
+ ~ alphabetical
+
+
+
+
+ {/* Category Pills */}
+
+ {categories.map((cat) => {
+ const isActive = categoryParam.toLowerCase() === cat.toLowerCase()
+ return (
+ handleCategorySelect(cat)}
+ className="btn"
+ style={{
+ padding: '0.4rem 0.9rem',
+ fontSize: '0.75rem',
+ borderRadius: '0px',
+ background: isActive ? 'var(--color-primary)' : 'rgba(255, 255, 255, 0.03)',
+ color: isActive ? '#21252b' : 'var(--text-secondary)',
+ borderColor: isActive ? 'var(--color-primary)' : 'var(--glass-border)',
+ }}
+ >
+ {isActive ? `* ${cat}` : cat}
+
+ )
+ })}
+
+
+
+ {loading ? (
+
+ ) : error ? (
+
+
+ API Error: Could not retrieve agent manifests from the server ({error}). Running in offline sandbox simulation mode.
+
+ ) : filteredAgents.length === 0 ? (
+
+
🤖
+
No Agents Found
+
+ Try adjusting your search filters or select a different category.
+
+
{ setSearchQuery(''); handleCategorySelect('All') }}
+ className="btn btn-secondary"
+ style={{ marginTop: '1.25rem' }}
+ >
+ clear-filters <~>
+
+
+ ) : (
+
+
+ // Showing {filteredAgents.length} agents
+ {categoryParam !== 'All' && Filtered by Category: {categoryParam} }
+
+
+ {filteredAgents.map((agent) => (
+
+ ))}
+
+
+ )}
)
}
diff --git a/frontend/src/pages/HomePage.tsx b/frontend/src/pages/HomePage.tsx
index 1893c74..82f11df 100644
--- a/frontend/src/pages/HomePage.tsx
+++ b/frontend/src/pages/HomePage.tsx
@@ -1,22 +1,80 @@
import { Link } from 'react-router-dom'
+import { getCategoryIcon } from '../components/Icons'
export default function HomePage() {
+ const categories = [
+ { name: 'Productivity', desc: 'Email managers, calendar assistants, and agenda creators.' },
+ { name: 'Developer Tools', desc: 'Code reviewers, issue triagers, and commit summarizers.' },
+ { name: 'Career', desc: 'Resume auditors, job matchers, and portfolio planners.' },
+ { name: 'Data Analysis', desc: 'Spreadsheet formatters, outlier finders, and chart builders.' },
+ { name: 'Education', desc: 'Flashcard generators, study guide builders, and tutorial summaries.' },
+ { name: 'Travel', desc: 'Flight deal finders, hotel scrapers, and itinerary planners.' },
+ { name: 'Communication', desc: 'Meeting summary writers, notes formatters, and draft generators.' },
+ { name: 'Finance', desc: 'Expense audits, burn rate calculations, and budgets.' }
+ ]
+
+ const stats = [
+ { number: '10+', label: 'AI Agents' },
+ { number: '8', label: 'Power Tools' },
+ { number: '34,200+', label: 'Market Runs' },
+ { number: '100%', label: 'Simulation Ready' }
+ ]
+
return (
-
-
Welcome to AgentStore
-
- The App Store for Agents — discover, run, remix, and rate AI agents.
-
-
- This is a skeleton UI. Students will implement full features via Kanban tickets.
+
+
+ # agent-store
+
+ Discover, simulate, fork, and review specialized AI agents that execute complex multi-step workflows using integrated marketplace tools.
+
+
+
+ view-agents <~>
+
+
+ explore-tools ≥
+
+
+
+
+
+ {stats.map((stat, i) => (
+
+
+
{stat.number}
+
{stat.label}
+
+
+ ))}
-
-
- Browse Agents
-
-
- Browse Tools
-
+
+
+
+
# categories
+ View All Agents ~~>
+
+
+ {categories.map((cat, i) => (
+
+
+ {getCategoryIcon(cat.name, 24)}
+
+
{cat.name}
+
{cat.desc}
+
+ ))}
+
+
+
+
+
💡
+
+ Developer Sandbox Mode Active: This application runs using a localized client database fallback. You can search, filter, test terminal execution traces, and write reviews in-memory without starting the backend!
+
)
diff --git a/frontend/src/pages/ToolsPage.tsx b/frontend/src/pages/ToolsPage.tsx
index 0e6aca9..8f8d576 100644
--- a/frontend/src/pages/ToolsPage.tsx
+++ b/frontend/src/pages/ToolsPage.tsx
@@ -1,63 +1,286 @@
import { useEffect, useState } from 'react'
-import { fetchTools } from '../api/client'
-
-interface Tool {
- id: string
- name: string
- description: string
- category: string
- permission_level: string
-}
+import { Link } from 'react-router-dom'
+import { fetchTools, fetchAgents, type Tool, type AgentSummary } from '../api/client'
+import { ToolGridSkeleton } from '../components/SkeletonLoader'
+import { SearchIcon, ShieldAlertIcon, KeyIcon, ToolIcon } from '../components/Icons'
-/**
- * ToolsPage — browse marketplace tools.
- *
- * TODO: Add tool detail page
- * TODO: Show input/output schemas
- * TODO: Link tools to agents that use them
- */
export default function ToolsPage() {
const [tools, setTools] = useState
([])
+ const [agents, setAgents] = useState([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
+ const [searchQuery, setSearchQuery] = useState('')
+ const [selectedCategory, setSelectedCategory] = useState('All')
+
+ const [openSchemas, setOpenSchemas] = useState>({})
+
useEffect(() => {
- fetchTools()
- .then((t) => setTools(t as Tool[]))
+ setLoading(true)
+ Promise.all([fetchTools(), fetchAgents()])
+ .then(([toolsData, agentsData]) => {
+ setTools(toolsData)
+ setAgents(agentsData)
+ setError(null)
+ })
.catch((e) => setError(e.message))
- .finally(() => setLoading(false))
+ .finally(() => {
+ setTimeout(() => setLoading(false), 500)
+ })
}, [])
- if (loading) return Loading tools...
- if (error) {
- return (
-
-
Browse Tools
-
- Could not load tools ({error}). Start the backend first.
-
-
- )
+ useEffect(() => {
+ if (!loading && window.location.hash) {
+ const id = window.location.hash.replace('#', '')
+ const element = document.getElementById(id)
+ if (element) {
+ setTimeout(() => {
+ element.scrollIntoView({ behavior: 'smooth', block: 'center' })
+ element.style.borderColor = 'var(--color-primary)'
+ element.style.boxShadow = '0 0 20px rgba(199, 120, 221, 0.2)'
+ }, 100)
+ }
+ }
+ }, [loading])
+
+ const toggleSchema = (toolId: string, type: 'input' | 'output') => {
+ const current = openSchemas[toolId]
+ setOpenSchemas({
+ ...openSchemas,
+ [toolId]: current === type ? null : type
+ })
}
+ const categories = ['All', 'Productivity', 'Email', 'Developer', 'Data', 'Search', 'System']
+
+ const filteredTools = tools.filter((tool) => {
+ const matchesCategory = selectedCategory === 'All' || tool.category.toLowerCase() === selectedCategory.toLowerCase()
+ const matchesSearch =
+ tool.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ tool.description.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ tool.permissions_required.some((p) => p.toLowerCase().includes(searchQuery.toLowerCase()))
+ return matchesCategory && matchesSearch
+ })
+
return (
-
-
Browse Tools
-
- {tools.length} tools available for agents to use
-
-
- {tools.map((tool) => (
-
-
-
{tool.name}
- {tool.category}
-
-
{tool.description}
-
{tool.permission_level} access
+
+
+
# tools
+
+ Explore external services and APIs that AI agents use to perform active read and write workflows.
+
+
+
+ {/* Filter and Search Panel */}
+
+
+ {/* Search bar */}
+
+
+
+
+ setSearchQuery(e.target.value)}
+ className="form-input"
+ style={{ width: '100%', paddingLeft: '2.5rem' }}
+ />
- ))}
+
+
+ {/* Category Pills */}
+
+ {categories.map((cat) => {
+ const isActive = selectedCategory.toLowerCase() === cat.toLowerCase()
+ return (
+ setSelectedCategory(cat)}
+ className="btn"
+ style={{
+ padding: '0.4rem 0.9rem',
+ fontSize: '0.75rem',
+ borderRadius: '0px',
+ background: isActive ? 'var(--color-primary)' : 'rgba(255, 255, 255, 0.03)',
+ color: isActive ? '#21252b' : 'var(--text-secondary)',
+ borderColor: isActive ? 'var(--color-primary)' : 'var(--glass-border)',
+ }}
+ >
+ {cat}
+
+ )
+ })}
+
+
+ {loading ? (
+
+ ) : error ? (
+
+
+ API Error: Could not load registry tools ({error}). Running offline.
+
+ ) : filteredTools.length === 0 ? (
+
+
🛠️
+
No Tools Found
+
+ Try adjusting your query filters.
+
+
+ ) : (
+
+ {filteredTools.map((tool) => {
+ const dependentAgents = agents.filter((agent) =>
+ agent.tools_required.some((tId) => tId === tool.id)
+ )
+ const openSchemaType = openSchemas[tool.id]
+
+ return (
+
+ )
+ })}
+
+ )}
)
}