<!--
SPDX-FileCopyrightText: Amolith <amolith@secluded.site>

SPDX-License-Identifier: CC0-1.0
-->

# AGENTS.md

This file guides LLM agents through interacting with and maintaining
the lune project—a CLI for the Lunatask productivity app.

## Lunatask concepts

Lunatask is end-to-end encrypted and any sensitive information
(person/task/note/habit names, contents, etc.) is all omitted. The API
only returns basic metadata (IDs, timestamps, status, etc.). That also
means there is no way to dynamically list the user's areas, habits, or
notebooks; they must copy those details from the desktop app into lune
during interactive initialisation or reconfiguration.

## Commands

Run these most to least often:

```bash
task fmt lint:fix staticcheck test  # Important ones to run often
task                                # Include vulnerability and copyright checks
```

The Taskfile auto-installs tools (gofumpt, staticcheck, govulncheck) when
running their respective tasks.

## Architecture

```
main.go              → cmd.Execute() entrypoint
cmd/
  root.go            → Root command, registers all subcommands and groups
  init/              → Interactive setup wizard (huh forms, multi-step navigation)
  ping.go            → Token verification command
  add.go, done.go, jrnl.go  → Shortcuts (delegate to resource commands)
  task/              → task add/list/show/update/delete
  note/              → note add/list/show/update/delete
  person/            → person add/list/show/update/delete/timeline
  area/              → area list/show (read-only)
  goal/              → goal list/show (read-only)
  journal/           → journal add
  habit/             → habit track
  mcp/               → MCP server command (stdio/sse/http transports)
internal/
  client/            → Lunatask API client factory (uses system keyring)
  config/            → TOML config at ~/.config/lune/config.toml
  completion/        → Shell completion helpers (config-based + static values)
  dateutil/          → Natural language date parsing via go-dateparser
  stats/             → Usage statistics helpers
  ui/                → Lipgloss styles (Success, Warning, Error, H1, H2, FormatDate)
  validate/          → Input validation (delegates to go-lunatask Parse* functions)
  mcp/
    shared/          → Provider types (AreaProvider, HabitProvider) and error helpers
    tools/           → MCP tool handlers (crud/, habit/, timeline/, timestamp/)
    resources/       → MCP resource handlers (areas/, habits/, notebooks/, task/, note/, person/)
```

**Command flow**: `fang.Execute()` wraps Cobra with version/commit info and
signal handling. Resource commands live in subpackages (`cmd/task/`); shortcuts
in `cmd/` delegate to resource commands by calling their `RunE` directly and
copying their flagset.

**API client pattern**: `client.New()` returns a `*lunatask.Client`. Create
operations use builders: `client.NewTask(name).InArea(id).WithStatus(s).Create(ctx)`.
Update operations use separate builders: `client.UpdateTask(id).WithStatus(s).Update(ctx)`.

## Command patterns

**Resource commands** (task, note, person, etc.):

- Parent command in `<resource>.go` with `GroupID: "resources"`
- Subcommands (add, list, get, update, delete) in separate files
- Export the `*Cmd` vars for shortcuts to reference

**Shortcuts** (add, done, jrnl):

- Live in `cmd/` directly with `GroupID: "shortcuts"`
- Copy flags from resource command:
  `addCmd.Flags().AddFlagSet(task.AddCmd.Flags())`
- Call resource command's `RunE` directly

**Shell completions** registered inline via `RegisterFlagCompletionFunc`;
see `cmd/task/add.go:56-63` for examples. Use `completion.Areas`, `completion.Goals`,
etc. for config-based completions, or `completion.Static("val1", "val2")` for
fixed options.

## MCP server patterns

The `lune mcp` command starts a Model Context Protocol server for LLM tool
integration. Architecture:

- **Resources** (`internal/mcp/resources/`): Read-only data from config (areas,
  habits, notebooks). Each has a `Handler` with `HandleRead` method.
- **Tools** (`internal/mcp/tools/`): Actions that call the Lunatask API. Each
  tool uses `*Input`/`*Output` structs with `jsonschema:"required"` tags for
  MCP schema generation.
- **Shared** (`internal/mcp/shared/`): Provider types decouple config from MCP
  handlers; `ErrorResult()` returns structured tool errors.

**Consolidated tools** (7 total):

- **CRUD tools** (`crud/`): `create`, `update`, `delete`, `query` — each accepts
  an `entity` field to specify the type (task, note, person, journal, area, etc.)
- **Action tools**: `track_habit` (habit/), `add_timeline_note` (timeline/),
  `get_timestamp` (timestamp/)

**Tool handler pattern**: Tools use `mcp.AddTool()` with typed input structs.
Validation happens in `parse*Input()` functions returning `*mcp.CallToolResult`
for errors (not Go errors). Parse functions call `lunatask.Parse*()` for enums
and `dateutil.Parse()` for dates.

