AGENTS.md

  1# AGENTS.md
  2
  3This file provides guidance to AI coding agents when working with code in this repository.
  4
  5## Build Commands
  6
  7All commands use `just` task runner:
  8
  9- **Development workflow**: `just` (runs fmt, lint, staticcheck, test, vuln, reuse)
 10- **Format code**: `just fmt` (uses gofumpt)
 11- **Lint**: `just lint` (uses golangci-lint)
 12- **Static analysis**: `just staticcheck` (uses staticcheck)
 13- **Test**: `just test` (runs all tests with verbose output)
 14- **Single test**: `go test -v ./path/to/package -run TestName`
 15- **Vulnerability check**: `just vuln` (uses govulncheck)
 16- **License compliance**: `just reuse` (REUSE specification)
 17- **Build**: `just build` (outputs to `./lunatask-mcp-server`, supports GOOS/GOARCH env vars)
 18- **Run**: `just run` (builds and runs with version injection)
 19- **Compress binary**: `just pack` (requires upx, run after build)
 20- **Clean**: `just clean` (removes binary) or `just clean-all` (removes binary and config)
 21
 22Version is injected at build time via git describe, so always use `just build` or `just run` rather than plain `go build`.
 23
 24## Licensing
 25
 26This project is REUSE compliant. All files must have SPDX headers:
 27
 28- **Code files** (`.go`): `SPDX-License-Identifier: AGPL-3.0-or-later`
 29- **Documentation** (`.md`, `.toml`): `SPDX-License-Identifier: CC0-1.0`
 30- **Copyright**: `SPDX-FileCopyrightText: Amolith <amolith@secluded.site>`
 31
 32Always include both lines in new files.
 33
 34## Architecture Overview
 35
 36This is an MCP (Model Context Protocol) server that exposes Lunatask API functionality to LLMs. The codebase uses a clean three-layer architecture:
 37
 38### Layer 1: Main Entry Point (`cmd/lunatask-mcp-server.go`)
 39
 40- Loads TOML configuration from `config.toml` (or custom path via `-c/--config`)
 41- Creates and configures the MCP SSE server (Server-Sent Events transport)
 42- Implements Provider interfaces (`AreaProvider`, `GoalProvider`, `HabitProvider`) that wrap config structs
 43- Registers all MCP tools with their handlers
 44- Validates configuration on startup (areas/goals must have names and IDs, timezone must be valid)
 45
 46### Layer 2: Tools Package (`tools/`)
 47
 48- Bridges MCP tool calls to Lunatask API client
 49- Handles MCP request/response formatting
 50- Performs pre-validation and argument transformation (e.g., string priorities → integers)
 51- Consumes Provider interfaces to access configuration without tight coupling
 52- Contains handlers for: timestamp parsing, area/goal listing, task CRUD, habit tracking
 53
 54### Layer 3: Lunatask Package (`lunatask/`)
 55
 56- Low-level HTTP client for Lunatask REST API (`https://api.lunatask.app/v1`)
 57- Defines request/response types with JSON tags
 58- Uses `go-playground/validator` for request validation
 59- Contains API methods for tasks (`CreateTask`, `UpdateTask`, `DeleteTask`) and habits (`TrackHabitActivity`)
 60
 61### Key Design Patterns
 62
 63**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.
 64
 65**String-to-Integer Mapping**: MCP tools accept human-readable strings that get translated to API integers:
 66- Priority: `"lowest"`=-2, `"low"`=-1, `"neutral"`=0, `"high"`=1, `"highest"`=2
 67- Eisenhower matrix: `"uncategorised"`=0, `"both urgent and important"`=1, `"urgent, but not important"`=2, `"important, but not urgent"`=3, `"neither urgent nor important"`=4
 68
 69**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`).
 70
 71## Configuration System
 72
 73On first run, the server generates `config.toml` with placeholder values and exits. Users must:
 74
 751. Add their Lunatask API access token
 762. Configure areas with real IDs from Lunatask (areas can have nested goals)
 773. Configure habits with real IDs
 784. Set timezone in IANA format (e.g., `America/New_York`, not `EST`)
 795. Optionally customize server host/port (defaults to `localhost:8080`)
 80
 81The config uses TOML (not JSON) per project philosophy. Example structure:
 82
 83```toml
 84access_token = "your-token"
 85timezone = "America/New_York"
 86
 87[server]
 88host = "localhost"
 89port = 8080
 90
 91[[area]]
 92name = "Work"
 93id = "uuid-here"
 94
 95  [[area.goal]]
 96  name = "Q1 Project"
 97  id = "uuid-here"
 98
 99[[habit]]
100name = "Exercise"
101id = "uuid-here"
102```
103
104## Critical Implementation Details
105
106### Goal Validation in Task Operations
107
108When 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.
109
110### Update vs Create Semantics
111
112`UpdateTask` reuses `CreateTaskRequest` type but has different semantics:
113- Only provided fields are updated (partial updates)
114- Empty strings for text fields (name, note, scheduled_on) clear existing values
115- Empty strings for enum fields (status, motivation) also clear values
116- The tools handler only sends fields present in MCP request arguments
117
118### Empty Package Files
119
120`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.
121
122### MCP Tool Workflow Dependencies
123
124Several tools must be called in specific order:
1251. `list_areas_and_goals` → provides IDs for `create_task`/`update_task`
1262. `get_timestamp` → provides formatted timestamps for task scheduling and habit tracking
1273. `list_habits_and_activities` → provides IDs for `track_habit_activity`
128
129The MCP tool descriptions document these workflows, and the LLM system prompts (in README.md) reinforce the pattern.
130
131### Validation Happens at Two Levels
132
1331. **Tools layer**: Type checking, area/goal relationship validation, string-to-int translation
1342. **Lunatask layer**: go-playground/validator tags enforce API constraints (UUID format, string lengths, numeric ranges, enum values)
135
136This prevents invalid requests from reaching the API and provides better error messages.
137
138### Version Injection
139
140The `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`.
141
142## Testing Considerations
143
144- Tests should use `go test -v ./...` to run all packages
145- Mock the HTTP client in lunatask package tests (set `Client.HTTPClient` to custom `*http.Client`)
146- Mock Provider interfaces in tools package tests
147- Timezone-dependent tests should explicitly set timezone in config
148- Natural language date parsing is non-deterministic (relative to "now"), so test with absolute dates or mock time
149
150## Related Documentation
151
152The README.md contains extensive documentation about:
153- MCP tool descriptions and parameters (authoritative reference for tool behavior)
154- Example LLM system prompts showing real-world usage patterns
155- User-specific workflow rules (area workflows, estimate requirements, status defaults)
156- Contribution workflow using pr.pico.sh (SSH-based patches, no GitHub account needed)
157
158When modifying tools, ensure MCP descriptions in `cmd/lunatask-mcp-server.go` stay synchronized with actual behavior.