genoma-evolution 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.brv/.obsidian/app.json +1 -0
- package/.brv/.obsidian/appearance.json +1 -0
- package/.brv/.obsidian/core-plugins.json +33 -0
- package/.brv/.obsidian/graph.json +22 -0
- package/.brv/.obsidian/workspace.json +195 -0
- package/.brv/Sin ti/314/201tulo 1.canvas" +1 -0
- package/.brv/Sin ti/314/201tulo 2.canvas" +1 -0
- package/.brv/Sin ti/314/201tulo.canvas" +1 -0
- package/.brv/_queue_status.json +1 -0
- package/.brv/config.json +5 -0
- package/.brv/context-tree/_index.md +60 -0
- package/.brv/context-tree/_manifest.json +165 -0
- package/.brv/context-tree/backend/_index.md +24 -0
- package/.brv/context-tree/backend/backend/_index.md +40 -0
- package/.brv/context-tree/backend/backend/init.abstract.md +0 -0
- package/.brv/context-tree/backend/backend/init.md +27 -0
- package/.brv/context-tree/backend/backend/init.overview.md +29 -0
- package/.brv/context-tree/backend/backend/job_tracker.abstract.md +1 -0
- package/.brv/context-tree/backend/backend/job_tracker.md +273 -0
- package/.brv/context-tree/backend/backend/job_tracker.overview.md +31 -0
- package/.brv/context-tree/backend/backend/main.abstract.md +0 -0
- package/.brv/context-tree/backend/backend/main.md +1292 -0
- package/.brv/context-tree/backend/backend/main.overview.md +30 -0
- package/.brv/context-tree/backend/backend/requirements.abstract.md +1 -0
- package/.brv/context-tree/backend/backend/requirements.md +37 -0
- package/.brv/context-tree/backend/backend/requirements.overview.md +28 -0
- package/.brv/context-tree/docs/_index.md +37 -0
- package/.brv/context-tree/docs/api/_index.md +54 -0
- package/.brv/context-tree/docs/api/context.md +11 -0
- package/.brv/context-tree/docs/api/hermes_api_openapi_specification.abstract.md +0 -0
- package/.brv/context-tree/docs/api/hermes_api_openapi_specification.md +468 -0
- package/.brv/context-tree/docs/api/hermes_api_openapi_specification.overview.md +44 -0
- package/.brv/context-tree/frontend/_index.md +48 -0
- package/.brv/context-tree/frontend/hermes_dashboard/_index.md +31 -0
- package/.brv/context-tree/frontend/hermes_dashboard/architecture_overview.abstract.md +0 -0
- package/.brv/context-tree/frontend/hermes_dashboard/architecture_overview.md +41 -0
- package/.brv/context-tree/frontend/hermes_dashboard/architecture_overview.overview.md +34 -0
- package/.brv/context-tree/frontend/src/_index.md +53 -0
- package/.brv/context-tree/frontend/src/components/_index.md +52 -0
- package/.brv/context-tree/frontend/src/components/sidebar_navigation_component.abstract.md +0 -0
- package/.brv/context-tree/frontend/src/components/sidebar_navigation_component.md +161 -0
- package/.brv/context-tree/frontend/src/components/sidebar_navigation_component.overview.md +32 -0
- package/.brv/context-tree/frontend/src/context.md +10 -0
- package/.brv/context-tree/frontend/src/functioncallingpage.abstract.md +0 -0
- package/.brv/context-tree/frontend/src/functioncallingpage.md +34 -0
- package/.brv/context-tree/frontend/src/functioncallingpage.overview.md +26 -0
- package/.brv/context-tree/frontend/src/lib/_index.md +48 -0
- package/.brv/context-tree/frontend/src/lib/api_client_library.abstract.md +1 -0
- package/.brv/context-tree/frontend/src/lib/api_client_library.md +403 -0
- package/.brv/context-tree/frontend/src/lib/api_client_library.overview.md +69 -0
- package/.brv/context-tree/frontend/src/page.abstract.md +0 -0
- package/.brv/context-tree/frontend/src/page.md +103 -0
- package/.brv/context-tree/frontend/src/page.overview.md +7 -0
- package/.brv/context-tree/frontend/src/settingspage.abstract.md +0 -0
- package/.brv/context-tree/frontend/src/settingspage.md +124 -0
- package/.brv/context-tree/frontend/src/settingspage.overview.md +34 -0
- package/.brv/context-tree/frontend/src/sidebar.abstract.md +0 -0
- package/.brv/context-tree/frontend/src/sidebar.md +170 -0
- package/.brv/context-tree/frontend/src/sidebar.overview.md +25 -0
- package/.brv/context-tree/meta/_index.md +24 -0
- package/.brv/context-tree/meta/curation_context/_index.md +24 -0
- package/.brv/context-tree/meta/curation_context/empty_context.abstract.md +4 -0
- package/.brv/context-tree/meta/curation_context/empty_context.md +35 -0
- package/.brv/context-tree/meta/curation_context/empty_context.overview.md +20 -0
- package/.brv/dream-log/drm-1777341062653.json +33 -0
- package/.brv/dream-state.json +8 -0
- package/.brv/dream.lock +0 -0
- package/.brv/review-backups/docs/api/hermes_api_openapi_specification.md +468 -0
- package/.claude/settings.local.json +7 -0
- package/.claude/worktrees/phase-2-mcp/.brv/.obsidian/app.json +1 -0
- package/.claude/worktrees/phase-2-mcp/.brv/.obsidian/appearance.json +1 -0
- package/.claude/worktrees/phase-2-mcp/.brv/.obsidian/core-plugins.json +33 -0
- package/.claude/worktrees/phase-2-mcp/.brv/.obsidian/graph.json +22 -0
- package/.claude/worktrees/phase-2-mcp/.brv/.obsidian/workspace.json +195 -0
- package/.claude/worktrees/phase-2-mcp/.brv/Sin t/303/255tulo 1.canvas" +1 -0
- package/.claude/worktrees/phase-2-mcp/.brv/Sin t/303/255tulo 2.canvas" +1 -0
- package/.claude/worktrees/phase-2-mcp/.brv/Sin t/303/255tulo.canvas" +1 -0
- package/.claude/worktrees/phase-2-mcp/.brv/_queue_status.json +1 -0
- package/.claude/worktrees/phase-2-mcp/.brv/config.json +5 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/_index.md +60 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/_manifest.json +165 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/backend/_index.md +24 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/backend/backend/_index.md +40 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/backend/backend/init.abstract.md +0 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/backend/backend/init.md +27 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/backend/backend/init.overview.md +29 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/backend/backend/job_tracker.abstract.md +1 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/backend/backend/job_tracker.md +273 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/backend/backend/job_tracker.overview.md +31 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/backend/backend/main.abstract.md +0 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/backend/backend/main.md +1292 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/backend/backend/main.overview.md +30 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/backend/backend/requirements.abstract.md +1 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/backend/backend/requirements.md +37 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/backend/backend/requirements.overview.md +28 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/docs/_index.md +37 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/docs/api/_index.md +54 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/docs/api/context.md +11 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/docs/api/hermes_api_openapi_specification.abstract.md +0 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/docs/api/hermes_api_openapi_specification.md +468 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/docs/api/hermes_api_openapi_specification.overview.md +44 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/frontend/_index.md +48 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/frontend/hermes_dashboard/_index.md +31 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/frontend/hermes_dashboard/architecture_overview.abstract.md +0 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/frontend/hermes_dashboard/architecture_overview.md +41 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/frontend/hermes_dashboard/architecture_overview.overview.md +34 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/frontend/src/_index.md +53 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/frontend/src/components/_index.md +52 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/frontend/src/components/sidebar_navigation_component.abstract.md +0 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/frontend/src/components/sidebar_navigation_component.md +161 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/frontend/src/components/sidebar_navigation_component.overview.md +32 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/frontend/src/context.md +10 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/frontend/src/functioncallingpage.abstract.md +0 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/frontend/src/functioncallingpage.md +34 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/frontend/src/functioncallingpage.overview.md +26 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/frontend/src/lib/_index.md +48 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/frontend/src/lib/api_client_library.abstract.md +1 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/frontend/src/lib/api_client_library.md +403 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/frontend/src/lib/api_client_library.overview.md +69 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/frontend/src/page.abstract.md +0 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/frontend/src/page.md +103 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/frontend/src/page.overview.md +7 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/frontend/src/settingspage.abstract.md +0 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/frontend/src/settingspage.md +124 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/frontend/src/settingspage.overview.md +34 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/frontend/src/sidebar.abstract.md +0 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/frontend/src/sidebar.md +170 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/frontend/src/sidebar.overview.md +25 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/meta/_index.md +24 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/meta/curation_context/_index.md +24 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/meta/curation_context/empty_context.abstract.md +4 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/meta/curation_context/empty_context.md +35 -0
- package/.claude/worktrees/phase-2-mcp/.brv/context-tree/meta/curation_context/empty_context.overview.md +20 -0
- package/.claude/worktrees/phase-2-mcp/.brv/dream-log/drm-1777341062653.json +33 -0
- package/.claude/worktrees/phase-2-mcp/.brv/dream-state.json +8 -0
- package/.claude/worktrees/phase-2-mcp/.brv/dream.lock +0 -0
- package/.claude/worktrees/phase-2-mcp/.brv/review-backups/docs/api/hermes_api_openapi_specification.md +468 -0
- package/.claude/worktrees/phase-2-mcp/.claude/settings.local.json +13 -0
- package/.claude/worktrees/phase-2-mcp/.kilocode/package-lock.json +378 -0
- package/.claude/worktrees/phase-2-mcp/.kilocode/package.json +5 -0
- package/.claude/worktrees/phase-2-mcp/AGENTS.md +5 -0
- package/.claude/worktrees/phase-2-mcp/CLAUDE.md +29 -0
- package/.claude/worktrees/phase-2-mcp/QA_AUDIT_PLAN.md +156 -0
- package/.claude/worktrees/phase-2-mcp/README.md +316 -0
- package/.claude/worktrees/phase-2-mcp/agent-agnostic-evolution-dashboard.md +405 -0
- package/.claude/worktrees/phase-2-mcp/backend/__init__.py +0 -0
- package/.claude/worktrees/phase-2-mcp/backend/collectors/__init__.py +0 -0
- package/.claude/worktrees/phase-2-mcp/backend/collectors/claude_code_collector.py +277 -0
- package/.claude/worktrees/phase-2-mcp/backend/collectors/hermes_collector.py +68 -0
- package/.claude/worktrees/phase-2-mcp/backend/curator.py +512 -0
- package/.claude/worktrees/phase-2-mcp/backend/eval/__init__.py +19 -0
- package/.claude/worktrees/phase-2-mcp/backend/eval/engine.py +116 -0
- package/.claude/worktrees/phase-2-mcp/backend/eval/scorers.py +201 -0
- package/.claude/worktrees/phase-2-mcp/backend/generate_dataset.py +86 -0
- package/.claude/worktrees/phase-2-mcp/backend/job_tracker.py +232 -0
- package/.claude/worktrees/phase-2-mcp/backend/main.py +1746 -0
- package/.claude/worktrees/phase-2-mcp/backend/mcp_server.py +250 -0
- package/.claude/worktrees/phase-2-mcp/backend/promethean/__init__.py +24 -0
- package/.claude/worktrees/phase-2-mcp/backend/promethean/cycle_orchestrator.py +270 -0
- package/.claude/worktrees/phase-2-mcp/backend/promethean/delta_validator.py +191 -0
- package/.claude/worktrees/phase-2-mcp/backend/promethean/dspy_compiler.py +315 -0
- package/.claude/worktrees/phase-2-mcp/backend/promethean/gepa_strategist.py +213 -0
- package/.claude/worktrees/phase-2-mcp/backend/promethean/models.py +260 -0
- package/.claude/worktrees/phase-2-mcp/backend/promethean/skill_deployer.py +195 -0
- package/.claude/worktrees/phase-2-mcp/backend/promethean/trace_ingestion.py +142 -0
- package/.claude/worktrees/phase-2-mcp/backend/requirements.txt +6 -0
- package/.claude/worktrees/phase-2-mcp/backend/sdd_evolve.py +459 -0
- package/.claude/worktrees/phase-2-mcp/backend/skill_detector.py +227 -0
- package/.claude/worktrees/phase-2-mcp/backend/skill_registry.py +289 -0
- package/.claude/worktrees/phase-2-mcp/backend/storage/__init__.py +5 -0
- package/.claude/worktrees/phase-2-mcp/backend/storage/run_store.py +393 -0
- package/.claude/worktrees/phase-2-mcp/backend/storage/schema.sql +99 -0
- package/.claude/worktrees/phase-2-mcp/backend/validate_evolution.py +267 -0
- package/.claude/worktrees/phase-2-mcp/components.json +28 -0
- package/.claude/worktrees/phase-2-mcp/docs/api/hermes-api.openapi.yaml +438 -0
- package/.claude/worktrees/phase-2-mcp/docs/hero.svg +148 -0
- package/.claude/worktrees/phase-2-mcp/eslint.config.mjs +18 -0
- package/.claude/worktrees/phase-2-mcp/install.sh +245 -0
- package/.claude/worktrees/phase-2-mcp/next-env.d.ts +6 -0
- package/.claude/worktrees/phase-2-mcp/next.config.ts +32 -0
- package/.claude/worktrees/phase-2-mcp/package-lock.json +11936 -0
- package/.claude/worktrees/phase-2-mcp/package.json +41 -0
- package/.claude/worktrees/phase-2-mcp/pnpm-workspace.yaml +4 -0
- package/.claude/worktrees/phase-2-mcp/postcss.config.mjs +7 -0
- package/.claude/worktrees/phase-2-mcp/public/file.svg +1 -0
- package/.claude/worktrees/phase-2-mcp/public/fonts/SF-Pro-Display-Bold.otf +0 -0
- package/.claude/worktrees/phase-2-mcp/public/fonts/SF-Pro-Display-Heavy.otf +0 -0
- package/.claude/worktrees/phase-2-mcp/public/fonts/SF-Pro-Display-Medium.otf +0 -0
- package/.claude/worktrees/phase-2-mcp/public/fonts/SF-Pro-Display-Regular.otf +0 -0
- package/.claude/worktrees/phase-2-mcp/public/fonts/SF-Pro-Display-Semibold.otf +0 -0
- package/.claude/worktrees/phase-2-mcp/public/fonts/SF-Pro-Text-Bold.otf +0 -0
- package/.claude/worktrees/phase-2-mcp/public/fonts/SF-Pro-Text-Heavy.otf +0 -0
- package/.claude/worktrees/phase-2-mcp/public/fonts/SF-Pro-Text-Medium.otf +0 -0
- package/.claude/worktrees/phase-2-mcp/public/fonts/SF-Pro-Text-Regular.otf +0 -0
- package/.claude/worktrees/phase-2-mcp/public/fonts/SF-Pro-Text-Semibold.otf +0 -0
- package/.claude/worktrees/phase-2-mcp/public/globe.svg +1 -0
- package/.claude/worktrees/phase-2-mcp/public/next.svg +1 -0
- package/.claude/worktrees/phase-2-mcp/public/theme-preview.html +257 -0
- package/.claude/worktrees/phase-2-mcp/public/vercel.svg +1 -0
- package/.claude/worktrees/phase-2-mcp/public/window.svg +1 -0
- package/.claude/worktrees/phase-2-mcp/run.sh +26 -0
- package/.claude/worktrees/phase-2-mcp/skills-lock.json +10 -0
- package/.claude/worktrees/phase-2-mcp/specs/event-schema.md +223 -0
- package/.claude/worktrees/phase-2-mcp/specs/examples/run.jsonl +3 -0
- package/.claude/worktrees/phase-2-mcp/src/app/api/[...path]/route.ts +55 -0
- package/.claude/worktrees/phase-2-mcp/src/app/api/auth/token/route.ts +22 -0
- package/.claude/worktrees/phase-2-mcp/src/app/evolution/page.tsx +589 -0
- package/.claude/worktrees/phase-2-mcp/src/app/favicon.ico +0 -0
- package/.claude/worktrees/phase-2-mcp/src/app/globals.css +321 -0
- package/.claude/worktrees/phase-2-mcp/src/app/layout.tsx +63 -0
- package/.claude/worktrees/phase-2-mcp/src/app/page.tsx +70 -0
- package/.claude/worktrees/phase-2-mcp/src/app/skills/page.tsx +369 -0
- package/.claude/worktrees/phase-2-mcp/src/components/ApiConfigCard.tsx +199 -0
- package/.claude/worktrees/phase-2-mcp/src/components/ColorBends.css +1 -0
- package/.claude/worktrees/phase-2-mcp/src/components/ColorBends.d.ts +1 -0
- package/.claude/worktrees/phase-2-mcp/src/components/ColorBends.jsx +1 -0
- package/.claude/worktrees/phase-2-mcp/src/components/CoreLoopToggle.tsx +111 -0
- package/.claude/worktrees/phase-2-mcp/src/components/EnvironmentStatus.tsx +176 -0
- package/.claude/worktrees/phase-2-mcp/src/components/EvolutionBackground.tsx +1 -0
- package/.claude/worktrees/phase-2-mcp/src/components/ReactQueryProvider.tsx +24 -0
- package/.claude/worktrees/phase-2-mcp/src/components/Sidebar.tsx +247 -0
- package/.claude/worktrees/phase-2-mcp/src/components/SkillDiffViewer.tsx +154 -0
- package/.claude/worktrees/phase-2-mcp/src/components/ThemeAwareBackground.tsx +67 -0
- package/.claude/worktrees/phase-2-mcp/src/components/ThemeToggle.tsx +54 -0
- package/.claude/worktrees/phase-2-mcp/src/components/WelcomeHero.tsx +77 -0
- package/.claude/worktrees/phase-2-mcp/src/components/bits/ClickSpark.tsx +116 -0
- package/.claude/worktrees/phase-2-mcp/src/components/bits/CountUp.tsx +98 -0
- package/.claude/worktrees/phase-2-mcp/src/components/bits/DarkSelect.tsx +95 -0
- package/.claude/worktrees/phase-2-mcp/src/components/bits/DecryptedText.tsx +161 -0
- package/.claude/worktrees/phase-2-mcp/src/components/bits/ElectricBorder.tsx +184 -0
- package/.claude/worktrees/phase-2-mcp/src/components/bits/GlitchText.tsx +34 -0
- package/.claude/worktrees/phase-2-mcp/src/components/bits/ShinyText.tsx +55 -0
- package/.claude/worktrees/phase-2-mcp/src/components/bits/SpotlightCard.tsx +42 -0
- package/.claude/worktrees/phase-2-mcp/src/components/bits/TextType.tsx +95 -0
- package/.claude/worktrees/phase-2-mcp/src/components/bits/index.ts +9 -0
- package/.claude/worktrees/phase-2-mcp/src/components/pages/CuratorPage.tsx +632 -0
- package/.claude/worktrees/phase-2-mcp/src/components/pages/DatasetPage.tsx +271 -0
- package/.claude/worktrees/phase-2-mcp/src/components/pages/EvolutionPage.tsx +676 -0
- package/.claude/worktrees/phase-2-mcp/src/components/pages/FunctionCallingPage.tsx +1 -0
- package/.claude/worktrees/phase-2-mcp/src/components/pages/LogsPage.tsx +272 -0
- package/.claude/worktrees/phase-2-mcp/src/components/pages/MetricsPage.tsx +246 -0
- package/.claude/worktrees/phase-2-mcp/src/components/pages/OverviewPage.tsx +420 -0
- package/.claude/worktrees/phase-2-mcp/src/components/pages/SettingsPage.tsx +88 -0
- package/.claude/worktrees/phase-2-mcp/src/components/pages/SkillStudioPage.tsx +376 -0
- package/.claude/worktrees/phase-2-mcp/src/components/ui/animated-theme-toggler.tsx +97 -0
- package/.claude/worktrees/phase-2-mcp/src/components/ui/button.tsx +67 -0
- package/.claude/worktrees/phase-2-mcp/src/components/ui/card.tsx +103 -0
- package/.claude/worktrees/phase-2-mcp/src/components/ui/input.tsx +19 -0
- package/.claude/worktrees/phase-2-mcp/src/components/ui/separator.tsx +28 -0
- package/.claude/worktrees/phase-2-mcp/src/components/ui/sheet.tsx +147 -0
- package/.claude/worktrees/phase-2-mcp/src/components/ui/sidebar.tsx +702 -0
- package/.claude/worktrees/phase-2-mcp/src/components/ui/skeleton.tsx +13 -0
- package/.claude/worktrees/phase-2-mcp/src/components/ui/theme-toggle.tsx +272 -0
- package/.claude/worktrees/phase-2-mcp/src/components/ui/tooltip.tsx +57 -0
- package/.claude/worktrees/phase-2-mcp/src/hooks/use-mobile.ts +19 -0
- package/.claude/worktrees/phase-2-mcp/src/lib/api.ts +455 -0
- package/.claude/worktrees/phase-2-mcp/src/lib/queryClient.ts +12 -0
- package/.claude/worktrees/phase-2-mcp/src/lib/utils.ts +6 -0
- package/.claude/worktrees/phase-2-mcp/stitch/agent_dashboard/DESIGN_SPEC.md +521 -0
- package/.claude/worktrees/phase-2-mcp/stitch/agent_dashboard/prototype.html +676 -0
- package/.claude/worktrees/phase-2-mcp/stitch/curator_workspace/code.html +448 -0
- package/.claude/worktrees/phase-2-mcp/stitch/curator_workspace/screen.png +0 -0
- package/.claude/worktrees/phase-2-mcp/stitch/datasets/code.html +479 -0
- package/.claude/worktrees/phase-2-mcp/stitch/datasets/screen.png +0 -0
- package/.claude/worktrees/phase-2-mcp/stitch/evolution_history/code.html +461 -0
- package/.claude/worktrees/phase-2-mcp/stitch/evolution_history/screen.png +0 -0
- package/.claude/worktrees/phase-2-mcp/stitch/hermes_dashboard/DESIGN.md +192 -0
- package/.claude/worktrees/phase-2-mcp/stitch/hermes_dashboard/DESIGN_SPEC.md +455 -0
- package/.claude/worktrees/phase-2-mcp/stitch/hermes_overview/code.html +399 -0
- package/.claude/worktrees/phase-2-mcp/stitch/hermes_overview/screen.png +0 -0
- package/.claude/worktrees/phase-2-mcp/stitch/live_logs/code.html +324 -0
- package/.claude/worktrees/phase-2-mcp/stitch/live_logs/screen.png +0 -0
- package/.claude/worktrees/phase-2-mcp/stitch/skill_hub/code.html +596 -0
- package/.claude/worktrees/phase-2-mcp/stitch/skill_hub/screen.png +0 -0
- package/.claude/worktrees/phase-2-mcp/stitch/system_metrics/code.html +527 -0
- package/.claude/worktrees/phase-2-mcp/stitch/system_metrics/screen.png +0 -0
- package/.claude/worktrees/phase-2-mcp/stitch/system_settings/code.html +257 -0
- package/.claude/worktrees/phase-2-mcp/stitch/system_settings/screen.png +0 -0
- package/.claude/worktrees/phase-2-mcp/test_dashboard.py +201 -0
- package/.claude/worktrees/phase-2-mcp/tests/collectors/__init__.py +0 -0
- package/.claude/worktrees/phase-2-mcp/tests/collectors/fixtures/sample_session.jsonl +7 -0
- package/.claude/worktrees/phase-2-mcp/tests/collectors/test_claude_code_collector.py +171 -0
- package/.claude/worktrees/phase-2-mcp/tests/collectors/test_hermes_collector.py +167 -0
- package/.claude/worktrees/phase-2-mcp/tests/eval/test_engine.py +234 -0
- package/.claude/worktrees/phase-2-mcp/tests/eval/test_scorers.py +249 -0
- package/.claude/worktrees/phase-2-mcp/tests/storage/__init__.py +0 -0
- package/.claude/worktrees/phase-2-mcp/tests/storage/test_run_store.py +359 -0
- package/.claude/worktrees/phase-2-mcp/tests/test_curator.py +559 -0
- package/.claude/worktrees/phase-2-mcp/tests/test_mcp_server.py +114 -0
- package/.claude/worktrees/phase-2-mcp/tsconfig.json +34 -0
- package/.env.example +72 -0
- package/.kilocode/package-lock.json +378 -0
- package/.kilocode/package.json +5 -0
- package/AGENTS.md +5 -0
- package/CLAUDE.md +29 -0
- package/QA_AUDIT_PLAN.md +156 -0
- package/README.md +355 -0
- package/agent-agnostic-evolution-dashboard.md +405 -0
- package/backend/__init__.py +0 -0
- package/backend/collectors/__init__.py +0 -0
- package/backend/collectors/claude_code_collector.py +277 -0
- package/backend/collectors/hermes_collector.py +68 -0
- package/backend/curator.py +512 -0
- package/backend/eval/__init__.py +19 -0
- package/backend/eval/engine.py +116 -0
- package/backend/eval/scorers.py +201 -0
- package/backend/generate_dataset.py +86 -0
- package/backend/job_tracker.py +232 -0
- package/backend/main.py +1746 -0
- package/backend/mcp_server.py +250 -0
- package/backend/promethean/__init__.py +24 -0
- package/backend/promethean/cycle_orchestrator.py +270 -0
- package/backend/promethean/delta_validator.py +191 -0
- package/backend/promethean/dspy_compiler.py +315 -0
- package/backend/promethean/gepa_strategist.py +213 -0
- package/backend/promethean/models.py +260 -0
- package/backend/promethean/skill_deployer.py +195 -0
- package/backend/promethean/trace_ingestion.py +142 -0
- package/backend/requirements.txt +6 -0
- package/backend/sdd_evolve.py +459 -0
- package/backend/skill_detector.py +227 -0
- package/backend/skill_registry.py +289 -0
- package/backend/storage/__init__.py +5 -0
- package/backend/storage/run_store.py +393 -0
- package/backend/storage/schema.sql +99 -0
- package/backend/validate_evolution.py +267 -0
- package/bin/genoma.js +250 -0
- package/components.json +28 -0
- package/docs/api/hermes-api.openapi.yaml +438 -0
- package/docs/hero.svg +148 -0
- package/eslint.config.mjs +18 -0
- package/install.sh +245 -0
- package/next-env.d.ts +6 -0
- package/next.config.ts +32 -0
- package/package.json +46 -0
- package/pnpm-workspace.yaml +4 -0
- package/postcss.config.mjs +7 -0
- package/public/file.svg +1 -0
- package/public/fonts/SF-Pro-Display-Bold.otf +0 -0
- package/public/fonts/SF-Pro-Display-Heavy.otf +0 -0
- package/public/fonts/SF-Pro-Display-Medium.otf +0 -0
- package/public/fonts/SF-Pro-Display-Regular.otf +0 -0
- package/public/fonts/SF-Pro-Display-Semibold.otf +0 -0
- package/public/fonts/SF-Pro-Text-Bold.otf +0 -0
- package/public/fonts/SF-Pro-Text-Heavy.otf +0 -0
- package/public/fonts/SF-Pro-Text-Medium.otf +0 -0
- package/public/fonts/SF-Pro-Text-Regular.otf +0 -0
- package/public/fonts/SF-Pro-Text-Semibold.otf +0 -0
- package/public/globe.svg +1 -0
- package/public/next.svg +1 -0
- package/public/theme-preview.html +257 -0
- package/public/vercel.svg +1 -0
- package/public/window.svg +1 -0
- package/run.sh +26 -0
- package/scripts/postinstall.js +50 -0
- package/skills-lock.json +10 -0
- package/specs/event-schema.md +223 -0
- package/specs/examples/run.jsonl +3 -0
- package/src/app/api/[...path]/route.ts +55 -0
- package/src/app/api/auth/token/route.ts +22 -0
- package/src/app/evolution/page.tsx +589 -0
- package/src/app/favicon.ico +0 -0
- package/src/app/globals.css +321 -0
- package/src/app/layout.tsx +63 -0
- package/src/app/page.tsx +70 -0
- package/src/app/skills/page.tsx +369 -0
- package/src/components/ApiConfigCard.tsx +199 -0
- package/src/components/ColorBends.css +1 -0
- package/src/components/ColorBends.d.ts +1 -0
- package/src/components/ColorBends.jsx +1 -0
- package/src/components/CoreLoopToggle.tsx +111 -0
- package/src/components/EnvironmentStatus.tsx +176 -0
- package/src/components/EvolutionBackground.tsx +1 -0
- package/src/components/ReactQueryProvider.tsx +24 -0
- package/src/components/Sidebar.tsx +247 -0
- package/src/components/SkillDiffViewer.tsx +154 -0
- package/src/components/ThemeAwareBackground.tsx +67 -0
- package/src/components/ThemeToggle.tsx +54 -0
- package/src/components/WelcomeHero.tsx +77 -0
- package/src/components/bits/ClickSpark.tsx +116 -0
- package/src/components/bits/CountUp.tsx +98 -0
- package/src/components/bits/DarkSelect.tsx +95 -0
- package/src/components/bits/DecryptedText.tsx +161 -0
- package/src/components/bits/ElectricBorder.tsx +184 -0
- package/src/components/bits/GlitchText.tsx +34 -0
- package/src/components/bits/ShinyText.tsx +55 -0
- package/src/components/bits/SpotlightCard.tsx +42 -0
- package/src/components/bits/TextType.tsx +95 -0
- package/src/components/bits/index.ts +9 -0
- package/src/components/pages/CuratorPage.tsx +632 -0
- package/src/components/pages/DatasetPage.tsx +271 -0
- package/src/components/pages/EvolutionPage.tsx +676 -0
- package/src/components/pages/FunctionCallingPage.tsx +1 -0
- package/src/components/pages/LogsPage.tsx +272 -0
- package/src/components/pages/MetricsPage.tsx +246 -0
- package/src/components/pages/OverviewPage.tsx +420 -0
- package/src/components/pages/SettingsPage.tsx +88 -0
- package/src/components/pages/SkillStudioPage.tsx +376 -0
- package/src/components/ui/animated-theme-toggler.tsx +97 -0
- package/src/components/ui/button.tsx +67 -0
- package/src/components/ui/card.tsx +103 -0
- package/src/components/ui/input.tsx +19 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/sheet.tsx +147 -0
- package/src/components/ui/sidebar.tsx +702 -0
- package/src/components/ui/skeleton.tsx +13 -0
- package/src/components/ui/theme-toggle.tsx +272 -0
- package/src/components/ui/tooltip.tsx +57 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/lib/api.ts +455 -0
- package/src/lib/queryClient.ts +12 -0
- package/src/lib/utils.ts +6 -0
- package/stitch/agent_dashboard/DESIGN_SPEC.md +521 -0
- package/stitch/agent_dashboard/prototype.html +676 -0
- package/stitch/curator_workspace/code.html +448 -0
- package/stitch/curator_workspace/screen.png +0 -0
- package/stitch/datasets/code.html +479 -0
- package/stitch/datasets/screen.png +0 -0
- package/stitch/evolution_history/code.html +461 -0
- package/stitch/evolution_history/screen.png +0 -0
- package/stitch/hermes_dashboard/DESIGN.md +192 -0
- package/stitch/hermes_dashboard/DESIGN_SPEC.md +455 -0
- package/stitch/hermes_overview/code.html +399 -0
- package/stitch/hermes_overview/screen.png +0 -0
- package/stitch/live_logs/code.html +324 -0
- package/stitch/live_logs/screen.png +0 -0
- package/stitch/skill_hub/code.html +596 -0
- package/stitch/skill_hub/screen.png +0 -0
- package/stitch/system_metrics/code.html +527 -0
- package/stitch/system_metrics/screen.png +0 -0
- package/stitch/system_settings/code.html +257 -0
- package/stitch/system_settings/screen.png +0 -0
- package/test_dashboard.py +201 -0
- package/tests/collectors/__init__.py +0 -0
- package/tests/collectors/fixtures/sample_session.jsonl +7 -0
- package/tests/collectors/test_claude_code_collector.py +171 -0
- package/tests/collectors/test_hermes_collector.py +167 -0
- package/tests/eval/test_engine.py +234 -0
- package/tests/eval/test_scorers.py +249 -0
- package/tests/storage/__init__.py +0 -0
- package/tests/storage/test_run_store.py +359 -0
- package/tests/test_curator.py +559 -0
- package/tests/test_e2e_npm.py +621 -0
- package/tests/test_mcp_server.py +114 -0
- package/tsconfig.json +34 -0
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { api, fetchSkillProviders } from '@/lib/api';
|
|
3
|
+
|
|
4
|
+
import { useState, useEffect } from "react";
|
|
5
|
+
import { Sparkles, ToggleLeft, ToggleRight, Search, RefreshCw } from "lucide-react";
|
|
6
|
+
|
|
7
|
+
interface Skill {
|
|
8
|
+
name: string;
|
|
9
|
+
description: string;
|
|
10
|
+
enabled: boolean;
|
|
11
|
+
tags: string[];
|
|
12
|
+
is_fork?: boolean;
|
|
13
|
+
category?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface Provider {
|
|
17
|
+
name: string;
|
|
18
|
+
total: number;
|
|
19
|
+
enabled: number;
|
|
20
|
+
skills: Skill[];
|
|
21
|
+
path?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default function SkillsPage() {
|
|
25
|
+
const [providers, setProviders] = useState<Provider[]>([]);
|
|
26
|
+
const [loading, setLoading] = useState(true);
|
|
27
|
+
const [search, setSearch] = useState("");
|
|
28
|
+
const [filterProvider, setFilterProvider] = useState<string>("all");
|
|
29
|
+
const [filterStatus, setFilterStatus] = useState<string>("all");
|
|
30
|
+
const [deleteTarget, setDeleteTarget] = useState<{provider: string, skill: string} | null>(null);
|
|
31
|
+
|
|
32
|
+
const fetchSkills = async () => {
|
|
33
|
+
setLoading(true);
|
|
34
|
+
try {
|
|
35
|
+
const data = await fetchSkillProviders();
|
|
36
|
+
if (data.status === "ok") {
|
|
37
|
+
setProviders(data.providers);
|
|
38
|
+
}
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error("Error fetching skills:", error);
|
|
41
|
+
}
|
|
42
|
+
setLoading(false);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
fetchSkills();
|
|
47
|
+
}, []);
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
const handleDeleteSkill = async (provider: string, skillName: string, isGlobal: boolean = false) => {
|
|
51
|
+
if (!confirm(`¿Eliminar ${isGlobal ? "GLOBALMENTE" : "de "+provider} la skill "${skillName}"?`)) return;
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
const endpoint = isGlobal
|
|
55
|
+
? `/api/skills/global/${encodeURIComponent(skillName)}`
|
|
56
|
+
: `/api/skills/${provider}/${encodeURIComponent(skillName)}`;
|
|
57
|
+
|
|
58
|
+
const res = await api<{status:string; message?:string}>(endpoint, {
|
|
59
|
+
method: "DELETE",
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
if (res.status === "ok") {
|
|
63
|
+
fetchSkills();
|
|
64
|
+
} else {
|
|
65
|
+
alert("Error: " + (res.message || "unknown"));
|
|
66
|
+
}
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.error("Delete error:", error);
|
|
69
|
+
alert("Error eliminando skill");
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const toggleSkill = async (provider: string, skillName: string, enabled: boolean) => {
|
|
74
|
+
try {
|
|
75
|
+
await api<{status:string}>("/api/skills/toggle", {
|
|
76
|
+
method: "POST",
|
|
77
|
+
headers: { "Content-Type": "application/json" },
|
|
78
|
+
body: JSON.stringify({ provider, skill_name: skillName, enabled }),
|
|
79
|
+
});
|
|
80
|
+
// Actualizar localmente
|
|
81
|
+
setProviders(prev => prev.map(p => {
|
|
82
|
+
if (p.name !== provider) return p;
|
|
83
|
+
return {
|
|
84
|
+
...p,
|
|
85
|
+
skills: p.skills.map(s =>
|
|
86
|
+
s.name === skillName ? { ...s, enabled } : s
|
|
87
|
+
),
|
|
88
|
+
enabled: p.skills.filter(s =>
|
|
89
|
+
(s.name === skillName ? enabled : s.enabled)
|
|
90
|
+
).length,
|
|
91
|
+
};
|
|
92
|
+
}));
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.error("Error toggling skill:", error);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const [filterCategory, setFilterCategory] = useState<string>("all");
|
|
99
|
+
|
|
100
|
+
// Extraer categorías únicas
|
|
101
|
+
const allCategories = Array.from(
|
|
102
|
+
new Set(providers.flatMap(p => p.skills.map(s => s.category)))
|
|
103
|
+
).sort();
|
|
104
|
+
|
|
105
|
+
// Filtrar skills por búsqueda y categoría
|
|
106
|
+
const filteredProviders = providers.map(p => ({
|
|
107
|
+
...p,
|
|
108
|
+
skills: p.skills.filter(s => {
|
|
109
|
+
const q = search.toLowerCase();
|
|
110
|
+
const matchText =
|
|
111
|
+
s.name.toLowerCase().includes(q) ||
|
|
112
|
+
s.description.toLowerCase().includes(q);
|
|
113
|
+
const matchProvider = filterProvider === "all" || p.name === filterProvider;
|
|
114
|
+
const matchStatus = filterStatus === "all" ||
|
|
115
|
+
(filterStatus === "enabled" && s.enabled) ||
|
|
116
|
+
(filterStatus === "disabled" && !s.enabled);
|
|
117
|
+
const matchCategory = filterCategory === "all" || s.category === filterCategory;
|
|
118
|
+
return matchText && matchProvider && matchStatus && matchCategory;
|
|
119
|
+
}),
|
|
120
|
+
})).filter(p => p.skills.length > 0);
|
|
121
|
+
|
|
122
|
+
const providerIcons: Record<string, string> = {
|
|
123
|
+
"claude-code": "🤖",
|
|
124
|
+
"opencode": "💻",
|
|
125
|
+
"kilocode": "⚡",
|
|
126
|
+
"antigravity": "🚀",
|
|
127
|
+
"hermes": "🪽",
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const providerColors: Record<string, string> = {
|
|
131
|
+
"claude-code": "bg-orange-500/10 border-orange-500/30 text-orange-400",
|
|
132
|
+
"opencode": "bg-blue-500/10 border-blue-500/30 text-blue-400",
|
|
133
|
+
"kilocode": "bg-yellow-500/10 border-yellow-500/30 text-yellow-400",
|
|
134
|
+
"antigravity": "bg-purple-500/10 border-purple-500/30 text-purple-400",
|
|
135
|
+
"hermes": "bg-emerald-500/10 border-emerald-500/30 text-emerald-400",
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
<div className="min-h-screen bg-gray-950 text-gray-100 p-6">
|
|
140
|
+
<div className="max-w-6xl mx-auto">
|
|
141
|
+
{/* Header */}
|
|
142
|
+
<div className="flex items-center justify-between mb-8">
|
|
143
|
+
<div className="flex items-center gap-3">
|
|
144
|
+
<Sparkles className="w-8 h-8 text-purple-400" />
|
|
145
|
+
<div>
|
|
146
|
+
<h1 className="text-3xl font-bold">Skill Hub</h1>
|
|
147
|
+
<p className="text-gray-400 text-sm">
|
|
148
|
+
Gestiona tus skills de todos los proveedores
|
|
149
|
+
</p>
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
<button
|
|
153
|
+
onClick={fetchSkills}
|
|
154
|
+
disabled={loading}
|
|
155
|
+
className="flex items-center gap-2 px-4 py-2 bg-gray-800 hover:bg-gray-700 rounded-lg transition-colors"
|
|
156
|
+
>
|
|
157
|
+
<RefreshCw className={`w-4 h-4 ${loading ? "animate-spin" : ""}`} />
|
|
158
|
+
Refresh
|
|
159
|
+
</button>
|
|
160
|
+
</div>
|
|
161
|
+
|
|
162
|
+
{/* Search & Filters */}
|
|
163
|
+
<div className="relative mb-6">
|
|
164
|
+
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-500" />
|
|
165
|
+
<input
|
|
166
|
+
type="text"
|
|
167
|
+
placeholder="Buscar skills por nombre, proveedor o descripción..."
|
|
168
|
+
value={search}
|
|
169
|
+
onChange={(e) => setSearch(e.target.value)}
|
|
170
|
+
className="w-full pl-10 pr-4 py-3 bg-gray-900 border border-gray-800 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500"
|
|
171
|
+
/>
|
|
172
|
+
</div>
|
|
173
|
+
|
|
174
|
+
{/* Category & Provider Filters */}
|
|
175
|
+
<div className="flex flex-wrap gap-3 mb-6">
|
|
176
|
+
<select
|
|
177
|
+
value={filterCategory}
|
|
178
|
+
onChange={(e) => setFilterCategory(e.target.value)}
|
|
179
|
+
className="px-4 py-2 bg-gray-900 border border-gray-800 rounded-lg text-sm text-gray-300 focus:outline-none focus:ring-2 focus:ring-purple-500"
|
|
180
|
+
>
|
|
181
|
+
<option value="all">📁 Todas las categorías</option>
|
|
182
|
+
{allCategories.map(cat => (
|
|
183
|
+
<option key={cat} value={cat}>{cat}</option>
|
|
184
|
+
))}
|
|
185
|
+
</select>
|
|
186
|
+
|
|
187
|
+
<select
|
|
188
|
+
value={filterProvider}
|
|
189
|
+
onChange={(e) => setFilterProvider(e.target.value)}
|
|
190
|
+
className="px-4 py-2 bg-gray-900 border border-gray-800 rounded-lg text-sm text-gray-300 focus:outline-none focus:ring-2 focus:ring-purple-500"
|
|
191
|
+
>
|
|
192
|
+
<option value="all">🔧 Todos los providers</option>
|
|
193
|
+
{providers.map(p => (
|
|
194
|
+
<option key={p.name} value={p.name}>{p.name}</option>
|
|
195
|
+
))}
|
|
196
|
+
</select>
|
|
197
|
+
|
|
198
|
+
<select
|
|
199
|
+
value={filterStatus}
|
|
200
|
+
onChange={(e) => setFilterStatus(e.target.value)}
|
|
201
|
+
className="px-4 py-2 bg-gray-900 border border-gray-800 rounded-lg text-sm text-gray-300 focus:outline-none focus:ring-2 focus:ring-purple-500"
|
|
202
|
+
>
|
|
203
|
+
<option value="all">⚡ Todos los estados</option>
|
|
204
|
+
<option value="enabled">✅ Activas</option>
|
|
205
|
+
<option value="disabled">❌ Desactivadas</option>
|
|
206
|
+
</select>
|
|
207
|
+
|
|
208
|
+
{(filterCategory !== "all" || filterProvider !== "all" || filterStatus !== "all" || search) && (
|
|
209
|
+
<button
|
|
210
|
+
onClick={() => { setFilterCategory("all"); setFilterProvider("all"); setFilterStatus("all"); setSearch(""); }}
|
|
211
|
+
className="px-4 py-2 bg-gray-800 hover:bg-gray-700 rounded-lg text-sm text-gray-400 transition-colors"
|
|
212
|
+
>
|
|
213
|
+
Limpiar filtros
|
|
214
|
+
</button>
|
|
215
|
+
)}
|
|
216
|
+
</div>
|
|
217
|
+
|
|
218
|
+
{/* Stats */}
|
|
219
|
+
{!loading && (
|
|
220
|
+
<div className="grid grid-cols-2 md:grid-cols-5 gap-4 mb-8">
|
|
221
|
+
{providers.map(p => (
|
|
222
|
+
<div
|
|
223
|
+
key={p.name}
|
|
224
|
+
className={`p-4 rounded-lg border ${providerColors[p.name] || "bg-gray-800 border-gray-700"}`}
|
|
225
|
+
>
|
|
226
|
+
<div className="text-2xl mb-1">{providerIcons[p.name] || "🔧"}</div>
|
|
227
|
+
<div className="text-sm font-medium capitalize">{p.name}</div>
|
|
228
|
+
<div className="text-2xl font-bold">{p.enabled}/{p.total}</div>
|
|
229
|
+
<div className="text-xs opacity-70">habilitadas</div>
|
|
230
|
+
</div>
|
|
231
|
+
))}
|
|
232
|
+
</div>
|
|
233
|
+
)}
|
|
234
|
+
|
|
235
|
+
{/* Skills por proveedor */}
|
|
236
|
+
{loading ? (
|
|
237
|
+
<div className="flex items-center justify-center py-12">
|
|
238
|
+
<RefreshCw className="w-8 h-8 animate-spin text-purple-400" />
|
|
239
|
+
<span className="ml-3">Escaneando skills...</span>
|
|
240
|
+
</div>
|
|
241
|
+
) : (
|
|
242
|
+
<div className="space-y-8">
|
|
243
|
+
{filteredProviders.map(provider => (
|
|
244
|
+
<div key={provider.name} className="bg-gray-900/50 rounded-xl border border-gray-800 p-6">
|
|
245
|
+
<div className="flex items-center justify-between mb-4">
|
|
246
|
+
<div className="flex items-center gap-3">
|
|
247
|
+
<span className="text-3xl">{providerIcons[provider.name] || "🔧"}</span>
|
|
248
|
+
<div>
|
|
249
|
+
<h2 className="text-xl font-bold capitalize flex items-center gap-2">
|
|
250
|
+
{provider.name}
|
|
251
|
+
<span className="text-sm font-normal text-gray-500">
|
|
252
|
+
({provider.enabled}/{provider.total} activas)
|
|
253
|
+
</span>
|
|
254
|
+
</h2>
|
|
255
|
+
</div>
|
|
256
|
+
</div>
|
|
257
|
+
<div className="flex items-center gap-2">
|
|
258
|
+
<span className="px-3 py-1.5 rounded-lg text-sm font-medium bg-gray-800 text-gray-400 border border-gray-700">
|
|
259
|
+
{provider.total} skills
|
|
260
|
+
</span>
|
|
261
|
+
</div>
|
|
262
|
+
</div>
|
|
263
|
+
|
|
264
|
+
<div className="grid gap-3 md:grid-cols-2">
|
|
265
|
+
{provider.skills
|
|
266
|
+
.filter(skill => {
|
|
267
|
+
const q = search.toLowerCase();
|
|
268
|
+
const matchText =
|
|
269
|
+
skill.name.toLowerCase().includes(q) ||
|
|
270
|
+
skill.description.toLowerCase().includes(q) ||
|
|
271
|
+
skill.tags.some(t => t.toLowerCase().includes(q));
|
|
272
|
+
const matchProvider = filterProvider === "all" || provider.name === filterProvider;
|
|
273
|
+
const matchStatus = filterStatus === "all" ||
|
|
274
|
+
(filterStatus === "enabled" && skill.enabled) ||
|
|
275
|
+
(filterStatus === "disabled" && !skill.enabled);
|
|
276
|
+
return matchText && matchProvider && matchStatus;
|
|
277
|
+
})
|
|
278
|
+
.map(skill => (
|
|
279
|
+
<div
|
|
280
|
+
key={`${provider.name}-${skill.name}`}
|
|
281
|
+
className="p-4 bg-gray-800/50 rounded-lg border border-gray-700/50 hover:border-gray-600 transition-colors"
|
|
282
|
+
>
|
|
283
|
+
<div className="flex items-start justify-between gap-4">
|
|
284
|
+
<div className="flex-1 min-w-0">
|
|
285
|
+
<div className="flex items-center gap-2 mb-1">
|
|
286
|
+
<h3 className="font-medium text-gray-100 truncate">
|
|
287
|
+
{skill.name}
|
|
288
|
+
</h3>
|
|
289
|
+
{skill.is_fork && (
|
|
290
|
+
<span className="px-1.5 py-0.5 text-xs bg-yellow-500/20 text-yellow-300 border border-yellow-500/30 rounded">
|
|
291
|
+
FORK
|
|
292
|
+
</span>
|
|
293
|
+
)}
|
|
294
|
+
</div>
|
|
295
|
+
<p className="text-sm text-gray-400 line-clamp-2">
|
|
296
|
+
{skill.description}
|
|
297
|
+
</p>
|
|
298
|
+
{skill.tags.length > 0 && (
|
|
299
|
+
<div className="flex gap-1 mt-2 flex-wrap">
|
|
300
|
+
{skill.tags.slice(0, 4).map(tag => (
|
|
301
|
+
<span key={tag} className="px-2 py-0.5 text-xs bg-gray-700 text-gray-300 rounded">
|
|
302
|
+
{tag}
|
|
303
|
+
</span>
|
|
304
|
+
))}
|
|
305
|
+
{skill.tags.length > 4 && (
|
|
306
|
+
<span className="px-2 py-0.5 text-xs bg-gray-700 text-gray-300 rounded">
|
|
307
|
+
+{skill.tags.length - 4}
|
|
308
|
+
</span>
|
|
309
|
+
)}
|
|
310
|
+
</div>
|
|
311
|
+
)}
|
|
312
|
+
</div>
|
|
313
|
+
<div className="flex items-center gap-2 flex-wrap">
|
|
314
|
+
<button
|
|
315
|
+
onClick={() => toggleSkill(provider.name, skill.name, !skill.enabled)}
|
|
316
|
+
className={`flex items-center gap-2 px-3 py-1.5 rounded-lg text-sm font-medium transition-colors ${
|
|
317
|
+
skill.enabled
|
|
318
|
+
? "bg-green-500/10 text-green-400 border border-green-500/20 hover:bg-green-500/20"
|
|
319
|
+
: "bg-gray-700 text-gray-300 border border-gray-600 hover:bg-gray-600"
|
|
320
|
+
}`}
|
|
321
|
+
title={skill.enabled ? "Desactivar" : "Activar"}
|
|
322
|
+
>
|
|
323
|
+
{skill.enabled ? (
|
|
324
|
+
<><ToggleRight className="w-4 h-4" /> Activada</>
|
|
325
|
+
) : (
|
|
326
|
+
<><ToggleLeft className="w-4 h-4" /> Desactivada</>
|
|
327
|
+
)}
|
|
328
|
+
</button>
|
|
329
|
+
<button
|
|
330
|
+
onClick={() => handleDeleteSkill(provider.name, skill.name, false)}
|
|
331
|
+
className="px-3 py-1.5 rounded-lg text-sm font-medium bg-red-500/10 text-red-400 border border-red-500/20 hover:bg-red-500/20 transition-colors"
|
|
332
|
+
title="Eliminar de este provider"
|
|
333
|
+
>
|
|
334
|
+
🗑️ Quitar
|
|
335
|
+
</button>
|
|
336
|
+
{!skill.is_fork && (
|
|
337
|
+
<button
|
|
338
|
+
onClick={() => handleDeleteSkill(provider.name, skill.name, true)}
|
|
339
|
+
className="px-3 py-1.5 rounded-lg text-sm font-medium bg-orange-500/10 text-orange-400 border border-orange-500/20 hover:bg-orange-500/20 transition-colors"
|
|
340
|
+
title="Eliminar skill globalmente"
|
|
341
|
+
>
|
|
342
|
+
🗑️💥 Eliminar Global
|
|
343
|
+
</button>
|
|
344
|
+
)}
|
|
345
|
+
</div>
|
|
346
|
+
</div>
|
|
347
|
+
</div>
|
|
348
|
+
))}
|
|
349
|
+
</div>
|
|
350
|
+
</div>
|
|
351
|
+
))}
|
|
352
|
+
{!loading && filteredProviders.length === 0 ? (
|
|
353
|
+
<div className="text-center py-12 text-gray-500">
|
|
354
|
+
No se encontraron skills. Instala skills en los directorios:
|
|
355
|
+
<pre className="mt-2 p-4 bg-gray-900 rounded text-xs text-left overflow-auto">
|
|
356
|
+
{`~/.claude/skills/
|
|
357
|
+
~/.opencode/skills/
|
|
358
|
+
~/.kilocode/skills/
|
|
359
|
+
~/.antigravity/providers/
|
|
360
|
+
~/.hermes/skills/`}
|
|
361
|
+
</pre>
|
|
362
|
+
</div>
|
|
363
|
+
) : null}
|
|
364
|
+
</div>
|
|
365
|
+
)}
|
|
366
|
+
</div>
|
|
367
|
+
</div>
|
|
368
|
+
);
|
|
369
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { motion, AnimatePresence } from "framer-motion";
|
|
5
|
+
import { Key, Check, X, Loader2, Eye, EyeOff, Globe, Cpu } from "lucide-react";
|
|
6
|
+
|
|
7
|
+
interface ApiField {
|
|
8
|
+
key: string;
|
|
9
|
+
label: string;
|
|
10
|
+
placeholder: string;
|
|
11
|
+
icon: React.ReactNode;
|
|
12
|
+
status: "idle" | "testing" | "success" | "error";
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default function ApiConfigCard() {
|
|
16
|
+
const [fields, setFields] = useState<ApiField[]>([
|
|
17
|
+
{
|
|
18
|
+
key: "OPENAI_API_KEY",
|
|
19
|
+
label: "OpenAI API Key",
|
|
20
|
+
placeholder: "sk-...",
|
|
21
|
+
icon: <Globe className="w-4 h-4" />,
|
|
22
|
+
status: "idle",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
key: "NOUS_API_KEY",
|
|
26
|
+
label: "Nous API Key",
|
|
27
|
+
placeholder: "nous-...",
|
|
28
|
+
icon: <Cpu className="w-4 h-4" />,
|
|
29
|
+
status: "idle",
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
key: "OLLAMA_ENDPOINT",
|
|
33
|
+
label: "Ollama Endpoint",
|
|
34
|
+
placeholder: "http://localhost:11434",
|
|
35
|
+
icon: <Key className="w-4 h-4" />,
|
|
36
|
+
status: "idle",
|
|
37
|
+
},
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
const [values, setValues] = useState<Record<string, string>>({});
|
|
41
|
+
const [showValues, setShowValues] = useState<Record<string, boolean>>({});
|
|
42
|
+
|
|
43
|
+
const testConnection = async (fieldKey: string) => {
|
|
44
|
+
setFields((prev) =>
|
|
45
|
+
prev.map((f) => (f.key === fieldKey ? { ...f, status: "testing" } : f))
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
// Simulate API test
|
|
49
|
+
await new Promise((resolve) => setTimeout(resolve, 1500));
|
|
50
|
+
|
|
51
|
+
const success = values[fieldKey]?.length > 0;
|
|
52
|
+
setFields((prev) =>
|
|
53
|
+
prev.map((f) =>
|
|
54
|
+
f.key === fieldKey
|
|
55
|
+
? { ...f, status: success ? "success" : "error" }
|
|
56
|
+
: f
|
|
57
|
+
)
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
// Reset after 3s
|
|
61
|
+
setTimeout(() => {
|
|
62
|
+
setFields((prev) =>
|
|
63
|
+
prev.map((f) =>
|
|
64
|
+
f.key === fieldKey ? { ...f, status: "idle" } : f
|
|
65
|
+
)
|
|
66
|
+
);
|
|
67
|
+
}, 3000);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const statusColor = (status: ApiField["status"]) => {
|
|
71
|
+
switch (status) {
|
|
72
|
+
case "testing":
|
|
73
|
+
return "text-accent-cyan";
|
|
74
|
+
case "success":
|
|
75
|
+
return "text-success";
|
|
76
|
+
case "error":
|
|
77
|
+
return "text-error";
|
|
78
|
+
default:
|
|
79
|
+
return "text-muted-foreground";
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const statusIcon = (status: ApiField["status"]) => {
|
|
84
|
+
switch (status) {
|
|
85
|
+
case "testing":
|
|
86
|
+
return <Loader2 className="w-4 h-4 animate-spin" />;
|
|
87
|
+
case "success":
|
|
88
|
+
return <Check className="w-4 h-4" />;
|
|
89
|
+
case "error":
|
|
90
|
+
return <X className="w-4 h-4" />;
|
|
91
|
+
default:
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<motion.div
|
|
98
|
+
initial={{ opacity: 0, y: 20 }}
|
|
99
|
+
animate={{ opacity: 1, y: 0 }}
|
|
100
|
+
transition={{ delay: 0.3, duration: 0.6 }}
|
|
101
|
+
className="glass-card rounded-2xl p-6 md:p-8"
|
|
102
|
+
>
|
|
103
|
+
{/* Header */}
|
|
104
|
+
<div className="flex items-center gap-3 mb-6">
|
|
105
|
+
<div className="w-8 h-8 rounded-lg bg-accent-violet/10 flex items-center justify-center">
|
|
106
|
+
<Key className="w-4 h-4 text-accent-violet" />
|
|
107
|
+
</div>
|
|
108
|
+
<div>
|
|
109
|
+
<h2 className="text-sm font-semibold tracking-wide uppercase">
|
|
110
|
+
API Configuration
|
|
111
|
+
</h2>
|
|
112
|
+
<p className="text-xs text-muted-foreground mt-0.5">
|
|
113
|
+
Configure your model endpoints
|
|
114
|
+
</p>
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
{/* Fields */}
|
|
119
|
+
<div className="space-y-4">
|
|
120
|
+
{fields.map((field, index) => (
|
|
121
|
+
<motion.div
|
|
122
|
+
key={field.key}
|
|
123
|
+
initial={{ opacity: 0, x: -10 }}
|
|
124
|
+
animate={{ opacity: 1, x: 0 }}
|
|
125
|
+
transition={{ delay: 0.4 + index * 0.1 }}
|
|
126
|
+
className="space-y-2"
|
|
127
|
+
>
|
|
128
|
+
<label className="flex items-center gap-2 text-xs font-medium text-muted-foreground">
|
|
129
|
+
{field.icon}
|
|
130
|
+
{field.label}
|
|
131
|
+
</label>
|
|
132
|
+
<div className="flex gap-2">
|
|
133
|
+
<div className="relative flex-1">
|
|
134
|
+
<input
|
|
135
|
+
type={showValues[field.key] ? "text" : "password"}
|
|
136
|
+
placeholder={field.placeholder}
|
|
137
|
+
value={values[field.key] || ""}
|
|
138
|
+
onChange={(e) =>
|
|
139
|
+
setValues((prev) => ({ ...prev, [field.key]: e.target.value }))
|
|
140
|
+
}
|
|
141
|
+
className="w-full px-4 py-2.5 rounded-xl bg-white/[0.02] border border-white/[0.06] text-sm text-foreground placeholder:text-muted-foreground/50 transition-all duration-200"
|
|
142
|
+
/>
|
|
143
|
+
<button
|
|
144
|
+
onClick={() =>
|
|
145
|
+
setShowValues((prev) => ({
|
|
146
|
+
...prev,
|
|
147
|
+
[field.key]: !prev[field.key],
|
|
148
|
+
}))
|
|
149
|
+
}
|
|
150
|
+
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground/50 hover:text-muted-foreground transition-colors"
|
|
151
|
+
>
|
|
152
|
+
{showValues[field.key] ? (
|
|
153
|
+
<EyeOff className="w-3.5 h-3.5" />
|
|
154
|
+
) : (
|
|
155
|
+
<Eye className="w-3.5 h-3.5" />
|
|
156
|
+
)}
|
|
157
|
+
</button>
|
|
158
|
+
</div>
|
|
159
|
+
<motion.button
|
|
160
|
+
whileHover={{ scale: 1.02 }}
|
|
161
|
+
whileTap={{ scale: 0.98 }}
|
|
162
|
+
onClick={() => testConnection(field.key)}
|
|
163
|
+
disabled={field.status === "testing"}
|
|
164
|
+
className={`px-4 py-2.5 rounded-xl text-xs font-medium border transition-all duration-300 flex items-center gap-2 min-w-[120px] justify-center ${
|
|
165
|
+
field.status === "success"
|
|
166
|
+
? "bg-success/10 border-success/30 text-success"
|
|
167
|
+
: field.status === "error"
|
|
168
|
+
? "bg-error/10 border-error/30 text-error"
|
|
169
|
+
: field.status === "testing"
|
|
170
|
+
? "bg-accent-cyan/10 border-accent-cyan/30 text-accent-cyan"
|
|
171
|
+
: "bg-white/[0.02] border-white/[0.06] text-muted-foreground hover:border-white/10 hover:text-foreground"
|
|
172
|
+
}`}
|
|
173
|
+
>
|
|
174
|
+
<AnimatePresence mode="wait">
|
|
175
|
+
<motion.span
|
|
176
|
+
key={field.status}
|
|
177
|
+
initial={{ opacity: 0, y: 5 }}
|
|
178
|
+
animate={{ opacity: 1, y: 0 }}
|
|
179
|
+
exit={{ opacity: 0, y: -5 }}
|
|
180
|
+
className="flex items-center gap-2"
|
|
181
|
+
>
|
|
182
|
+
{statusIcon(field.status)}
|
|
183
|
+
{field.status === "idle"
|
|
184
|
+
? "Test"
|
|
185
|
+
: field.status === "testing"
|
|
186
|
+
? "Testing..."
|
|
187
|
+
: field.status === "success"
|
|
188
|
+
? "Connected"
|
|
189
|
+
: "Failed"}
|
|
190
|
+
</motion.span>
|
|
191
|
+
</AnimatePresence>
|
|
192
|
+
</motion.button>
|
|
193
|
+
</div>
|
|
194
|
+
</motion.div>
|
|
195
|
+
))}
|
|
196
|
+
</div>
|
|
197
|
+
</motion.div>
|
|
198
|
+
);
|
|
199
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// REMOVED — replaced with pure CSS gradient in ThemeAwareBackground.tsx
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// REMOVED — replaced with pure CSS gradient
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// REMOVED — replaced with pure CSS gradient in ThemeAwareBackground.tsx
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { motion } from "framer-motion";
|
|
5
|
+
import { Power, Activity } from "lucide-react";
|
|
6
|
+
|
|
7
|
+
export default function CoreLoopToggle() {
|
|
8
|
+
const [active, setActive] = useState(false);
|
|
9
|
+
const [statusText, setStatusText] = useState("Standby");
|
|
10
|
+
|
|
11
|
+
const handleToggle = () => {
|
|
12
|
+
const next = !active;
|
|
13
|
+
setActive(next);
|
|
14
|
+
setStatusText(next ? "Running" : "Standby");
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<motion.div
|
|
19
|
+
initial={{ opacity: 0, y: 20 }}
|
|
20
|
+
animate={{ opacity: 1, y: 0 }}
|
|
21
|
+
transition={{ delay: 0.5, duration: 0.6 }}
|
|
22
|
+
className="glass-card rounded-2xl p-6 md:p-8"
|
|
23
|
+
>
|
|
24
|
+
{/* Header */}
|
|
25
|
+
<div className="flex items-center gap-3 mb-6">
|
|
26
|
+
<div className="w-8 h-8 rounded-lg bg-accent-cyan/10 flex items-center justify-center">
|
|
27
|
+
<Activity className="w-4 h-4 text-accent-cyan" />
|
|
28
|
+
</div>
|
|
29
|
+
<div>
|
|
30
|
+
<h2 className="text-sm font-semibold tracking-wide uppercase">
|
|
31
|
+
Core Evolution Loop
|
|
32
|
+
</h2>
|
|
33
|
+
<p className="text-xs text-muted-foreground mt-0.5">
|
|
34
|
+
hermes_core_loop.py
|
|
35
|
+
</p>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
{/* Toggle + Status */}
|
|
40
|
+
<div className="flex items-center justify-between">
|
|
41
|
+
<div className="flex items-center gap-3">
|
|
42
|
+
{/* LED indicator */}
|
|
43
|
+
<div className="relative">
|
|
44
|
+
<div
|
|
45
|
+
className={`w-2.5 h-2.5 rounded-full ${
|
|
46
|
+
active ? "bg-success" : "bg-muted"
|
|
47
|
+
} ${active ? "led-pulse" : ""}`}
|
|
48
|
+
style={{ color: "#22c55e" }}
|
|
49
|
+
/>
|
|
50
|
+
{active && (
|
|
51
|
+
<div className="absolute inset-0 w-2.5 h-2.5 rounded-full bg-success animate-ping opacity-20" />
|
|
52
|
+
)}
|
|
53
|
+
</div>
|
|
54
|
+
<span className="text-sm text-muted-foreground">
|
|
55
|
+
{statusText}
|
|
56
|
+
</span>
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
{/* iOS-style toggle */}
|
|
60
|
+
<button
|
|
61
|
+
onClick={handleToggle}
|
|
62
|
+
className="relative focus:outline-none"
|
|
63
|
+
aria-label="Toggle core loop"
|
|
64
|
+
>
|
|
65
|
+
<motion.div
|
|
66
|
+
className={`w-14 h-8 rounded-full p-1 transition-colors duration-300 ${
|
|
67
|
+
active
|
|
68
|
+
? "bg-accent-violet"
|
|
69
|
+
: "bg-white/10"
|
|
70
|
+
}`}
|
|
71
|
+
>
|
|
72
|
+
<motion.div
|
|
73
|
+
animate={{ x: active ? 24 : 0 }}
|
|
74
|
+
transition={{ type: "spring", stiffness: 500, damping: 30 }}
|
|
75
|
+
className="w-6 h-6 rounded-full bg-white shadow-lg flex items-center justify-center"
|
|
76
|
+
>
|
|
77
|
+
<Power
|
|
78
|
+
className={`w-3 h-3 transition-colors ${
|
|
79
|
+
active ? "text-accent-violet" : "text-muted-foreground"
|
|
80
|
+
}`}
|
|
81
|
+
/>
|
|
82
|
+
</motion.div>
|
|
83
|
+
</motion.div>
|
|
84
|
+
</button>
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
{/* Status bar when active */}
|
|
88
|
+
{active && (
|
|
89
|
+
<motion.div
|
|
90
|
+
initial={{ opacity: 0, height: 0 }}
|
|
91
|
+
animate={{ opacity: 1, height: "auto" }}
|
|
92
|
+
exit={{ opacity: 0, height: 0 }}
|
|
93
|
+
className="mt-4 pt-4 border-t border-white/[0.06]"
|
|
94
|
+
>
|
|
95
|
+
<div className="flex items-center justify-between text-xs">
|
|
96
|
+
<span className="text-muted-foreground">Evolution cycle</span>
|
|
97
|
+
<span className="text-accent-cyan font-mono">#1</span>
|
|
98
|
+
</div>
|
|
99
|
+
<div className="flex items-center justify-between text-xs mt-2">
|
|
100
|
+
<span className="text-muted-foreground">Population</span>
|
|
101
|
+
<span className="text-accent-violet font-mono">5 / 5</span>
|
|
102
|
+
</div>
|
|
103
|
+
<div className="flex items-center justify-between text-xs mt-2">
|
|
104
|
+
<span className="text-muted-foreground">Best fitness</span>
|
|
105
|
+
<span className="text-success font-mono">0.87</span>
|
|
106
|
+
</div>
|
|
107
|
+
</motion.div>
|
|
108
|
+
)}
|
|
109
|
+
</motion.div>
|
|
110
|
+
);
|
|
111
|
+
}
|