AGENTS.md

  1<!--
  2SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
  3
  4SPDX-License-Identifier: CC0-1.0
  5-->
  6
  7# AGENTS.md
  8
  9This file guides LLM agents through interacting with and maintaining
 10the lune project—a CLI for the Lunatask productivity app.
 11
 12## Lunatask concepts
 13
 14Lunatask is end-to-end encrypted and any sensitive information
 15(person/task/note/habit names, contents, etc.) is all omitted. The API
 16only returns basic metadata (IDs, timestamps, status, etc.). That also
 17means there is no way to dynamically list the user's areas, habits, or
 18notebooks; they must copy those details from the desktop app into lune
 19during interactive initialisation or reconfiguration.
 20
 21## Commands
 22
 23Run these most to least often:
 24
 25```bash
 26task fmt lint:fix staticcheck test  # Important ones to run often
 27task                                # Include vulnerability and copyright checks
 28```
 29
 30The Taskfile auto-installs tools (gofumpt, staticcheck, govulncheck) when
 31running their respective tasks.
 32
 33## Architecture
 34
 35```
 36main.go              → cmd.Execute() entrypoint
 37cmd/
 38  root.go            → Root command, registers all subcommands and groups
 39  init/              → Interactive setup wizard (huh forms, multi-step navigation)
 40  ping.go            → Token verification command
 41  add.go, done.go, jrnl.go  → Shortcuts (delegate to resource commands)
 42  task/              → task add/list/show/update/delete
 43  note/              → note add/list/show/update/delete
 44  person/            → person add/list/show/update/delete/timeline
 45  area/              → area list/show (read-only)
 46  goal/              → goal list/show (read-only)
 47  journal/           → journal add
 48  habit/             → habit track
 49  mcp/               → MCP server command (stdio/sse/http transports)
 50internal/
 51  client/            → Lunatask API client factory (uses system keyring)
 52  config/            → TOML config at ~/.config/lune/config.toml
 53  completion/        → Shell completion helpers (config-based + static values)
 54  dateutil/          → Natural language date parsing via go-dateparser
 55  stats/             → Usage statistics helpers
 56  ui/                → Lipgloss styles (Success, Warning, Error, H1, H2, FormatDate)
 57  validate/          → Input validation (delegates to go-lunatask Parse* functions)
 58  mcp/
 59    shared/          → Provider types (AreaProvider, HabitProvider) and error helpers
 60    tools/           → MCP tool handlers (crud/, habit/, timeline/, timestamp/)
 61    resources/       → MCP resource handlers (areas/, habits/, notebooks/, task/, note/, person/)
 62```
 63
 64**Command flow**: `fang.Execute()` wraps Cobra with version/commit info and
 65signal handling. Resource commands live in subpackages (`cmd/task/`); shortcuts
 66in `cmd/` delegate to resource commands by calling their `RunE` directly and
 67copying their flagset.
 68
 69**API client pattern**: `client.New()` returns a `*lunatask.Client`. Create
 70operations use builders: `client.NewTask(name).InArea(id).WithStatus(s).Create(ctx)`.
 71Update operations use separate builders: `client.UpdateTask(id).WithStatus(s).Update(ctx)`.
 72
 73## Command patterns
 74
 75**Resource commands** (task, note, person, etc.):
 76
 77- Parent command in `<resource>.go` with `GroupID: "resources"`
 78- Subcommands (add, list, get, update, delete) in separate files
 79- Export the `*Cmd` vars for shortcuts to reference
 80
 81**Shortcuts** (add, done, jrnl):
 82
 83- Live in `cmd/` directly with `GroupID: "shortcuts"`
 84- Copy flags from resource command:
 85  `addCmd.Flags().AddFlagSet(task.AddCmd.Flags())`
 86- Call resource command's `RunE` directly
 87
 88**Shell completions** registered inline via `RegisterFlagCompletionFunc`;
 89see `cmd/task/add.go:56-63` for examples. Use `completion.Areas`, `completion.Goals`,
 90etc. for config-based completions, or `completion.Static("val1", "val2")` for
 91fixed options.
 92
 93## MCP server patterns
 94
 95The `lune mcp` command starts a Model Context Protocol server for LLM tool
 96integration. Architecture:
 97
 98- **Resources** (`internal/mcp/resources/`): Read-only data from config (areas,
 99  habits, notebooks). Each has a `Handler` with `HandleRead` method.
