Files
LazyBearWorks/CLAUDE.md
Shahovalov MIkhail 9b27cd7dc2 fix: исправить навигацию клавиатуры в TUI через GlobalKeyboardService
- Добавить GlobalKeyboardService — выделенный поток с блокирующим
  Console.ReadKey, единственный источник клавишных событий для TUI
- Убрать FocusManager из App.razor: перехватывал Tab до компонентов
- Удалить @onkeydown с <Select>: RazorConsole не пробрасывает Tab/стрелки
  через этот механизм
- Использовать FocusedValue вместо Value в Select для корректной подсветки
- Обновить CLAUDE.md и AGENTS.md: архитектура TUI, RazorConsole gotchas
- Добавить docs/tui_log.md: разбор проблемы и справочник по RazorConsole

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 23:31:53 +03:00

5.0 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Language Conventions

  • Output / explanations: Russian
  • Code: English
  • Comments, commit messages: Russian

Commands

# Build
dotnet build

# Run (starts TUI + HTTP MCP endpoint on port 5000)
dotnet run --project LazyBear.MCP

# Test MCP tool wiring (only needed after changing transport or tool registration)
npx @modelcontextprotocol/inspector dotnet run --project LazyBear.MCP

There are no test projects. After making changes, run dotnet build. If MCP wiring changed (new tool, new module, transport changes), also run the inspector.

Port is controlled via the ASPNETCORE_URLS environment variable (default http://localhost:5000). Ignore launchSettings.json — it shows a different port that is not used.

Architecture

LazyBear is a .NET 10 MCP server exposing Jira, Confluence, and Kubernetes integrations to AI clients. It runs in two simultaneous modes that share a single DI container:

  1. TUI (foreground) — RazorConsole terminal UI (App.razor), owns the console, handles keyboard navigation
  2. HTTP MCP endpoint (background)McpWebHostedService runs as a hosted service, serves MCP tool calls on port 5000

Both share the same singletons: ToolRegistryService, InMemoryLogSink, GlobalKeyboardService, and the three client providers.

Plugin System (IToolModule)

Each integration implements IToolModule (in Services/ToolRegistry/IToolModule.cs) and declares its ModuleName, ToolNames[], and Description. Tool classes are auto-discovered at startup via reflection using WithToolsFromAssembly() — classes are annotated with [McpServerToolType], methods with [McpServerTool]. Registering a new module requires only: implement IToolModule, create tool classes with attributes, and add DI registration in Program.cs.

ToolRegistryService

Singleton that tracks enabled/disabled state for both modules and individual tools at runtime (no restart needed). Uses ConcurrentDictionary for thread safety. Fires StateChanged event on toggles; TUI components subscribe to re-render. Tool keys use the format "ModuleName::ToolName".

Provider Pattern

Each client (K8sClientProvider, JiraClientProvider, ConfluenceClientProvider) is a lazy singleton. If initialization fails (missing config, unreachable endpoint), it captures an InitializationError string. Tools return error strings instead of throwing exceptions — this is intentional so MCP clients see the configuration issue gracefully.

Logging

InMemoryLogSink maintains a 500-entry circular ConcurrentQueue. All .NET logs flow through InMemoryLoggerProvider → sink → OnLog event → TUI Logs tab live view.

TUI Keyboard Input

GlobalKeyboardService (TUI/GlobalKeyboardService.cs) is the single source of keyboard events for the entire TUI. It runs a dedicated background thread with a blocking Console.ReadKey(intercept: true) — no polling. App.razor subscribes to OnKeyPressed, converts ConsoleKeyInfo to KeyboardEventArgs, and dispatches via InvokeAsync.

Do not use @onkeydown on <Select> or other RazorConsole components for navigation logic — the framework intercepts Tab and arrow keys internally before they reach component-level callbacks. See docs/tui_log.md for the full breakdown.

TUI Navigation

Tabs: Overview → Logs → Settings (switch with Tab/Shift+Tab). In Settings: arrow keys navigate the module→tool tree, Space toggles enable/disable, Enter expands/collapses. In Overview, Enter on a module jumps to its Settings entry.

Configuration Gotchas

  • Jira:Url is required; if missing, all Jira tools return string errors
  • K8s kubeconfig fallback order: explicit Kubernetes:KubeconfigPath~/.kube/config → in-cluster config
  • Source of truth: Program.cs, not README.md (README is aspirational)
  • Pages/ directory exists but Razor Pages are not enabled in Program.cs — do not use them
  • RazorConsole keyboard gotchas: @onkeydown on interactive components doesn't propagate Tab/arrows; FocusManager intercepts Tab globally; there is no public global key-intercept API. Full notes: docs/tui_log.md
  • GlobalKeyboardService registration pattern — registered as both singleton and hosted service so it can be injected into Razor components: services.AddSingleton<T>() + services.AddHostedService(sp => sp.GetRequiredService<T>())
  • Console.KeyAvailable polling causes rendering lag — it acquires the console mutex on every call and competes with RazorConsole's renderer. Always use blocking Console.ReadKey in a dedicated thread instead

Key Dependencies

Package Role
ModelContextProtocol.AspNetCore 1.2.0 HTTP MCP transport
KubernetesClient 19.0.2 K8s API
RestSharp 112.0.0 Jira / Confluence HTTP
RazorConsole.Core 0.5.0 Terminal UI framework
Polly 8.4.2 Retry/resilience policies