@@ -1,158 +1,60 @@
-# AGENTS.md
-
-This file provides guidance to AI coding agents when working with code in this repository.
-
-## Build Commands
-
-All commands use `just` task runner:
-
-- **Development workflow**: `just` (runs fmt, lint, staticcheck, test, vuln, reuse)
-- **Format code**: `just fmt` (uses gofumpt)
-- **Lint**: `just lint` (uses golangci-lint)
-- **Static analysis**: `just staticcheck` (uses staticcheck)
-- **Test**: `just test` (runs all tests with verbose output)
-- **Single test**: `go test -v ./path/to/package -run TestName`
-- **Vulnerability check**: `just vuln` (uses govulncheck)
-- **License compliance**: `just reuse` (REUSE specification)
-- **Build**: `just build` (outputs to `./lunatask-mcp-server`, supports GOOS/GOARCH env vars)
-- **Run**: `just run` (builds and runs with version injection)
-- **Compress binary**: `just pack` (requires upx, run after build)
-- **Clean**: `just clean` (removes binary) or `just clean-all` (removes binary and config)
-
-Version is injected at build time via git describe, so always use `just build` or `just run` rather than plain `go build`.
-
-## Licensing
-
-This project is REUSE compliant. All files must have SPDX headers:
-
-- **Code files** (`.go`): `SPDX-License-Identifier: AGPL-3.0-or-later`
-- **Documentation** (`.md`, `.toml`): `SPDX-License-Identifier: CC0-1.0`
-- **Copyright**: `SPDX-FileCopyrightText: Amolith <amolith@secluded.site>`
-
-Always include both lines in new files.
-
-## Architecture Overview
-
-This is an MCP (Model Context Protocol) server that exposes Lunatask API functionality to LLMs. The codebase uses a clean three-layer architecture:
-
-### Layer 1: Main Entry Point (`cmd/lunatask-mcp-server.go`)
-
-- Loads TOML configuration from `config.toml` (or custom path via `-c/--config`)
-- Creates and configures the MCP SSE server (Server-Sent Events transport)
-- Implements Provider interfaces (`AreaProvider`, `GoalProvider`, `HabitProvider`) that wrap config structs
-- Registers all MCP tools with their handlers
-- Validates configuration on startup (areas/goals must have names and IDs, timezone must be valid)
-
-### Layer 2: Tools Package (`tools/`)
-
-- Bridges MCP tool calls to Lunatask API client
-- Handles MCP request/response formatting
-- Performs pre-validation and argument transformation (e.g., string priorities → integers)
-- Consumes Provider interfaces to access configuration without tight coupling
-- Contains handlers for: timestamp parsing, area/goal listing, task CRUD, habit tracking
-
-### Layer 3: Lunatask Package (`lunatask/`)
-
-- Low-level HTTP client for Lunatask REST API (`https://api.lunatask.app/v1`)
-- Defines request/response types with JSON tags
-- Uses `go-playground/validator` for request validation
-- Contains API methods for tasks (`CreateTask`, `UpdateTask`, `DeleteTask`) and habits (`TrackHabitActivity`)
+<!--
+SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
-### Key Design Patterns
+SPDX-License-Identifier: CC0-1.0
+-->
-**Provider Interfaces**: The tools package defines `AreaProvider`, `GoalProvider`, and `HabitProvider` interfaces. The main package's config structs implement these, allowing tools to access config data without importing main or knowing config structure details.
-
-**String-to-Integer Mapping**: MCP tools accept human-readable strings that get translated to API integers:
-- Priority: `"lowest"`=-2, `"low"`=-1, `"neutral"`=0, `"high"`=1, `"highest"`=2
-- Eisenhower matrix: `"uncategorised"`=0, `"both urgent and important"`=1, `"urgent, but not important"`=2, `"important, but not urgent"`=3, `"neither urgent nor important"`=4
-
-**Natural Language Date Parsing**: Uses `github.com/ijt/go-anytime` with timezone support. The `get_timestamp` tool parses expressions like "tomorrow at 3pm" into RFC3339 timestamps. Timezone is configured in `config.toml` using IANA/Olson format (e.g., `America/New_York`).
-
-## Configuration System
-
-On first run, the server generates `config.toml` with placeholder values and exits. Users must:
-
-1. Add their Lunatask API access token
-2. Configure areas with real IDs from Lunatask (areas can have nested goals)
-3. Configure habits with real IDs
-4. Set timezone in IANA format (e.g., `America/New_York`, not `EST`)
-5. Optionally customize server host/port (defaults to `localhost:8080`)
-
-The config uses TOML (not JSON) per project philosophy. Example structure:
-
-```toml
-access_token = "your-token"
-timezone = "America/New_York"
-
-[server]
-host = "localhost"
-port = 8080
+# AGENTS.md
-[[area]]
-name = "Work"
-id = "uuid-here"
+MCP server exposing Lunatask (task/habit tracker) to LLMs via SSE. Primary use case: Home Assistant voice assistant says "remind me to call mom tomorrow" → HA's LLM calls this server's `create_task` tool → task appears in Lunatask.
- [[area.goal]]
- name = "Q1 Project"
- id = "uuid-here"
+## Commands
-[[habit]]
-name = "Exercise"
-id = "uuid-here"
+```sh
+just # Run all checks (fmt, lint, staticcheck, test, vuln, reuse)
+just build # Build with version info
+just run # Build and run
```
-## Critical Implementation Details
+## Architecture
-### Goal Validation in Task Operations
-
-When creating or updating tasks with a `goal_id`, the tools package validates that the goal belongs to the specified area. This prevents associating tasks with goals from different areas, which Lunatask may reject or handle incorrectly.
-
-### Update vs Create Semantics
-
-`UpdateTask` reuses `CreateTaskRequest` type but has different semantics:
-- Only provided fields are updated (partial updates)
-- Empty strings for text fields (name, note, scheduled_on) clear existing values
-- Empty strings for enum fields (status, motivation) also clear values
-- The tools handler only sends fields present in MCP request arguments
-
-### Empty Package Files
-
-`lunatask/notes.go`, `lunatask/people.go`, and `lunatask/timeline.go` exist but are empty. These are placeholders for future Lunatask API features. Don't remove them without checking if they're imported elsewhere.
-
-### MCP Tool Workflow Dependencies
+```
+cmd/lunatask-mcp-server.go → Config, tool registration, SSE server
+tools/ → MCP tool handlers
+lunatask/ → HTTP client for Lunatask API
+```
-Several tools must be called in specific order:
-1. `list_areas_and_goals` → provides IDs for `create_task`/`update_task`
-2. `get_timestamp` → provides formatted timestamps for task scheduling and habit tracking
-3. `list_habits_and_activities` → provides IDs for `track_habit_activity`
+**Data flow**: SSE request → MCP server → tool handler → lunatask client → Lunatask API
-The MCP tool descriptions document these workflows, and the LLM system prompts (in README.md) reinforce the pattern.
+**Domain model**: Areas contain Goals. Habits are separate. Tasks belong to an Area and optionally a Goal.
-### Validation Happens at Two Levels
+## Adding New Functionality
-1. **Tools layer**: Type checking, area/goal relationship validation, string-to-int translation
-2. **Lunatask layer**: go-playground/validator tags enforce API constraints (UUID format, string lengths, numeric ranges, enum values)
+**New MCP tool**:
+1. Add handler method to `tools/` (on `Handlers` struct)
+2. Register tool with `mcpServer.AddTool()` in `cmd/lunatask-mcp-server.go`
-This prevents invalid requests from reaching the API and provides better error messages.
+**New API endpoint**: Add method to `lunatask/Client` with request/response types
-### Version Injection
+## Key Patterns
-The `version` variable in main is set via ldflags during build: `-ldflags "-X main.version=..."`. The justfile extracts version from `git describe --long`. Don't hardcode version strings; if version is empty, it displays a helpful message about using `just build`.
+- **Tool descriptions are prompts**: The verbose `mcp.WithDescription()` strings guide the *calling* LLM's behavior—they explain workflows, valid values, and when to use each tool
+- **Error returns**: Handlers return `(result, nil)` with `IsError: true`—application errors go in the result, not the Go error
+- **Enum translation**: Human strings (`"high"`) → API integers (`1`) in handlers before calling client
+- **Provider interfaces**: `tools/` defines interfaces (`AreaProvider`, etc.) that `cmd/` types implement, keeping packages decoupled
-## Testing Considerations
+## Gotchas
-- Tests should use `go test -v ./...` to run all packages
-- Mock the HTTP client in lunatask package tests (set `Client.HTTPClient` to custom `*http.Client`)
-- Mock Provider interfaces in tools package tests
-- Timezone-dependent tests should explicitly set timezone in config
-- Natural language date parsing is non-deterministic (relative to "now"), so test with absolute dates or mock time
+- Area/goal/habit IDs are static in config, not fetched—Lunatask API has no list endpoint
+- `CreateTask` returns `(nil, nil)` on HTTP 204 (task already exists)—not an error
+- `update_task` MCP tool requires `name` even for partial updates
+- No tests exist yet
+- All files need SPDX headers (`just reuse` checks this)
-## Related Documentation
+## Contributing
-The README.md contains extensive documentation about:
-- MCP tool descriptions and parameters (authoritative reference for tool behavior)
-- Example LLM system prompts showing real-world usage patterns
-- User-specific workflow rules (area workflows, estimate requirements, status defaults)
-- Contribution workflow using pr.pico.sh (SSH-based patches, no GitHub account needed)
+Patches via [pr.pico.sh](https://pr.pico.sh/r/amolith/llm-projects):
-When modifying tools, ensure MCP descriptions in `cmd/lunatask-mcp-server.go` stay synchronized with actual behavior.
+```sh
+git format-patch origin/main --stdout | ssh pr.pico.sh pr create amolith/llm-projects
+```