**Config-driven tools**: `cfg.MCP.Tools.*` booleans enable/disable individual
tools. All default to enabled via `cfg.MCP.MCPDefaults()` (except `query`, which
is a fallback for agents without MCP resource support).

## Core dependencies

Reference docs via context7 or doc-agent when unsure:

- `git.secluded.site/go-lunatask` (context7:
  `/websites/pkg_go_dev_git_secluded_site_go-lunatask`): Lunatask API client.
  Amolith maintains it, so issues can be resolved or missing features added
  quickly.
- `github.com/charmbracelet/huh` (context7: `/charmbracelet/huh`):
  powerful and attractive libs for interactive forms and prompts
- `github.com/charmbracelet/lipgloss` (context7: `/charmbracelet/lipgloss`):
  Terminal styling.
- `github.com/charmbracelet/fang` (context7: `/charmbracelet/fang`): Cobra
  wrapper with fancy styling, version injection, signal handling.
- `github.com/zalando/go-keyring` (context7: `/zalando/go-keyring`):
  Cross-platform interface to the system keyring
- `github.com/BurntSushi/toml` (context7: `/burntsushi/toml`): Config file
  parsing.
- `github.com/spf13/cobra` (context7:
  `/websites/pkg_go_dev_github_com_spf13_cobra`): CLI framework.
- `github.com/markusmobius/go-dateparser` (context7:
  `/markusmobius/go-dateparser`): Natural language date parsing.
- `github.com/modelcontextprotocol/go-sdk/mcp` (context7:
  `/modelcontextprotocol/go-sdk`): MCP server SDK for LLM tool integration.

## Gotchas

### Enum validation

Use `validate.TaskStatus()`, `validate.Motivation()`, `validate.RelationshipStrength()`
to parse and validate string inputs to `lunatask.*` types. These normalize
case and return typed values or wrapped errors (`ErrInvalidStatus`, etc.).

### Date parsing

`dateutil.Parse()` uses PHP strtotime syntax ("yesterday", "-2 days", "next Monday")
and ISO dates ("2024-01-15"). Empty input returns today's date. Returns
`lunatask.Date` type.

### Stdin convention

Many commands accept `-` as an argument or flag value to read from stdin:
- `task add -` reads task name from first stdin line
- `--note -` reads entire stdin as the note content

The implementations differ: name uses `bufio.Scanner` (single line), note uses
`os.ReadFile("/dev/stdin")` (all content).

### Deep link support

Commands accepting IDs also accept `lunatask://` deep links. Use
`validate.Reference()` to normalize either format to a UUID. Deep link
parsing and building use `lunatask.ParseDeepLink()` and `lunatask.BuildDeepLink()`
from go-lunatask.

### Linter exclusions for cmd/

The `.golangci.yaml` disables several linters for `cmd/`:

- `gochecknoglobals`, `gochecknoinits`: Cobra requires package-level vars and
  `init()`
- `errcheck`, `godox`: Stub implementations deliberately print without checking,
  TODOs are placeholders
- `wrapcheck`: CLI returns errors for display; wrapping adds noise
- `dupl`: Builder types (`TaskBuilder`, `TaskUpdateBuilder`) share method
  signatures but differ in type; duplication is structural

These patterns are intentional.

### Config key lookups

Areas, goals, notebooks, and habits are stored with user-defined `key` fields
(short aliases like "work", "personal"). Commands accept keys, not UUIDs.
Lookup methods: `cfg.AreaByKey()`, `area.GoalByKey()`, `cfg.NotebookByKey()`,
`cfg.HabitByKey()`. There are also `*ByID()` methods for reverse lookups.

Goals require their parent area context—use `cfg.FindGoalsByKey()` to find goals
across all areas, which returns `[]GoalMatch{Goal, Area}` pairs.

### Access token

Read from system keyring via `internal/client.New()`. No interactive
prompts—fail fast with `client.ErrNoToken` if not configured.

### Output formatting

Use `cmd.OutOrStdout()` and `cmd.ErrOrStderr()` for testability. Styles are in
`internal/ui/styles.go`—use `ui.Success`, `ui.Error`, etc. rather than inline
colors.

**Accessibility**: Avoid dim text (ANSI color 8)—it's unreadable in many terminal
color schemes. Use italic or other formatting for de-emphasized text instead.

## Testing

Table-driven tests with `t.Parallel()`. Use `_test` package suffix for black-box
testing (see `cmd/init/ui_test.go`). When testing commands, use `cmd.SetOut()` /
`cmd.SetErr()` to capture output.

### Init wizard

The `cmd/init/` package implements a multi-step wizard using `huh` forms.
Navigation uses `wizardNav` (navNext/navBack/navQuit) with sentinel errors
(`errQuit`, `errReset`). Steps are functions returning navigation direction;
the wizard loops through them allowing back/forward navigation.
