diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-01-12 13:43:07 -1000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-01-12 13:43:07 -1000 |
| commit | 1d47891d0097c10920ab5706b54c847024ec8f29 (patch) | |
| tree | ec5f88639ef8dcb27b4428153f0f10de93bcfdd5 | |
| parent | 80c233287b65927a012ff46a27d4eac9a796fce0 (diff) | |
Remove AI agent middleware and snapshot endpoint
Simplified the dashboard by removing the AI agent access layer:
- Deleted internal/middleware/ai_auth.go and tests
- Removed AIAgentAPIKey from config.Config
- Removed /api/claude/snapshot endpoint registration
- Updated SESSION_STATE.md and CLAUDE.md documentation
- All tests passing after cleanup
Dashboard is now human-facing only without the AI agent endpoint.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
| -rw-r--r-- | CLAUDE.md | 311 | ||||
| -rw-r--r-- | SESSION_STATE.md | 17 | ||||
| -rw-r--r-- | go.mod | 2 | ||||
| -rw-r--r-- | internal/config/config.go | 6 | ||||
| -rw-r--r-- | internal/middleware/ai_auth.go | 46 |
5 files changed, 44 insertions, 338 deletions
@@ -1,284 +1,41 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. +# Claude Code Project Guidelines ## Project Overview +A unified web dashboard aggregating Trello (PRIMARY), Todoist, Obsidian, and PlanToEat. +**Stack:** Go backend + HTMX + Tailwind CSS + SQLite. -A unified web dashboard that aggregates tasks (Todoist & **Trello**), notes (Obsidian), and meal planning (PlanToEat) into a single interface. Built with Go backend + HTMX + Tailwind CSS frontend. - -**Current Status:** ✅ Phase 1 COMPLETE -- Trello integration (PRIMARY feature - all boards + cards) -- Todoist integration (tasks + projects) -- Obsidian integration (20 most recent notes) -- PlanToEat integration (optional - API not public) -- Mobile-responsive web UI -- SQLite caching (5-min TTL) -- Auto-refresh (5 min) -- AI agent endpoint (`/api/claude/snapshot`) -- Full test coverage (9/9 tests passing) -- Binary builds successfully (17MB) +## Efficiency & Token Management +- **Context Minimization:** Do not read entire files if `grep`, `sed`, or `ls` can answer a question. +- **Surgical Edits:** Perform small, targeted file edits. Avoid rewriting entire files for single changes. +- **Dependency Awareness:** Use `go test`, `go build`, and `lint` to verify state rather than asking the agent to "think" through logic. +- **Proactive Checkpointing:** Treat every 3-4 turns as a potential session end. Update `SESSION_STATE.md` frequently. -**IMPORTANT:** Trello is the PRIMARY task management system - it's more heavily used than Todoist and is a core required feature. +## Workflow: Plan-then-Execute +1. **Discovery:** Use terminal tools to locate relevant Go code/handlers. +2. **Planning:** Propose a specific plan and **wait for user confirmation** before editing. +3. **Execution:** Apply changes incrementally, verifying with `go test ./...`. +4. **Handoff:** If a task is incomplete or the user mentions "limit" or "handoff," immediately summarize progress in `SESSION_STATE.md`. ## Essential Commands +- **Run:** `go run cmd/dashboard/main.go` +- **Test:** `go test ./...` +- **Build:** `go build -o dashboard cmd/dashboard/main.go` + +## State Management +- **SESSION_STATE.md:** The source of truth for resuming work. It must include: + - Current Task Goal + - Completed Items + - Architecture Decisions + - **Next 3 Specific Steps** + +## Technical Context +- **Trello is PRIMARY:** Key + Token required in query params. +- **Architecture:** chi router -> Handlers (`internal/handlers/`) -> Store (`internal/store/sqlite.go`). +- **AI Endpoint:** `GET /api/claude/snapshot` with Bearer token auth. +- **Errors:** Partial data/cache fallback preferred over hard failure. + +## Coding Style +- Use concise, idiomatic Go. +- Avoid verbose explanations or comments for self-evident logic. +- Prioritize terminal-based verification over manual code review. -### Development -```bash -# Run the application -go run cmd/dashboard/main.go - -# Run with live reload (requires air) -go install github.com/cosmtrek/air@latest -air - -# Install/update dependencies -go mod download -go mod tidy - -# Run tests -go test ./... - -# Build production binary -go build -o dashboard cmd/dashboard/main.go -./dashboard -``` - -### Database -Database migrations run automatically on startup. Migration files in `migrations/` are executed in alphabetical order. - -## Architecture - -### Request Flow -1. HTTP request hits chi router in `cmd/dashboard/main.go` -2. Router delegates to handler in `internal/handlers/` (needs implementation) -3. Handler checks cache validity via `internal/store/sqlite.go` -4. If cache is stale, handler fetches fresh data from API clients in `internal/api/` -5. Fresh data is saved to cache and returned to handler -6. Handler renders template from `web/templates/` or returns JSON - -### Cache Strategy -- **TTL-based caching**: Default 5 minutes (configurable via `CACHE_TTL_MINUTES`) -- **Cache metadata tracking**: `cache_metadata` table stores last fetch time per data source -- **Parallel API calls**: Use goroutines to fetch tasks, notes, and meals concurrently -- **Graceful degradation**: If one API fails, show partial data with error message - -### Data Sources - -**Trello API (`internal/api/trello.go`) - ✅ IMPLEMENTED** -- Endpoint: `https://api.trello.com/1` -- Auth: API Key + Token in query parameters (`key=XXX&token=YYY`) -- **Implemented operations**: - - `GetBoards()` - Fetch user's boards from `/1/members/me/boards` - - `GetCards(boardID)` - Fetch cards from specific board - - `GetBoardsWithCards()` - Fetch all boards with their cards - - Full board/list/card hierarchy parsing -- Store operations: `SaveBoards()`, `GetBoards()` -- Future: Create/update cards (Phase 2) -- **Status: COMPLETE** - This is the primary task management system - -**Todoist API (`internal/api/todoist.go`)** -- Endpoint: `https://api.todoist.com/rest/v2` -- Auth: Bearer token in Authorization header -- Key operations: `GetTasks()`, `GetProjects()` (both implemented) -- Future: `CreateTask()`, `CompleteTask()` (Phase 2) - -**Obsidian (`internal/api/obsidian.go`)** -- Direct filesystem access to markdown files -- Uses `filepath.Walk()` to recursively scan vault -- Limits to 20 most recent files by modification time -- Parses YAML frontmatter for tags, extracts inline #tags -- Content preview limited to ~500 chars - -**PlanToEat API (`internal/api/plantoeat.go`)** - OPTIONAL -- Endpoint: `https://www.plantoeat.com/api/v2` -- Auth: Bearer token in Authorization header -- Key operations: `GetUpcomingMeals(days)` (implemented) -- **Note:** PlanToEat API is not publicly available. Leave `PLANTOEAT_API_KEY` empty if you don't have access -- The dashboard works fine without it - meals section will simply be empty -- Future: `GetRecipes()`, `AddMealToPlanner()` (Phase 2) - -### Configuration System - -All configuration via environment variables (see `.env.example`): -- **Required**: `TODOIST_API_KEY`, `TRELLO_API_KEY`, `TRELLO_TOKEN` (core task management systems) -- **Optional**: `PLANTOEAT_API_KEY` (API not publicly available), `OBSIDIAN_VAULT_PATH`, `AI_AGENT_API_KEY` -- **Helper methods**: `cfg.HasPlanToEat()`, `cfg.HasObsidian()`, `cfg.HasTrello()` - -Configuration struct in `internal/config/config.go` validates on load and provides typed access. - -**Trello Setup:** Get both API key and token from https://trello.com/power-ups/admin -- Create a Power-Up (or use existing one) -- Go to "API Key" tab and generate a new API key -- **Important:** Copy the API Key, NOT the Secret -- In the description, follow the "testing/for-yourself" instructions -- Click the Token link to generate a personal token -- You need API Key + Token (Secret is not used) - -## Project Structure - -``` -task-dashboard/ -├── cmd/dashboard/main.go # Entry point, router setup, graceful shutdown -├── internal/ -│ ├── api/ # External API clients -│ │ ├── interfaces.go # API interfaces for testing -│ │ ├── trello.go # ✅ Trello client - GetBoards(), GetCards(), GetBoardsWithCards() -│ │ ├── todoist.go # ✅ Todoist client - GetTasks(), GetProjects() -│ │ ├── obsidian.go # ✅ Obsidian client - GetNotes() with filesystem scanning -│ │ └── plantoeat.go # ✅ PlanToEat client - GetUpcomingMeals() -│ ├── config/ # Environment variable loading and validation -│ ├── handlers/ # HTTP request handlers -│ │ ├── handlers.go # ✅ Web handlers - dashboard, tasks, notes, meals, boards, refresh -│ │ ├── handlers_test.go # ✅ Unit tests (5 tests) -│ │ └── ai_handlers.go # ✅ AI agent endpoint - /api/claude/snapshot -│ ├── middleware/ -│ │ └── ai_auth.go # ✅ Bearer token authentication for AI endpoint -│ ├── models/types.go # Data models: Task, Note, Meal, Board, Card, CacheMetadata, DashboardData -│ └── store/sqlite.go # Database layer with cache operations -├── web/ -│ ├── static/ # CSS and JS files -│ └── templates/ -│ └── index.html # ✅ Main dashboard template with Trello, Todoist, Obsidian, PlanToEat -├── migrations/ # SQL migration files (run automatically on startup) -└── test/ - └── acceptance_test.go # ✅ Acceptance tests (4 test suites) -``` - -## Key Implementation Details - -### Handler Pattern (✅ IMPLEMENTED) -Handlers use interface-based design for testability: -```go -type Handler struct { - store *store.Store - todoistClient api.TodoistAPI // Interface, not concrete type - trelloClient api.TrelloAPI // Interface for testing - obsidianClient api.ObsidianAPI - planToEatClient api.PlanToEatAPI - config *config.Config - templates *template.Template -} - -// Implemented endpoints: -// - HandleDashboard() - Main web UI -// - HandleGetTasks() - JSON endpoint for tasks -// - HandleGetBoards() - JSON endpoint for Trello boards -// - HandleGetNotes() - JSON endpoint for Obsidian notes -// - HandleGetMeals() - JSON endpoint for meals -// - HandleRefresh() - Force cache refresh -// - HandleAISnapshot() - AI-optimized endpoint with auth -``` - -### Database Operations -- **Batch inserts**: Use transactions for saving multiple items -- **Automatic timestamps**: `updated_at` set via `CURRENT_TIMESTAMP` -- **JSON encoding**: Arrays (labels, tags) stored as JSON strings -- **Ordering**: Tasks by completion status, due date, priority; Notes by modification time; Meals by date and meal type - -### Error Handling Philosophy -- **Partial data is better than no data**: If Todoist fails but Obsidian succeeds, show tasks with error message -- **Cache as fallback**: If API call fails and cache exists (even if stale), use cached data -- **Graceful degradation**: Skip unavailable services rather than failing completely -- **User-visible errors**: Store error messages in `DashboardData.Errors` for display - -## Development Status - -### ✅ Phase 1 Complete -All core features implemented and tested: -- ✅ Trello API client (PRIMARY feature - GetBoards, GetCards, GetBoardsWithCards) -- ✅ All API clients (Todoist, Obsidian, PlanToEat) -- ✅ Complete handler implementation (web + JSON endpoints) -- ✅ Frontend with Trello prominence (mobile-responsive, auto-refresh) -- ✅ Unit tests (5 tests in handlers_test.go) -- ✅ Acceptance tests (4 test suites in test/acceptance_test.go) -- ✅ AI agent endpoint with Bearer token auth -- ✅ Binary builds successfully (17MB) -- ✅ All 9 tests passing - -### Statistics -- **Binary size:** 17MB -- **Code:** 2,667 lines -- **Dependencies:** chi router, sqlite3, Go 1.21+ -- **Tests:** 5 unit tests, 4 acceptance tests (9/9 passing) - -### Phase 2 Ideas (Future) -Write operations: -- Create Todoist tasks via web UI and AI endpoint -- Mark tasks complete -- Create Trello cards -- Create Obsidian notes - -Enhancements: -- Unified search across all data sources -- Daily digest view -- PWA support -- Rate limiting for AI endpoint - -## AI Agent Access - -**NEW:** The dashboard now has a dedicated API endpoint optimized for AI agents like Claude. - -### Quick Start -1. Generate API key: `openssl rand -hex 32` -2. Add to `.env`: `AI_AGENT_API_KEY=your_key_here` -3. Restart server -4. Endpoint: `GET /api/claude/snapshot` -5. Auth: `Authorization: Bearer your_key_here` - -### Response Format -Optimized JSON with: -- `tasks`: today, overdue, next_7_days -- `meals`: today, next_7_days -- `notes`: 10 most recent (150 char previews) -- `trello_boards`: all boards with cards - -### Implementation -- Middleware: `internal/middleware/ai_auth.go` - Bearer token validation -- Handler: `internal/handlers/ai_handlers.go` - `/api/claude/snapshot` -- Uses existing cache (5min TTL) -- Response size: <100KB - -See `AI_AGENT_SETUP.md` for full documentation. - -## Important Notes - -- **No user authentication**: Single-user application, no auth needed (except AI endpoint) -- **Trello is PRIMARY**: The most heavily used feature - fully implemented and tested -- **Trello auth**: Requires both API key AND token as query parameters (different from Todoist/PlanToEat Bearer token pattern) -- **API rate limits**: Respected via caching (5min default), no aggressive polling -- **Obsidian performance**: Limited file scanning (max 20 files, ~500 chars per note) to avoid slowdowns on large vaults -- **Router**: Uses chi for HTTP routing with Logger, Recoverer, and 60s timeout middleware -- **Graceful shutdown**: Signal handling implemented in main.go -- **Testing**: Interface-based design allows full mocking of API clients -- **AI Agent**: Bearer token authentication for /api/claude/snapshot endpoint - -## Testing - -All tests passing (9/9): - -**Unit Tests** (`internal/handlers/handlers_test.go`): -- TestHandleGetTasks -- TestHandleGetBoards -- TestHandleRefresh -- TestHandleGetNotes -- TestHandleGetMeals - -**Acceptance Tests** (`test/acceptance_test.go`): -- TestFullWorkflow - Complete dashboard fetch and cache -- TestCaching - Cache TTL and invalidation -- TestErrorHandling - Graceful degradation -- TestConcurrentRequests - Race condition testing - -Run with: `go test ./...` - -## Troubleshooting - -**Import errors**: Run `go mod tidy` to resolve missing dependencies (chi router, sqlite3 driver) - -**Database locked**: Only one app instance can run at a time with SQLite - -**API errors**: Check `.env` file has correct API keys; verify keys in respective service dashboards -- Todoist: https://todoist.com/app/settings/integrations -- Trello: https://trello.com/power-ups/admin (create Power-Up, get Key and Token from API Key tab) - -**Tests failing**: Ensure you're in project root when running tests (migrations need to be found) diff --git a/SESSION_STATE.md b/SESSION_STATE.md index 4481900..8de99b6 100644 --- a/SESSION_STATE.md +++ b/SESSION_STATE.md @@ -1,14 +1,11 @@ # Current Session State ## 🎯 Active Goal -Phase 1 stability and optimization complete. +Removed AI Agent middleware and snapshot endpoint to simplify the dashboard. ## ✅ Completed - Initial Phase 1 feature set (Trello, Todoist, Obsidian, PlanToEat) -- AI Snapshot endpoint implementation (`/api/claude/snapshot`) - Basic testing suite (9/9 passing) -- **Security Fix:** Timing attack vulnerability in Bearer token validation (ai_auth.go:33) -- **Security Fix:** JSON injection in error responses (ai_auth.go:47-50) - **Database Hardening:** Enabled WAL mode for better concurrency (sqlite.go:32-35) - **Database Hardening:** Set MaxOpenConns(1) to prevent "database is locked" errors (sqlite.go:38) - **Security Fix:** SQL injection vulnerability in GetNotes LIMIT clause (sqlite.go:215-221) @@ -16,17 +13,21 @@ Phase 1 stability and optimization complete. - **Security Fix:** Path traversal mitigation - skip symbolic links in Obsidian scanner (obsidian.go:54-57) - **Commit:** 325811c "Mitigate path traversal risk in Obsidian scanner" - **Performance Optimization:** Parallelized Trello card fetching with semaphore-limited concurrency (trello.go:197-220) +- **Commit:** 80c2332 "Parallelize Trello card fetching for improved performance" +- **Cleanup:** Removed AI Agent middleware and `/api/claude/snapshot` endpoint + - Deleted: internal/middleware/ai_auth.go, ai_auth_test.go + - Removed: AIAgentAPIKey from config.go + - All tests passing after removal ## 🏗️ Architecture & Decisions - **Decision:** Use SQLite for caching with a 5-minute TTL. - **Decision:** Trello is the primary task system, requiring Key+Token auth. -- **Decision:** Agent endpoint uses Bearer token auth for security. - **Decision:** Limit Trello concurrent requests to 5 to prevent API rate limiting. +- **Decision:** Removed AI agent endpoint - dashboard is human-facing only. ## 📋 Next Steps -1. **Code Quality:** Commit parallelization changes. -2. **Testing:** Add unit tests for security fixes (timing attack, SQL injection, path traversal). -3. **Future:** Consider Phase 2 features (write operations, user management). +1. **Testing:** Add unit tests for security fixes (SQL injection, path traversal). +2. **Future:** Consider Phase 2 features (write operations, user management). ## ⚠️ Known Blockers / Debt - **Test Coverage:** Security fixes lack dedicated unit tests. @@ -7,4 +7,4 @@ require ( github.com/mattn/go-sqlite3 v1.14.33 ) -require github.com/joho/godotenv v1.5.1 // indirect +require github.com/joho/godotenv v1.5.1 diff --git a/internal/config/config.go b/internal/config/config.go index 4a86b06..dc7d1c3 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -22,9 +22,6 @@ type Config struct { Port string CacheTTLMinutes int Debug bool - - // AI Agent Access - AIAgentAPIKey string } // Load reads configuration from environment variables @@ -44,9 +41,6 @@ func Load() (*Config, error) { Port: getEnvWithDefault("PORT", "8080"), CacheTTLMinutes: getEnvAsInt("CACHE_TTL_MINUTES", 5), Debug: getEnvAsBool("DEBUG", false), - - // AI Agent Access - AIAgentAPIKey: os.Getenv("AI_AGENT_API_KEY"), } // Validate required fields diff --git a/internal/middleware/ai_auth.go b/internal/middleware/ai_auth.go deleted file mode 100644 index 3c04a37..0000000 --- a/internal/middleware/ai_auth.go +++ /dev/null @@ -1,46 +0,0 @@ -package middleware - -import ( - "net/http" - "strings" -) - -// AIAuthMiddleware validates Bearer token for AI agent access -func AIAuthMiddleware(validToken string) func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Skip auth if no token configured - if validToken == "" { - respondError(w, http.StatusServiceUnavailable, "ai_disabled", "AI agent access not configured") - return - } - - authHeader := r.Header.Get("Authorization") - - if authHeader == "" { - respondError(w, http.StatusUnauthorized, "unauthorized", "Missing Authorization header") - return - } - - if !strings.HasPrefix(authHeader, "Bearer ") { - respondError(w, http.StatusUnauthorized, "unauthorized", "Invalid Authorization header format") - return - } - - token := strings.TrimPrefix(authHeader, "Bearer ") - if token != validToken { - respondError(w, http.StatusUnauthorized, "unauthorized", "Invalid or missing token") - return - } - - next.ServeHTTP(w, r) - }) - } -} - -// respondError sends a JSON error response -func respondError(w http.ResponseWriter, status int, error, message string) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(status) - w.Write([]byte(`{"error":"` + error + `","message":"` + message + `"}`)) -} |