100- **Tools** (`internal/mcp/tools/`): Actions that call the Lunatask API. Each
101  tool uses `*Input`/`*Output` structs with `jsonschema:"required"` tags for
102  MCP schema generation.
103- **Shared** (`internal/mcp/shared/`): Provider types decouple config from MCP
104  handlers; `ErrorResult()` returns structured tool errors.
105
106**Consolidated tools** (7 total):
107
108- **CRUD tools** (`crud/`): `create`, `update`, `delete`, `query` — each accepts
109  an `entity` field to specify the type (task, note, person, journal, area, etc.)
110- **Action tools**: `track_habit` (habit/), `add_timeline_note` (timeline/),
111  `get_timestamp` (timestamp/)
112
113**Tool handler pattern**: Tools use `mcp.AddTool()` with typed input structs.
114Validation happens in `parse*Input()` functions returning `*mcp.CallToolResult`
115for errors (not Go errors). Parse functions call `lunatask.Parse*()` for enums
116and `dateutil.Parse()` for dates.
117
118**Config-driven tools**: `cfg.MCP.Tools.*` booleans enable/disable individual
119tools. All default to enabled via `cfg.MCP.MCPDefaults()` (except `query`, which
120is a fallback for agents without MCP resource support).
121
122## Core dependencies
123
124Reference docs via context7 or doc-agent when unsure:
125
126- `git.secluded.site/go-lunatask` (context7:
127  `/websites/pkg_go_dev_git_secluded_site_go-lunatask`): Lunatask API client.
128  Amolith maintains it, so issues can be resolved or missing features added
129  quickly.
130- `github.com/charmbracelet/huh` (context7: `/charmbracelet/huh`):
131  powerful and attractive libs for interactive forms and prompts
132- `github.com/charmbracelet/lipgloss` (context7: `/charmbracelet/lipgloss`):
133  Terminal styling.
134- `github.com/charmbracelet/fang` (context7: `/charmbracelet/fang`): Cobra
135  wrapper with fancy styling, version injection, signal handling.
136- `github.com/zalando/go-keyring` (context7: `/zalando/go-keyring`):
137  Cross-platform interface to the system keyring
138- `github.com/BurntSushi/toml` (context7: `/burntsushi/toml`): Config file
139  parsing.
140- `github.com/spf13/cobra` (context7:
141  `/websites/pkg_go_dev_github_com_spf13_cobra`): CLI framework.
142- `github.com/markusmobius/go-dateparser` (context7:
143  `/markusmobius/go-dateparser`): Natural language date parsing.
144- `github.com/modelcontextprotocol/go-sdk/mcp` (context7:
145  `/modelcontextprotocol/go-sdk`): MCP server SDK for LLM tool integration.
146
147## Gotchas
148
149### Enum validation
150
151Use `validate.TaskStatus()`, `validate.Motivation()`, `validate.RelationshipStrength()`
152to parse and validate string inputs to `lunatask.*` types. These normalize
153case and return typed values or wrapped errors (`ErrInvalidStatus`, etc.).
154
155### Date parsing
156
157`dateutil.Parse()` uses PHP strtotime syntax ("yesterday", "-2 days", "next Monday")
158and ISO dates ("2024-01-15"). Empty input returns today's date. Returns
159`lunatask.Date` type.
160
161### Stdin convention
162
163Many commands accept `-` as an argument or flag value to read from stdin:
164- `task add -` reads task name from first stdin line
165- `--note -` reads entire stdin as the note content
166
167The implementations differ: name uses `bufio.Scanner` (single line), note uses
168`os.ReadFile("/dev/stdin")` (all content).
169
170### Deep link support
171
172Commands accepting IDs also accept `lunatask://` deep links. Use
173`validate.Reference()` to normalize either format to a UUID. Deep link
174parsing and building use `lunatask.ParseDeepLink()` and `lunatask.BuildDeepLink()`
175from go-lunatask.
176
177### Linter exclusions for cmd/
178
179The `.golangci.yaml` disables several linters for `cmd/`:
180
181- `gochecknoglobals`, `gochecknoinits`: Cobra requires package-level vars and
182  `init()`
183- `errcheck`, `godox`: Stub implementations deliberately print without checking,
184  TODOs are placeholders
185- `wrapcheck`: CLI returns errors for display; wrapping adds noise
186- `dupl`: Builder types (`TaskBuilder`, `TaskUpdateBuilder`) share method
187  signatures but differ in type; duplication is structural
188
189These patterns are intentional.
190
191### Config key lookups
192
193Areas, goals, notebooks, and habits are stored with user-defined `key` fields
194(short aliases like "work", "personal"). Commands accept keys, not UUIDs.
195Lookup methods: `cfg.AreaByKey()`, `area.GoalByKey()`, `cfg.NotebookByKey()`,
196`cfg.HabitByKey()`. There are also `*ByID()` methods for reverse lookups.
197
198Goals require their parent area context—use `cfg.FindGoalsByKey()` to find goals
199across all areas, which returns `[]GoalMatch{Goal, Area}` pairs.
200
201### Access token
202
203Read from system keyring via `internal/client.New()`. No interactive
204prompts—fail fast with `client.ErrNoToken` if not configured.
205
206### Output formatting
207
208Use `cmd.OutOrStdout()` and `cmd.ErrOrStderr()` for testability. Styles are in
209`internal/ui/styles.go`—use `ui.Success`, `ui.Error`, etc. rather than inline
210colors.
211
212**Accessibility**: Avoid dim text (ANSI color 8)—it's unreadable in many terminal
213color schemes. Use italic or other formatting for de-emphasized text instead.
214
215## Testing
216
217Table-driven tests with `t.Parallel()`. Use `_test` package suffix for black-box
218testing (see `cmd/init/ui_test.go`). When testing commands, use `cmd.SetOut()` /
219`cmd.SetErr()` to capture output.
220
221### Init wizard
222
223The `cmd/init/` package implements a multi-step wizard using `huh` forms.
224Navigation uses `wizardNav` (navNext/navBack/navQuit) with sentinel errors
225(`errQuit`, `errReset`). Steps are functions returning navigation direction;
226the wizard loops through them allowing back/forward navigation.