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,559 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Comprehensive test suite for the Hermes Dashboard Curator module.
|
|
3
|
+
|
|
4
|
+
Tests both the curator.py module functions and the API endpoints.
|
|
5
|
+
|
|
6
|
+
Run: python3 -m pytest tests/test_curator.py -v
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
import sys
|
|
12
|
+
import tempfile
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
import pytest
|
|
16
|
+
from fastapi.testclient import TestClient
|
|
17
|
+
|
|
18
|
+
# ── Fixtures ────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@pytest.fixture
|
|
22
|
+
def temp_skills_dir(tmp_path):
|
|
23
|
+
"""Create a temporary skills directory structure for testing."""
|
|
24
|
+
skills_dir = tmp_path / ".hermes" / "skills"
|
|
25
|
+
skills_dir.mkdir(parents=True)
|
|
26
|
+
|
|
27
|
+
# Create a bundled manifest
|
|
28
|
+
bundled = skills_dir / ".bundled_manifest"
|
|
29
|
+
bundled.write_text("bundled-skill:abc123\nanother-bundled:def456\n")
|
|
30
|
+
|
|
31
|
+
# Create .archive dir
|
|
32
|
+
archive_dir = skills_dir / ".archive"
|
|
33
|
+
archive_dir.mkdir()
|
|
34
|
+
|
|
35
|
+
return skills_dir
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@pytest.fixture
|
|
39
|
+
def temp_usage_file(temp_skills_dir):
|
|
40
|
+
"""Create a usage.json with sample data."""
|
|
41
|
+
usage = {
|
|
42
|
+
"active-skill": {
|
|
43
|
+
"use_count": 10,
|
|
44
|
+
"view_count": 25,
|
|
45
|
+
"patch_count": 2,
|
|
46
|
+
"state": "active",
|
|
47
|
+
"pinned": False,
|
|
48
|
+
"created_at": "2026-03-01T14:20:00+00:00",
|
|
49
|
+
"last_used_at": "2026-04-28T18:12:03+00:00",
|
|
50
|
+
"last_viewed_at": "2026-04-28T09:44:17+00:00",
|
|
51
|
+
"last_patched_at": "2026-04-20T22:01:55+00:00",
|
|
52
|
+
"archived_at": None,
|
|
53
|
+
},
|
|
54
|
+
"stale-skill": {
|
|
55
|
+
"use_count": 3,
|
|
56
|
+
"view_count": 5,
|
|
57
|
+
"patch_count": 0,
|
|
58
|
+
"state": "stale",
|
|
59
|
+
"pinned": False,
|
|
60
|
+
"created_at": "2026-01-15T10:00:00+00:00",
|
|
61
|
+
"last_used_at": "2026-03-20T08:30:00+00:00",
|
|
62
|
+
"last_viewed_at": "2026-03-20T08:30:00+00:00",
|
|
63
|
+
"last_patched_at": None,
|
|
64
|
+
"archived_at": None,
|
|
65
|
+
},
|
|
66
|
+
"archived-skill": {
|
|
67
|
+
"use_count": 1,
|
|
68
|
+
"view_count": 2,
|
|
69
|
+
"patch_count": 0,
|
|
70
|
+
"state": "archived",
|
|
71
|
+
"pinned": False,
|
|
72
|
+
"created_at": "2025-12-01T10:00:00+00:00",
|
|
73
|
+
"last_used_at": "2026-01-10T14:00:00+00:00",
|
|
74
|
+
"last_viewed_at": "2026-01-10T14:00:00+00:00",
|
|
75
|
+
"last_patched_at": None,
|
|
76
|
+
"archived_at": "2026-04-10T10:00:00+00:00",
|
|
77
|
+
},
|
|
78
|
+
"pinned-skill": {
|
|
79
|
+
"use_count": 5,
|
|
80
|
+
"view_count": 8,
|
|
81
|
+
"patch_count": 1,
|
|
82
|
+
"state": "active",
|
|
83
|
+
"pinned": True,
|
|
84
|
+
"created_at": "2026-02-10T09:00:00+00:00",
|
|
85
|
+
"last_used_at": "2026-04-25T16:45:00+00:00",
|
|
86
|
+
"last_viewed_at": "2026-04-24T11:30:00+00:00",
|
|
87
|
+
"last_patched_at": "2026-04-22T10:15:00+00:00",
|
|
88
|
+
"archived_at": None,
|
|
89
|
+
},
|
|
90
|
+
"bundled-skill": {
|
|
91
|
+
"use_count": 20,
|
|
92
|
+
"view_count": 50,
|
|
93
|
+
"patch_count": 5,
|
|
94
|
+
"state": "active",
|
|
95
|
+
"pinned": False,
|
|
96
|
+
"created_at": "2026-01-01T00:00:00+00:00",
|
|
97
|
+
"last_used_at": "2026-04-29T10:00:00+00:00",
|
|
98
|
+
"last_viewed_at": "2026-04-29T10:00:00+00:00",
|
|
99
|
+
"last_patched_at": "2026-04-28T15:00:00+00:00",
|
|
100
|
+
"archived_at": None,
|
|
101
|
+
},
|
|
102
|
+
}
|
|
103
|
+
usage_file = temp_skills_dir / ".usage.json"
|
|
104
|
+
usage_file.write_text(json.dumps(usage, indent=2))
|
|
105
|
+
return usage_file
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@pytest.fixture
|
|
109
|
+
def curator_module(temp_skills_dir, temp_usage_file, monkeypatch):
|
|
110
|
+
"""Patch curator paths to use temp directory and import the module."""
|
|
111
|
+
import importlib
|
|
112
|
+
|
|
113
|
+
# Patch all paths in curator.py
|
|
114
|
+
monkeypatch.setattr(
|
|
115
|
+
"backend.curator.SKILLS_DIR", temp_skills_dir
|
|
116
|
+
)
|
|
117
|
+
monkeypatch.setattr(
|
|
118
|
+
"backend.curator.USAGE_FILE", temp_skills_dir / ".usage.json"
|
|
119
|
+
)
|
|
120
|
+
monkeypatch.setattr(
|
|
121
|
+
"backend.curator.CURATOR_LOG_DIR", temp_skills_dir.parent / "logs" / "curator"
|
|
122
|
+
)
|
|
123
|
+
monkeypatch.setattr(
|
|
124
|
+
"backend.curator.ARCHIVE_DIR", temp_skills_dir / ".archive"
|
|
125
|
+
)
|
|
126
|
+
monkeypatch.setattr(
|
|
127
|
+
"backend.curator.BUNDLED_MANIFEST", temp_skills_dir / ".bundled_manifest"
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Ensure paths exist
|
|
131
|
+
(temp_skills_dir.parent / "logs" / "curator").mkdir(parents=True, exist_ok=True)
|
|
132
|
+
|
|
133
|
+
# Reload to pick up patched paths
|
|
134
|
+
if "backend.curator" in sys.modules:
|
|
135
|
+
del sys.modules["backend.curator"]
|
|
136
|
+
from backend import curator
|
|
137
|
+
return curator
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
# ═══════════════════════════════════════════════════════════════════════
|
|
141
|
+
# Unit Tests — Module Functions
|
|
142
|
+
# ═══════════════════════════════════════════════════════════════════════
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class TestLoadSaveUsage:
|
|
146
|
+
def test_load_returns_empty_when_no_file(self, temp_skills_dir, monkeypatch):
|
|
147
|
+
monkeypatch.setattr("backend.curator.USAGE_FILE", temp_skills_dir / "nonexistent.json")
|
|
148
|
+
from backend.curator import _load_usage
|
|
149
|
+
assert _load_usage() == {}
|
|
150
|
+
|
|
151
|
+
def test_save_and_load_roundtrip(self, temp_skills_dir, monkeypatch):
|
|
152
|
+
usage_file = temp_skills_dir / ".usage.json"
|
|
153
|
+
monkeypatch.setattr("backend.curator.USAGE_FILE", usage_file)
|
|
154
|
+
from backend.curator import _save_usage, _load_usage
|
|
155
|
+
|
|
156
|
+
data = {"test-skill": {"use_count": 42, "state": "active"}}
|
|
157
|
+
_save_usage(data)
|
|
158
|
+
loaded = _load_usage()
|
|
159
|
+
assert loaded == data
|
|
160
|
+
|
|
161
|
+
def test_save_creates_parent_dir(self, temp_skills_dir, monkeypatch):
|
|
162
|
+
nested = temp_skills_dir / "nested" / "sub" / ".usage.json"
|
|
163
|
+
monkeypatch.setattr("backend.curator.USAGE_FILE", nested)
|
|
164
|
+
from backend.curator import _save_usage
|
|
165
|
+
_save_usage({"x": {"state": "active"}})
|
|
166
|
+
assert nested.exists()
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class TestIsAgentCreated:
|
|
170
|
+
def test_bundled_skill_returns_false(self, temp_skills_dir, monkeypatch):
|
|
171
|
+
monkeypatch.setattr("backend.curator.BUNDLED_MANIFEST", temp_skills_dir / ".bundled_manifest")
|
|
172
|
+
# Ensure bundled manifest exists
|
|
173
|
+
(temp_skills_dir / ".bundled_manifest").write_text("bundled-skill:abc123\n")
|
|
174
|
+
from backend.curator import _is_agent_created
|
|
175
|
+
assert _is_agent_created("bundled-skill") is False
|
|
176
|
+
|
|
177
|
+
def test_unknown_skill_returns_true(self, temp_skills_dir, monkeypatch):
|
|
178
|
+
monkeypatch.setattr("backend.curator.BUNDLED_MANIFEST", temp_skills_dir / ".bundled_manifest")
|
|
179
|
+
from backend.curator import _is_agent_created
|
|
180
|
+
assert _is_agent_created("my-custom-skill") is True
|
|
181
|
+
|
|
182
|
+
def test_no_manifest_returns_true(self, monkeypatch):
|
|
183
|
+
# No manifest at all
|
|
184
|
+
from backend.curator import _is_agent_created
|
|
185
|
+
assert _is_agent_created("anything") is True
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class TestSkillExists:
|
|
189
|
+
def test_exists_in_primary(self, temp_skills_dir, monkeypatch):
|
|
190
|
+
skill_dir = temp_skills_dir / "my-skill"
|
|
191
|
+
skill_dir.mkdir()
|
|
192
|
+
monkeypatch.setattr("backend.curator.SKILLS_DIR", temp_skills_dir)
|
|
193
|
+
from backend.curator import _skill_exists
|
|
194
|
+
assert _skill_exists("my-skill") is True
|
|
195
|
+
|
|
196
|
+
def test_exists_in_archive(self, temp_skills_dir, monkeypatch):
|
|
197
|
+
archive_dir = temp_skills_dir / ".archive"
|
|
198
|
+
archive_dir.mkdir(exist_ok=True)
|
|
199
|
+
(archive_dir / "archived-skill").mkdir()
|
|
200
|
+
monkeypatch.setattr("backend.curator.SKILLS_DIR", temp_skills_dir)
|
|
201
|
+
monkeypatch.setattr("backend.curator.ARCHIVE_DIR", archive_dir)
|
|
202
|
+
from backend.curator import _skill_exists
|
|
203
|
+
assert _skill_exists("archived-skill") is True
|
|
204
|
+
|
|
205
|
+
def test_not_found(self, temp_skills_dir, monkeypatch):
|
|
206
|
+
monkeypatch.setattr("backend.curator.SKILLS_DIR", temp_skills_dir)
|
|
207
|
+
from backend.curator import _skill_exists
|
|
208
|
+
assert _skill_exists("nonexistent") is False
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class TestRecordUse:
|
|
212
|
+
def test_record_use_increments_counter(self, temp_skills_dir, monkeypatch):
|
|
213
|
+
# Create skill dir so _skill_exists passes
|
|
214
|
+
(temp_skills_dir / "test-skill").mkdir()
|
|
215
|
+
monkeypatch.setattr("backend.curator.SKILLS_DIR", temp_skills_dir)
|
|
216
|
+
monkeypatch.setattr("backend.curator.USAGE_FILE", temp_skills_dir / ".usage.json")
|
|
217
|
+
# Remove old manifest so it's agent-created
|
|
218
|
+
if (temp_skills_dir / ".bundled_manifest").exists():
|
|
219
|
+
(temp_skills_dir / ".bundled_manifest").unlink()
|
|
220
|
+
|
|
221
|
+
from backend.curator import record_use, get_skills_usage
|
|
222
|
+
|
|
223
|
+
result = record_use("test-skill", "use")
|
|
224
|
+
assert result["status"] == "ok"
|
|
225
|
+
assert result["action"] == "use"
|
|
226
|
+
|
|
227
|
+
skills = get_skills_usage()
|
|
228
|
+
assert len(skills) == 1
|
|
229
|
+
assert skills[0]["name"] == "test-skill"
|
|
230
|
+
assert skills[0]["use_count"] == 1
|
|
231
|
+
assert skills[0]["state"] == "active"
|
|
232
|
+
|
|
233
|
+
def test_record_view_and_patch(self, temp_skills_dir, monkeypatch):
|
|
234
|
+
(temp_skills_dir / "test-skill").mkdir()
|
|
235
|
+
monkeypatch.setattr("backend.curator.SKILLS_DIR", temp_skills_dir)
|
|
236
|
+
monkeypatch.setattr("backend.curator.USAGE_FILE", temp_skills_dir / ".usage.json")
|
|
237
|
+
if (temp_skills_dir / ".bundled_manifest").exists():
|
|
238
|
+
(temp_skills_dir / ".bundled_manifest").unlink()
|
|
239
|
+
|
|
240
|
+
from backend.curator import record_use, get_skills_usage
|
|
241
|
+
|
|
242
|
+
record_use("test-skill", "view")
|
|
243
|
+
record_use("test-skill", "patch")
|
|
244
|
+
|
|
245
|
+
skills = get_skills_usage()
|
|
246
|
+
assert skills[0]["view_count"] == 1
|
|
247
|
+
assert skills[0]["patch_count"] == 1
|
|
248
|
+
|
|
249
|
+
def test_unknown_skill_returns_error(self, temp_skills_dir, monkeypatch):
|
|
250
|
+
monkeypatch.setattr("backend.curator.SKILLS_DIR", temp_skills_dir)
|
|
251
|
+
monkeypatch.setattr("backend.curator.USAGE_FILE", temp_skills_dir / ".usage.json")
|
|
252
|
+
from backend.curator import record_use
|
|
253
|
+
result = record_use("nonexistent", "use")
|
|
254
|
+
assert result["status"] == "error"
|
|
255
|
+
|
|
256
|
+
def test_bundled_skill_skips(self, temp_skills_dir, monkeypatch):
|
|
257
|
+
# Keep the manifest so bundled-skill is recognized
|
|
258
|
+
(temp_skills_dir / ".bundled_manifest").write_text("bundled-skill:abc123\n")
|
|
259
|
+
# Create dir so _skill_exists passes
|
|
260
|
+
(temp_skills_dir / "bundled-skill").mkdir()
|
|
261
|
+
monkeypatch.setattr("backend.curator.SKILLS_DIR", temp_skills_dir)
|
|
262
|
+
monkeypatch.setattr("backend.curator.BUNDLED_MANIFEST", temp_skills_dir / ".bundled_manifest")
|
|
263
|
+
from backend.curator import record_use
|
|
264
|
+
result = record_use("bundled-skill", "use")
|
|
265
|
+
assert result["status"] == "skipped"
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
class TestPinUnpin:
|
|
269
|
+
def test_pin_skill(self, temp_skills_dir, monkeypatch):
|
|
270
|
+
(temp_skills_dir / "my-skill").mkdir()
|
|
271
|
+
monkeypatch.setattr("backend.curator.SKILLS_DIR", temp_skills_dir)
|
|
272
|
+
monkeypatch.setattr("backend.curator.USAGE_FILE", temp_skills_dir / ".usage.json")
|
|
273
|
+
if (temp_skills_dir / ".bundled_manifest").exists():
|
|
274
|
+
(temp_skills_dir / ".bundled_manifest").unlink()
|
|
275
|
+
|
|
276
|
+
from backend.curator import pin_skill, get_status
|
|
277
|
+
result = pin_skill("my-skill")
|
|
278
|
+
assert result["status"] == "ok"
|
|
279
|
+
assert result["pinned"] is True
|
|
280
|
+
|
|
281
|
+
status = get_status()
|
|
282
|
+
assert status["stats"]["pinned"] == 1
|
|
283
|
+
assert "my-skill" in status["pinned_skills"]
|
|
284
|
+
|
|
285
|
+
def test_unpin_skill(self, temp_skills_dir, monkeypatch):
|
|
286
|
+
"""Unpin a skill that exists in usage data."""
|
|
287
|
+
usage_file = temp_skills_dir / ".usage.json"
|
|
288
|
+
usage_file.write_text(json.dumps({
|
|
289
|
+
"pinned-skill": {
|
|
290
|
+
"use_count": 5, "view_count": 8, "patch_count": 1,
|
|
291
|
+
"state": "active", "pinned": True,
|
|
292
|
+
"created_at": "2026-02-10T09:00:00+00:00",
|
|
293
|
+
"last_used_at": "2026-04-25T16:45:00+00:00",
|
|
294
|
+
"last_viewed_at": None, "last_patched_at": None,
|
|
295
|
+
"archived_at": None,
|
|
296
|
+
}
|
|
297
|
+
}))
|
|
298
|
+
monkeypatch.setattr("backend.curator.USAGE_FILE", usage_file)
|
|
299
|
+
from backend.curator import unpin_skill
|
|
300
|
+
result = unpin_skill("pinned-skill")
|
|
301
|
+
assert result["status"] == "ok"
|
|
302
|
+
assert result["pinned"] is False
|
|
303
|
+
|
|
304
|
+
def test_pin_bundled_refuses(self, temp_skills_dir, monkeypatch):
|
|
305
|
+
(temp_skills_dir / "bundled-skill").mkdir()
|
|
306
|
+
(temp_skills_dir / ".bundled_manifest").write_text("bundled-skill:abc123\n")
|
|
307
|
+
monkeypatch.setattr("backend.curator.SKILLS_DIR", temp_skills_dir)
|
|
308
|
+
monkeypatch.setattr("backend.curator.BUNDLED_MANIFEST", temp_skills_dir / ".bundled_manifest")
|
|
309
|
+
from backend.curator import pin_skill
|
|
310
|
+
result = pin_skill("bundled-skill")
|
|
311
|
+
assert result["status"] == "error"
|
|
312
|
+
assert "never touched" in result["message"]
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
class TestGetStatus:
|
|
316
|
+
def test_status_returns_stats(self, curator_module, temp_usage_file):
|
|
317
|
+
status = curator_module.get_status()
|
|
318
|
+
assert status["status"] == "ok"
|
|
319
|
+
assert status["stats"]["active"] >= 3 # active-skill, pinned-skill, bundled-skill
|
|
320
|
+
assert status["stats"]["stale"] == 1
|
|
321
|
+
assert status["stats"]["archived"] == 1
|
|
322
|
+
assert status["stats"]["pinned"] == 1
|
|
323
|
+
|
|
324
|
+
def test_status_returns_lru(self, curator_module):
|
|
325
|
+
status = curator_module.get_status()
|
|
326
|
+
assert len(status["least_recently_used"]) >= 1
|
|
327
|
+
|
|
328
|
+
def test_status_last_run_none_when_no_reports(self, curator_module):
|
|
329
|
+
status = curator_module.get_status()
|
|
330
|
+
assert status["last_run"]["timestamp"] is None
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
class TestGetSkillsUsage:
|
|
334
|
+
def test_returns_sorted_by_use(self, curator_module):
|
|
335
|
+
skills = curator_module.get_skills_usage()
|
|
336
|
+
assert len(skills) >= 4
|
|
337
|
+
|
|
338
|
+
# Should be sorted by use_count descending
|
|
339
|
+
counts = [s["use_count"] for s in skills]
|
|
340
|
+
assert counts == sorted(counts, reverse=True)
|
|
341
|
+
|
|
342
|
+
def test_each_skill_has_required_fields(self, curator_module):
|
|
343
|
+
skills = curator_module.get_skills_usage()
|
|
344
|
+
for s in skills:
|
|
345
|
+
assert "name" in s
|
|
346
|
+
assert "state" in s
|
|
347
|
+
assert "pinned" in s
|
|
348
|
+
assert "use_count" in s
|
|
349
|
+
assert "view_count" in s
|
|
350
|
+
assert "agent_created" in s
|
|
351
|
+
|
|
352
|
+
def test_bundled_skill_flagged(self, curator_module):
|
|
353
|
+
skills = curator_module.get_skills_usage()
|
|
354
|
+
bundled = [s for s in skills if s["name"] == "bundled-skill"]
|
|
355
|
+
if bundled:
|
|
356
|
+
assert bundled[0]["agent_created"] is False
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
class TestCuratorRun:
|
|
360
|
+
def test_run_marks_stale_skills(self, temp_skills_dir, monkeypatch):
|
|
361
|
+
"""Test that skills unused for >30 days become stale."""
|
|
362
|
+
import datetime
|
|
363
|
+
|
|
364
|
+
# Create a skill with old usage data
|
|
365
|
+
usage = {
|
|
366
|
+
"old-skill": {
|
|
367
|
+
"use_count": 1,
|
|
368
|
+
"view_count": 1,
|
|
369
|
+
"patch_count": 0,
|
|
370
|
+
"state": "active",
|
|
371
|
+
"pinned": False,
|
|
372
|
+
"created_at": "2026-01-01T10:00:00+00:00",
|
|
373
|
+
"last_used_at": "2026-01-15T10:00:00+00:00",
|
|
374
|
+
"last_viewed_at": "2026-01-15T10:00:00+00:00",
|
|
375
|
+
"recent-skill": {
|
|
376
|
+
"use_count": 5,
|
|
377
|
+
"view_count": 10,
|
|
378
|
+
"patch_count": 0,
|
|
379
|
+
"state": "active",
|
|
380
|
+
"pinned": False,
|
|
381
|
+
"created_at": "2026-04-01T10:00:00+00:00",
|
|
382
|
+
"last_used_at": "2026-04-28T10:00:00+00:00",
|
|
383
|
+
"last_viewed_at": "2026-04-28T10:00:00+00:00",
|
|
384
|
+
},
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
usage_file = temp_skills_dir / ".usage.json"
|
|
389
|
+
usage_file.write_text(json.dumps(usage))
|
|
390
|
+
|
|
391
|
+
monkeypatch.setattr("backend.curator.SKILLS_DIR", temp_skills_dir)
|
|
392
|
+
monkeypatch.setattr("backend.curator.USAGE_FILE", usage_file)
|
|
393
|
+
monkeypatch.setattr("backend.curator.CURATOR_LOG_DIR", temp_skills_dir.parent / "logs" / "curator")
|
|
394
|
+
(temp_skills_dir.parent / "logs" / "curator").mkdir(parents=True, exist_ok=True)
|
|
395
|
+
|
|
396
|
+
from backend.curator import run_curator_review
|
|
397
|
+
|
|
398
|
+
result = run_curator_review(sync=True)
|
|
399
|
+
assert result["status"] == "ok"
|
|
400
|
+
assert "report_id" in result
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
# ═══════════════════════════════════════════════════════════════════════
|
|
404
|
+
# Integration Tests — API Endpoints
|
|
405
|
+
# ═══════════════════════════════════════════════════════════════════════
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
@pytest.fixture
|
|
409
|
+
def client(tmp_path, monkeypatch):
|
|
410
|
+
"""Create a TestClient with patched curator paths and sample data."""
|
|
411
|
+
skills_dir = tmp_path / ".hermes" / "skills"
|
|
412
|
+
skills_dir.mkdir(parents=True)
|
|
413
|
+
|
|
414
|
+
# Create bundled manifest
|
|
415
|
+
(skills_dir / ".bundled_manifest").write_text("bundled-skill:abc123\n")
|
|
416
|
+
|
|
417
|
+
# Create .archive
|
|
418
|
+
(skills_dir / ".archive").mkdir()
|
|
419
|
+
|
|
420
|
+
# Write sample usage data
|
|
421
|
+
usage = {
|
|
422
|
+
"active-skill": {"use_count": 10, "view_count": 25, "patch_count": 2,
|
|
423
|
+
"state": "active", "pinned": False,
|
|
424
|
+
"created_at": "2026-03-01T14:20:00+00:00",
|
|
425
|
+
"last_used_at": "2026-04-28T18:12:03+00:00",
|
|
426
|
+
"last_viewed_at": "2026-04-28T09:44:17+00:00",
|
|
427
|
+
"last_patched_at": "2026-04-20T22:01:55+00:00",
|
|
428
|
+
"archived_at": None},
|
|
429
|
+
"pinned-skill": {"use_count": 5, "view_count": 8, "patch_count": 1,
|
|
430
|
+
"state": "active", "pinned": True,
|
|
431
|
+
"created_at": "2026-02-10T09:00:00+00:00",
|
|
432
|
+
"last_used_at": "2026-04-25T16:45:00+00:00",
|
|
433
|
+
"last_viewed_at": None, "last_patched_at": None,
|
|
434
|
+
"archived_at": None},
|
|
435
|
+
"stale-skill": {"use_count": 3, "view_count": 5, "patch_count": 0,
|
|
436
|
+
"state": "stale", "pinned": False,
|
|
437
|
+
"created_at": "2026-01-15T10:00:00+00:00",
|
|
438
|
+
"last_used_at": "2026-03-20T08:30:00+00:00",
|
|
439
|
+
"last_viewed_at": "2026-03-20T08:30:00+00:00",
|
|
440
|
+
"last_patched_at": None, "archived_at": None},
|
|
441
|
+
}
|
|
442
|
+
usage_file = skills_dir / ".usage.json"
|
|
443
|
+
usage_file.write_text(json.dumps(usage, indent=2))
|
|
444
|
+
|
|
445
|
+
# Create some skill dirs so _skill_exists passes
|
|
446
|
+
for s in ["active-skill", "pinned-skill", "bundled-skill"]:
|
|
447
|
+
(skills_dir / s).mkdir(exist_ok=True)
|
|
448
|
+
|
|
449
|
+
# First import the curator module so it exists, THEN monkeypatch
|
|
450
|
+
import backend.curator as curator_mod
|
|
451
|
+
|
|
452
|
+
# Monkeypatch the already-imported module
|
|
453
|
+
monkeypatch.setattr(curator_mod, "SKILLS_DIR", skills_dir)
|
|
454
|
+
monkeypatch.setattr(curator_mod, "USAGE_FILE", usage_file)
|
|
455
|
+
monkeypatch.setattr(curator_mod, "CURATOR_LOG_DIR", tmp_path / "logs" / "curator")
|
|
456
|
+
monkeypatch.setattr(curator_mod, "ARCHIVE_DIR", skills_dir / ".archive")
|
|
457
|
+
monkeypatch.setattr(curator_mod, "BUNDLED_MANIFEST", skills_dir / ".bundled_manifest")
|
|
458
|
+
|
|
459
|
+
# Ensure logs dir
|
|
460
|
+
(tmp_path / "logs" / "curator").mkdir(parents=True, exist_ok=True)
|
|
461
|
+
|
|
462
|
+
# Now clear the cached main module so it re-imports curator fresh
|
|
463
|
+
if "backend.main" in sys.modules:
|
|
464
|
+
del sys.modules["backend.main"]
|
|
465
|
+
|
|
466
|
+
# Import main — it will import the patched curator
|
|
467
|
+
from backend import main as backend_main
|
|
468
|
+
|
|
469
|
+
# Store skills_dir in module-level var for test access
|
|
470
|
+
curator_mod._test_skills_dir = skills_dir
|
|
471
|
+
|
|
472
|
+
return TestClient(backend_main.app)
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
class TestAPIEndpoints:
|
|
476
|
+
def _skills_dir(self, client) -> Path:
|
|
477
|
+
"""Get the skills_dir from the module-level test var."""
|
|
478
|
+
import backend.curator
|
|
479
|
+
return backend.curator._test_skills_dir
|
|
480
|
+
|
|
481
|
+
def test_get_status(self, client):
|
|
482
|
+
resp = client.get("/api/curator/status")
|
|
483
|
+
assert resp.status_code == 200
|
|
484
|
+
data = resp.json()
|
|
485
|
+
assert data["status"] == "ok"
|
|
486
|
+
assert "stats" in data
|
|
487
|
+
assert "pinned_skills" in data
|
|
488
|
+
assert "least_recently_used" in data
|
|
489
|
+
|
|
490
|
+
def test_get_skills(self, client):
|
|
491
|
+
resp = client.get("/api/curator/skills")
|
|
492
|
+
assert resp.status_code == 200
|
|
493
|
+
data = resp.json()
|
|
494
|
+
assert "skills" in data
|
|
495
|
+
assert isinstance(data["skills"], list)
|
|
496
|
+
|
|
497
|
+
def test_pin_skill(self, client):
|
|
498
|
+
# Create skill dir in the same temp dir the client fixture uses
|
|
499
|
+
skills_dir = self._skills_dir(client)
|
|
500
|
+
(skills_dir / "my-custom").mkdir(exist_ok=True)
|
|
501
|
+
resp = client.post("/api/curator/pin/my-custom")
|
|
502
|
+
assert resp.status_code == 200, f"Expected 200, got {resp.status_code}: {resp.text}"
|
|
503
|
+
data = resp.json()
|
|
504
|
+
assert data["pinned"] is True
|
|
505
|
+
|
|
506
|
+
def test_unpin_skill(self, client):
|
|
507
|
+
resp = client.post("/api/curator/unpin/pinned-skill")
|
|
508
|
+
assert resp.status_code == 200, f"Expected 200, got {resp.status_code}: {resp.text}"
|
|
509
|
+
|
|
510
|
+
def test_run_curator(self, client):
|
|
511
|
+
resp = client.post("/api/curator/run")
|
|
512
|
+
assert resp.status_code == 200
|
|
513
|
+
data = resp.json()
|
|
514
|
+
assert data["status"] == "ok"
|
|
515
|
+
assert "report_id" in data
|
|
516
|
+
|
|
517
|
+
def test_get_reports(self, client):
|
|
518
|
+
resp = client.get("/api/curator/reports")
|
|
519
|
+
assert resp.status_code == 200
|
|
520
|
+
data = resp.json()
|
|
521
|
+
assert "reports" in data
|
|
522
|
+
|
|
523
|
+
def test_record_use(self, client):
|
|
524
|
+
skills_dir = self._skills_dir(client)
|
|
525
|
+
(skills_dir / "api-test-skill").mkdir(exist_ok=True)
|
|
526
|
+
resp = client.post(
|
|
527
|
+
"/api/curator/record-use",
|
|
528
|
+
json={"skill": "api-test-skill", "action": "use"},
|
|
529
|
+
)
|
|
530
|
+
assert resp.status_code == 200, f"Expected 200, got {resp.status_code}: {resp.text}"
|
|
531
|
+
data = resp.json()
|
|
532
|
+
assert data["status"] == "ok"
|
|
533
|
+
|
|
534
|
+
def test_record_use_missing_field(self, client):
|
|
535
|
+
resp = client.post("/api/curator/record-use", json={})
|
|
536
|
+
assert resp.status_code == 400
|
|
537
|
+
|
|
538
|
+
def test_record_use_invalid_action(self, client):
|
|
539
|
+
resp = client.post(
|
|
540
|
+
"/api/curator/record-use",
|
|
541
|
+
json={"skill": "test", "action": "invalid"},
|
|
542
|
+
)
|
|
543
|
+
assert resp.status_code == 400
|
|
544
|
+
|
|
545
|
+
def test_pin_bundled_returns_400(self, client):
|
|
546
|
+
resp = client.post("/api/curator/pin/bundled-skill")
|
|
547
|
+
assert resp.status_code == 400
|
|
548
|
+
|
|
549
|
+
def test_nonexistent_pin_returns_400(self, client):
|
|
550
|
+
resp = client.post("/api/curator/pin/nonexistent-skill")
|
|
551
|
+
assert resp.status_code == 400
|
|
552
|
+
|
|
553
|
+
def test_restore_nonexistent_returns_400(self, client):
|
|
554
|
+
resp = client.post("/api/curator/restore/nonexistent")
|
|
555
|
+
assert resp.status_code == 400
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
if __name__ == "__main__":
|
|
559
|
+
pytest.main(["-v", __file__])
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""Tests for backend.mcp_server MCP server."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
|
|
7
|
+
from backend.mcp_server import server, handle_tool_call
|
|
8
|
+
from backend.storage.run_store import RunStore
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_server_imports():
|
|
12
|
+
"""Server should import successfully."""
|
|
13
|
+
assert server is not None
|
|
14
|
+
assert server.name == "genoma"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_server_has_instructions():
|
|
18
|
+
"""Server should have proper instructions."""
|
|
19
|
+
assert "ingest_run" in server.instructions
|
|
20
|
+
assert "ingest_trace" in server.instructions
|
|
21
|
+
assert "query_runs" in server.instructions
|
|
22
|
+
assert "get_agent_stats" in server.instructions
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_ingest_run_minimal():
|
|
26
|
+
"""ingest_run should accept minimal required fields."""
|
|
27
|
+
async def run_test():
|
|
28
|
+
result = await handle_tool_call(
|
|
29
|
+
"ingest_run",
|
|
30
|
+
{
|
|
31
|
+
"run_id": "test-001",
|
|
32
|
+
"agent_name": "test-agent",
|
|
33
|
+
"started_at": "2026-05-19T00:00:00Z",
|
|
34
|
+
"task_name": "test-task",
|
|
35
|
+
"outcome": "success",
|
|
36
|
+
},
|
|
37
|
+
)
|
|
38
|
+
assert result.isError is False
|
|
39
|
+
assert "inserted" in result.content[0].text or "updated" in result.content[0].text
|
|
40
|
+
assert "test-001" in result.content[0].text
|
|
41
|
+
|
|
42
|
+
asyncio.run(run_test())
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_ingest_run_with_collector():
|
|
46
|
+
"""ingest_run should set collector='mcp-native' by default."""
|
|
47
|
+
async def run_test():
|
|
48
|
+
result = await handle_tool_call(
|
|
49
|
+
"ingest_run",
|
|
50
|
+
{
|
|
51
|
+
"run_id": "test-002",
|
|
52
|
+
"agent_name": "test",
|
|
53
|
+
"started_at": "2026-05-19T00:00:00Z",
|
|
54
|
+
"task_name": "test",
|
|
55
|
+
"outcome": "success",
|
|
56
|
+
},
|
|
57
|
+
)
|
|
58
|
+
assert result.isError is False
|
|
59
|
+
run = RunStore().get_run("test-002")
|
|
60
|
+
assert run.collector == "mcp-native"
|
|
61
|
+
|
|
62
|
+
asyncio.run(run_test())
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_ingest_trace():
|
|
66
|
+
"""ingest_trace should accept required fields."""
|
|
67
|
+
async def run_test():
|
|
68
|
+
result = await handle_tool_call(
|
|
69
|
+
"ingest_trace",
|
|
70
|
+
{
|
|
71
|
+
"agent": "test-agent",
|
|
72
|
+
"agent_version": "1.0.0",
|
|
73
|
+
"timestamp": "2026-05-19T00:00:00Z",
|
|
74
|
+
"task": "test-task",
|
|
75
|
+
"outcome": "success",
|
|
76
|
+
},
|
|
77
|
+
)
|
|
78
|
+
assert result.isError is False
|
|
79
|
+
assert "ingested" in result.content[0].text
|
|
80
|
+
|
|
81
|
+
asyncio.run(run_test())
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_query_runs():
|
|
85
|
+
"""query_runs should work on populated store."""
|
|
86
|
+
async def run_test():
|
|
87
|
+
result = await handle_tool_call("query_runs", {})
|
|
88
|
+
assert result.isError is False
|
|
89
|
+
content = result.content[0].text
|
|
90
|
+
assert "count" in content
|
|
91
|
+
|
|
92
|
+
asyncio.run(run_test())
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def test_get_agent_stats():
|
|
96
|
+
"""get_agent_stats should return agent list."""
|
|
97
|
+
async def run_test():
|
|
98
|
+
result = await handle_tool_call("get_agent_stats", {})
|
|
99
|
+
assert result.isError is False
|
|
100
|
+
content = result.content[0].text
|
|
101
|
+
assert "agents" in content
|
|
102
|
+
|
|
103
|
+
asyncio.run(run_test())
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
if __name__ == "__main__":
|
|
107
|
+
test_server_imports()
|
|
108
|
+
test_server_has_instructions()
|
|
109
|
+
test_ingest_run_minimal()
|
|
110
|
+
test_ingest_run_with_collector()
|
|
111
|
+
test_ingest_trace()
|
|
112
|
+
test_query_runs()
|
|
113
|
+
test_get_agent_stats()
|
|
114
|
+
print("✓ All tests passed")
|