diff --git a/.claude/settings.local.json b/.claude/settings.local.json index c18cfb3..e585ace 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -11,7 +11,16 @@ "WebFetch(domain:aspire.dev)", "mcp__playwright__browser_navigate", "mcp__playwright__browser_snapshot", - "Bash(git -C /c/users/wilco.boshoff/source/personal/dotnet/menlo log --oneline -20)" + "Bash(git -C /c/users/wilco.boshoff/source/personal/dotnet/menlo log --oneline -20)", + "Bash(git add:*)", + "Bash(git commit:*)", + "Bash(dotnet build:*)", + "Bash(dotnet test:*)", + "Bash(pnpm --dir /workspaces/menlo/src/ui/web test:all)", + "Bash(pnpm --dir /workspaces/menlo/src/ui/web lint:*)", + "Bash(grep:*)", + "Bash(find:*)", + "Bash(pnpm --dir src/ui/web lint:*)" ] }, "enableAllProjectMcpServers": true, diff --git a/.claude/skills/aspire-mcp/SKILL.md b/.claude/skills/aspire-mcp/SKILL.md index d55130d..48f0fbc 100644 --- a/.claude/skills/aspire-mcp/SKILL.md +++ b/.claude/skills/aspire-mcp/SKILL.md @@ -1,496 +1,95 @@ --- name: aspire-mcp -description: Use .NET Aspire MCP server to monitor, debug, and manage distributed application resources including services, databases, containers, logs, traces, and integrations +description: Use Aspire to monitor, debug, and manage distributed application resources including services, databases, containers, logs, traces, and integrations --- # Aspire MCP Server Integration -This skill enables AI agents to interact with .NET Aspire distributed applications through the Model Context Protocol (MCP) server. Use this skill to monitor resources, debug issues, analyze distributed traces, and manage your Aspire AppHost applications. - -## Purpose - -The Aspire MCP server provides programmatic access to: - -- All running resources (services, containers, executables, databases) -- Console and structured logs for debugging -- Distributed traces for performance analysis -- Resource health status and endpoints -- Available Aspire hosting integrations -- AppHost management and resource commands - -## When to Use This Skill - -Use this skill when you need to: - -- Check the health status of all Aspire resources -- Debug startup errors or runtime failures -- Analyze distributed tracing data across services -- Monitor console logs from specific resources -- Execute commands on running resources -- Discover available Aspire integrations -- Work with multiple AppHosts in a workspace ## Available MCP Tools ### Resource Management -#### `list_resources` - -Lists all resources including their state, health status, source, endpoints, and commands. - -**Use cases**: - -- Check if all services are running -- Verify resource health before running tests -- Get endpoint URLs for API testing -- Check which commands are available for each resource - -**Example queries**: +**`list_resources`** - Lists all resources with state, health, endpoints, and available commands. -- "Are all my Aspire resources healthy?" -- "What endpoints are available for the API service?" -- "Show me the status of all containers" - -#### `execute_resource_command` - -Executes a command on a specific resource. - -**Parameters**: - -- `resource_name`: Name of the resource (e.g., "menlo-api", "postgres") -- `command_name`: Command to execute (e.g., "Restart", "Stop", "Start") - -**Use cases**: - -- Restart unhealthy services -- Stop containers for maintenance -- Execute custom resource commands - -**Example queries**: - -- "Restart the menlo-api service" -- "Stop the postgres container" +**`execute_resource_command`** - Executes commands (Restart, Stop, Start) on specific resources. +- Parameters: `resource_name`, `command_name` ### Logging and Debugging -#### `list_console_logs` - -Retrieves console output logs for a specific resource. - -**Parameters**: - -- `resource_name`: Name of the resource to get logs from - -**Use cases**: - -- Read startup errors and stack traces -- Debug application crashes -- Monitor console output from services -- Check database initialization logs - -**Example queries**: - -- "Show me the console logs for menlo-api" -- "What errors are in the postgres logs?" -- "Check the last 50 lines of console output for the frontend" - -#### `list_structured_logs` - -Gathers structured logs with optional filtering by resource name. - -**Parameters**: - -- `resource_name` (optional): Filter logs by specific resource - -**Use cases**: - -- Search for specific error messages -- Filter logs by severity level -- Analyze application behavior -- Track request flow through services - -**Example queries**: - -- "Show me all error-level structured logs" -- "Find logs containing 'authentication failed'" -- "What structured logs does menlo-api have?" - -#### `list_traces` - -Fetches distributed traces; can filter using an optional resource name. - -**Parameters**: - -- `resource_name` (optional): Filter traces by resource +**`list_console_logs`** - Retrieves console output logs for debugging startup errors and crashes. +- Parameters: `resource_name` -**Use cases**: +**`list_structured_logs`** - Gathers structured logs with optional filtering by resource. +- Parameters: `resource_name` (optional) -- Analyze request performance across services -- Identify slow database queries -- Debug cross-service communication issues -- Track request flow through the system +**`list_traces`** - Fetches distributed traces for performance analysis. +- Parameters: `resource_name` (optional) -**Example queries**: - -- "Show me the slowest traces in the last 5 minutes" -- "Analyze HTTP request performance for menlo-api" -- "What traces involve the postgres database?" - -#### `list_trace_structured_logs` - -Extracts structured logs associated with a particular trace. - -**Parameters**: - -- `trace_id`: The trace ID to get logs for - -**Use cases**: - -- Correlate logs with specific requests -- Debug failed requests end-to-end -- Understand what happened during a slow request - -**Example queries**: - -- "Show me all logs for trace ID abc123" -- "What logs are associated with this slow request?" +**`list_trace_structured_logs`** - Extracts logs associated with a specific trace for correlation. +- Parameters: `trace_id` ### Integration Discovery -#### `list_integrations` - -Lists all available Aspire hosting integrations with their package IDs and versions. - -**Use cases**: - -- Discover what integrations are available -- Check package versions for integrations -- Find the right integration for a new dependency - -**Example queries**: - -- "What Aspire integrations are available for Redis?" -- "List all database integrations" -- "What version of the PostgreSQL integration is available?" - -#### `get_integration_docs` - -Retrieves documentation for specific Aspire hosting integration packages. - -**Parameters**: - -- `package_id`: The NuGet package ID of the integration +**`list_integrations`** - Lists available Aspire hosting integrations with package IDs and versions. -**Use cases**: - -- Learn how to configure an integration -- Check what options are available -- Get code examples for integration setup - -**Example queries**: - -- "Show me the documentation for Aspire.Hosting.PostgreSQL" -- "How do I configure the Redis integration?" -- "What are the options for the Ollama integration?" +**`get_integration_docs`** - Retrieves documentation for specific integration packages. +- Parameters: `package_id` ### AppHost Management -#### `list_apphosts` - -Shows all available AppHosts in the workspace. - -**Use cases**: - -- Work with monorepo containing multiple Aspire apps -- Switch between different application configurations -- Manage multiple environments (dev, staging, etc.) - -**Example queries**: - -- "What AppHosts are available?" -- "List all Aspire applications in this workspace" - -#### `select_apphost` - -Designates a specific AppHost as the active working context. - -**Parameters**: - -- `apphost_name`: Name of the AppHost to select - -**Use cases**: - -- Switch between multiple Aspire applications -- Focus on a specific application context -- Isolate operations to one AppHost - -**Example queries**: - -- "Switch to the Menlo.AppHost" -- "Make the staging AppHost active" - -## Setup Instructions - -### Prerequisites +**`list_apphosts`** - Shows all available AppHosts in the workspace. -1. .NET Aspire 13 or later installed -2. Aspire CLI installed (`dotnet tool install -g aspire`) -3. An Aspire AppHost project in your solution +**`select_apphost`** - Sets the active AppHost for operations. +- Parameters: `apphost_name` -### Configuration Steps +## Common Workflows -#### Option 1: Automatic Configuration (Recommended) +### Debugging Failures +1. List resources to check status +2. Check console/structured logs for errors +3. Analyze traces +4. Fix code and restart affected resources -Run the Aspire CLI command in your project directory: +### Performance Analysis +1. Get traces +2. Examine trace logs +3. Check resource logs +4. Optimize and verify improvement -```bash -cd /path/to/your/aspire/project -aspire mcp init -``` - -This automatically detects Claude Code and creates `.mcp.json` configuration. - -#### Option 2: Manual Configuration - -Create `.mcp.json` in your project root: - -```json -{ - "mcpServers": { - "aspire": { - "command": "aspire", - "args": ["mcp", "start"], - "transport": "stdio" - } - } -} -``` - -### Verification - -After configuration, restart Claude Code and verify: - -1. Ask: "Are all my Aspire resources running?" -2. Ask: "Show me the console logs for [your-service-name]" -3. Ask: "List available Aspire integrations" - -If the MCP server is configured correctly, you should get responses with actual resource data. - -## Best Practices - -### 1. Resource Monitoring Workflow - -When starting a development session: - -1. Check resource health: "Are all my Aspire resources healthy?" -2. Review any unhealthy services: "Show console logs for [unhealthy-service]" -3. Restart if needed: "Restart [service-name]" -4. Verify: "Check the status of [service-name]" - -### 2. Debugging Workflow - -When encountering errors: - -1. Check recent logs: "Show me console logs for [failing-service]" -2. Look for structured errors: "Show error-level structured logs for [service]" -3. Analyze traces: "Show me recent traces involving [service]" -4. Correlate: "Show structured logs for trace [trace-id]" -5. Fix and restart: "Restart [service]" - -### 3. Performance Analysis Workflow - -When optimizing performance: - -1. Get slow traces: "Show me the slowest traces in the last 10 minutes" -2. Analyze trace details: "Show structured logs for trace [trace-id]" -3. Check resource involvement: "What resources are involved in this trace?" -4. Identify bottlenecks: "Which service has the highest latency?" - -### 4. Integration Discovery Workflow - -When adding new dependencies: - -1. Search for integration: "What Aspire integrations are available for [technology]?" -2. Get documentation: "Show me the docs for [integration-package-id]" -3. Check version: "What version of [integration] is available?" -4. Follow docs to add integration to AppHost - -## Common Use Cases - -### Use Case 1: Debugging Startup Failures - -**Scenario**: Your API service won't start. - -**Workflow**: - -1. "List all resources and their status" -2. "Show console logs for menlo-api" (find startup error) -3. "Show structured logs for menlo-api" (get detailed error info) -4. Fix the code -5. "Restart menlo-api" -6. "Verify menlo-api is healthy" - -### Use Case 2: Analyzing Slow Requests - -**Scenario**: API responses are slow. - -**Workflow**: - -1. "Show me the slowest traces for menlo-api" -2. "Show structured logs for trace [slow-trace-id]" -3. "What database queries are in this trace?" -4. "Show postgres console logs" (check for slow queries) -5. Optimize query -6. "Compare trace performance before and after" - -### Use Case 3: Adding a New Integration - -**Scenario**: You want to add Redis caching. - -**Workflow**: - -1. "What Aspire integrations are available for Redis?" -2. "Show documentation for Aspire.Hosting.Redis" -3. Follow docs to add to AppHost -4. "List resources" (verify Redis appears) -5. "Show Redis console logs" (verify startup) - -### Use Case 4: Multi-Service Debugging - -**Scenario**: Request fails across multiple services. - -**Workflow**: - -1. "Show traces where status code is 500" -2. "Show structured logs for trace [failed-trace-id]" -3. "What resources are involved in this trace?" -4. "Show console logs for each involved service" -5. Identify root cause -6. "Restart affected services" +### Adding Integrations +1. Search available integrations +2. Get integration documentation +3. Add to AppHost following docs +4. Verify resource appears and is healthy ## Resource Exclusion -To exclude sensitive resources from MCP access, use `.ExcludeFromMcp()` in your AppHost: +Exclude sensitive resources from MCP access using `.ExcludeFromMcp()`: ```csharp var secretsDb = builder.AddPostgres("secrets-db") .ExcludeFromMcp(); // Not visible to AI agents ``` -This is useful for: - -- Production databases -- Secrets managers -- Sensitive configuration services -- Resources with destructive commands - -## Integration with Other Skills - -This skill works well with: - -- **mcp-builder**: Build custom MCP servers to extend Aspire -- **webapp-testing**: Use Aspire MCP to verify services before running tests -- **doc-coauthoring**: Document your AppHost configuration and resource dependencies - ## Troubleshooting -### MCP Server Not Responding - -1. Check Aspire CLI is installed: `aspire --version` -2. Verify AppHost is running: `dotnet run --project src/api/Menlo.AppHost` -3. Check `.mcp.json` is in project root -4. Restart Claude Code - -### No Resources Listed - -1. Verify AppHost is running -2. Check you're in the correct AppHost context: "List available AppHosts" -3. Switch if needed: "Select [apphost-name]" -4. Try: "List resources" again - -### Cannot Execute Commands - -1. Check resource supports the command: "List resources" (shows available commands) -2. Verify resource name is correct (case-sensitive) -3. Check resource state (some commands only work in specific states) - -### Logs Not Available - -1. Resources must be running to have logs -2. Some resources may not emit console logs (check structured logs instead) -3. Logs may be filtered by timestamp (request recent logs specifically) +| Issue | Solution | +| ------------------------- | ------------------------------------------------------------------------------------- | +| MCP server not responding | Verify Aspire CLI installed, AppHost running, `.mcp.json` exists, restart Claude Code | +| No resources listed | Check AppHost is running and selected correctly | +| Cannot execute commands | Verify resource name (case-sensitive) and command availability | +| Logs not available | Resources must be running; check structured logs if console logs missing | ## Limitations -- MCP server requires Aspire 13 or later -- Resources must be running to access logs and traces -- Trace retention is limited (typically last 15-30 minutes of activity) -- Some resource commands may require elevated permissions -- MCP server runs locally (no remote AppHost access) +- Requires Aspire 13+ +- Resources must be running for logs/traces +- Trace retention: 15-30 minutes +- Local only (no remote AppHost access) ## References - [Aspire MCP Server Documentation](https://aspire.dev/dashboard/mcp-server/) - [Configure the MCP Server](https://aspire.dev/get-started/configure-mcp/) -- [Scaling AI Agents with Aspire](https://devblogs.microsoft.com/aspire/scaling-ai-agents-with-aspire-isolation/) - [Model Context Protocol Specification](https://modelcontextprotocol.io) - -## Examples - -### Example 1: Complete Debugging Session - -User: "My API isn't responding" - -Agent (using aspire-mcp skill): - -1. Calls list_resources - Result: menlo-api state is "Failed" - -2. Calls list_console_logs for menlo-api - Result: "System.InvalidOperationException: Database connection failed" - -3. Calls list_resources again - Result: postgres state is "Running" but health is "Unhealthy" - -4. Calls list_console_logs for postgres - Result: "ERROR: max connections reached" - -Agent response: "Your API failed because the PostgreSQL database reached max connections. -Restart postgres to clear connections, then restart menlo-api." - -User: "Restart both services" - -Agent: -5. Calls execute_resource_command(resource_name: "postgres", command_name: "Restart") -6. Calls execute_resource_command(resource_name: "menlo-api", command_name: "Restart") -7. Calls list_resources to verify - Result: Both services now "Running" and "Healthy" - -Agent response: "Both services restarted successfully and are now healthy." - -### Example 2: Performance Investigation - -User: "Why are API requests slow?" - -Agent (using aspire-mcp skill): - -1. Calls list_traces(resource_name: "menlo-api") - Result: Shows traces with latencies from 50ms to 2500ms - -2. Calls list_trace_structured_logs for slowest trace - Result: Shows database query took 2.3 seconds - -3. Calls list_structured_logs(resource_name: "postgres") - Result: Shows query: "SELECT * FROM budgets JOIN transactions..." (no indexes) - -Agent response: "Your slow requests are caused by an unindexed database query. -The query 'SELECT * FROM budgets JOIN transactions...' takes 2.3 seconds. -Consider adding indexes on the join columns." - -## Notes - -- This skill is specific to .NET Aspire projects with MCP server configured -- Always verify AppHost is running before using MCP tools -- Resource names are case-sensitive and must match AppHost configuration -- Trace data is ephemeral (typically retained for 15-30 minutes) -- For production environments, use .ExcludeFromMcp() on sensitive resources diff --git a/.claude/skills/commit-changes/SKILL.md b/.claude/skills/commit-changes/SKILL.md new file mode 100644 index 0000000..c7cd7f9 --- /dev/null +++ b/.claude/skills/commit-changes/SKILL.md @@ -0,0 +1,10 @@ +--- +name: commit-changes +description: Commit changes to git +--- + +1. Check current changes from last commit +2. Use conventional commits +3. Avoid emojis unless truly helpful +4. List changes in the body +5. If a work item or business requirement is known add it with the action diff --git a/.claude/statusline/ctx_monitor.js b/.claude/statusline/ctx_monitor.js new file mode 100644 index 0000000..29e838b --- /dev/null +++ b/.claude/statusline/ctx_monitor.js @@ -0,0 +1,138 @@ +#!/usr/bin/env node +"use strict"; + +const fs = require("fs"); + +// --- input --- +const input = readJSON(0); // stdin +const sessionId = `\x1b[90m${String(input.session_id ?? "")}\x1b[0m`; +const transcript = input.transcript_path; +const model = input.model || {}; +const name = `\x1b[95m${String(model.display_name ?? "")}\x1b[0m`.trim(); +const CONTEXT_WINDOW = 200_000; + +// --- helpers --- +function readJSON(fd) { + try { + return JSON.parse(fs.readFileSync(fd, "utf8")); + } catch { + return {}; + } +} +function color(p) { + if (p >= 90) return "\x1b[31m"; // red + if (p >= 70) return "\x1b[33m"; // yellow + return "\x1b[32m"; // green +} +const comma = (n) => + new Intl.NumberFormat("en-US").format( + Math.max(0, Math.floor(Number(n) || 0)) + ); + +function usedTotal(u) { + return ( + (u?.input_tokens ?? 0) + + (u?.output_tokens ?? 0) + + (u?.cache_read_input_tokens ?? 0) + + (u?.cache_creation_input_tokens ?? 0) + ); +} + +function syntheticModel(j) { + const m = String(j?.message?.model ?? "").toLowerCase(); + return m === "" || m.includes("synthetic"); +} + +function assistantMessage(j) { + return j?.message?.role === "assistant"; +} + +function subContext(j) { + return j?.isSidechain === true; +} + +function contentNoResponse(j) { + const c = j?.message?.content; + return ( + Array.isArray(c) && + c.some( + (x) => + x && + x.type === "text" && + /no\s+response\s+requested/i.test(String(x.text)) + ) + ); +} + +function parseTs(j) { + const t = j?.timestamp; + const n = Date.parse(t); + return Number.isFinite(n) ? n : -Infinity; +} + +// Find the newest main-context entry by timestamp (not file order) +function newestMainUsageByTimestamp() { + if (!transcript) return null; + let latestTs = -Infinity; + let latestUsage = null; + + let lines; + try { + lines = fs.readFileSync(transcript, "utf8").split(/\r?\n/); + } catch { + return null; + } + + for (let i = lines.length - 1; i >= 0; i--) { + const line = lines[i].trim(); + if (!line) continue; + + let j; + try { + j = JSON.parse(line); + } catch { + continue; + } + const u = j.message?.usage; + if ( + subContext(j) || + syntheticModel(j) || + j.isApiErrorMessage === true || + usedTotal(u) === 0 || + contentNoResponse(j) || + !assistantMessage(j) + ) + continue; + + const ts = parseTs(j); + if (ts > latestTs) { + latestTs = ts; + latestUsage = u; + } + else if (ts == latestTs && usedTotal(u) > usedTotal(latestUsage)) { + latestUsage = u; + } + } + return latestUsage; +} + +// --- compute/print --- +const usage = newestMainUsageByTimestamp(); +if (!usage) { + console.log( + `${name} | \x1b[36mcontext window usage starts after your first question.\x1b[0m\nsession: ${sessionId}` + ); + process.exit(0); +} + +const used = usedTotal(usage); +const pct = CONTEXT_WINDOW > 0 ? Math.round((used * 1000) / CONTEXT_WINDOW) / 10 : 0; + +const usagePercentLabel = `${color(pct)}context used ${pct.toFixed(1)}%\x1b[0m`; +const usageCountLabel = `\x1b[33m(${comma(used)}/${comma( + CONTEXT_WINDOW +)})\x1b[0m`; + +console.log( + `${name} | ${usagePercentLabel} - ${usageCountLabel}\nsession: ${sessionId}` +); diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 897222f..a4cb2e1 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -77,6 +77,7 @@ "DOTNET_CLI_TELEMETRY_OPTOUT": "1", "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true", "MENLO_DEV_CONTAINER": "true", - "DOCKER_HOST": "unix:///var/run/docker.sock" + "DOCKER_HOST": "unix:///var/run/docker.sock", + "NODE_HOME": "/home/vscode/.local/share/mise/node/lts/bin" } } diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh index 452f2f7..9db0534 100644 --- a/.devcontainer/post-create.sh +++ b/.devcontainer/post-create.sh @@ -29,6 +29,10 @@ if ! grep -q 'mise activate' "$HOME/.bashrc" 2>/dev/null; then echo 'eval "$(mise activate bash)"' >> "$HOME/.bashrc" fi +if ! grep -q '$(which npx)' "$HOME/.bashrc" 2>/dev/null; then + echo 'export PATH="$(npx -y mise@latest which shims):$PATH"' >> "$HOME/.bashrc" +fi + echo "Node: $(node --version)" # ============================================ @@ -120,6 +124,18 @@ fi echo "Building .NET solution..." dotnet build --no-incremental || echo "Warning: dotnet build failed" +# ============================================ +# PLAYWRIGHT SETUP +# ============================================ +echo "Setting up Playwright browsers..." +if command -v pnpm &> /dev/null; then + # Install Playwright browsers and system dependencies + npx -y playwright@latest install --with-deps chromium || echo "Warning: Playwright browser installation failed" + echo "Playwright chromium browser installed" +else + echo "Skipping Playwright setup - pnpm not available (will be available after container restart)" +fi + # ============================================ # VERIFICATION # ============================================ diff --git a/.github/prompts/conventional-commit.prompt.md b/.github/prompts/conventional-commit.prompt.md deleted file mode 100644 index 669b79a..0000000 --- a/.github/prompts/conventional-commit.prompt.md +++ /dev/null @@ -1,52 +0,0 @@ ---- -description: Conventional Commit Prompt for Menlo -tools: ['execute/getTerminalOutput', 'execute/runTask', 'execute/getTaskOutput', 'execute/createAndRunTask', 'execute/runInTerminal', 'execute/runTests', 'read/problems', 'read/readFile', 'read/terminalSelection', 'read/terminalLastCommand', 'search', 'web/githubRepo', 'azure-mcp/search', 'sequential-thinking/*'] ---- - -You are an expert developer working on the Menlo Home Management project. All commits must follow semantic commit conventions to ensure clarity, traceability, and automation compatibility. - -Use the largest collection of changes to define the scope of the change and list additional changes as items in the description. - -If a specific set of files are included in the context, use them over #changes to define the commit message. If no context files have been added, use #changes to define the message. - -## Instructions - -- Include all changes as part of the description. -- Use the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) format. -- Reference relevant documentation in the `/docs` folder (requirements, guides, decisions, diagrams) when applicable. -- Link to GitHub issues or requirement folders if the commit relates to a tracked feature or bug. -- Use concise, descriptive language. Each commit should clearly communicate the intent and impact. -- If the change affects tests, documentation, or infrastructure, include that in the scope. -- For breaking changes, include `BREAKING CHANGE:` in the commit body and describe the impact. -- You MUST use the #runCommands tool to add the relevant files to the staging area -- You MUST use the #runCommands tool to write the commit message - -## Commit Message Structure - -First line is the title in lowercase. It is a quick reference description of what is changing and should follow conventional commit standards - -```markdown -(): - - - - -