From eb73805850399cb96451d43a9a67a8c5e9c1464f Mon Sep 17 00:00:00 2001 From: Amolith Date: Sat, 4 Oct 2025 14:28:53 -0600 Subject: [PATCH] feat(docs): add llm-oriented intro --- AGENTS.md | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000000000000000000000000000000000..5cba66e79c3df483a3485122ac0a6449f34c2e58 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,158 @@ +# 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 ` + +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`) + +### Key Design Patterns + +**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 + +[[area]] +name = "Work" +id = "uuid-here" + + [[area.goal]] + name = "Q1 Project" + id = "uuid-here" + +[[habit]] +name = "Exercise" +id = "uuid-here" +``` + +## Critical Implementation Details + +### 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 + +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` + +The MCP tool descriptions document these workflows, and the LLM system prompts (in README.md) reinforce the pattern. + +### Validation Happens at Two Levels + +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) + +This prevents invalid requests from reaching the API and provides better error messages. + +### Version Injection + +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`. + +## Testing Considerations + +- 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 + +## Related Documentation + +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) + +When modifying tools, ensure MCP descriptions in `cmd/lunatask-mcp-server.go` stay synchronized with actual behavior.