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 (task/, habit/, timestamp/)
 61    resources/       → MCP resource handlers (areas/, habits/, notebooks/)
 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**Tool handler pattern**: Tools use `mcp.AddTool()` with typed input structs.
107Validation happens in `parse*Input()` functions returning `*mcp.CallToolResult`
108for errors (not Go errors). Parse functions call `lunatask.Parse*()` for enums
109and `dateutil.Parse()` for dates.
110
111**Config-driven tools**: `cfg.MCP.Tools.*` booleans enable/disable individual
112tools. All default to enabled via `cfg.MCP.MCPDefaults()`.
113
114## Core dependencies
115
116Reference docs via context7 or doc-agent when unsure:
117
118- `git.secluded.site/go-lunatask` (context7:
119  `/websites/pkg_go_dev_git_secluded_site_go-lunatask`): Lunatask API client.
120  Amolith maintains it, so issues can be resolved or missing features added
121  quickly.
122- `github.com/charmbracelet/huh` (context7: `/charmbracelet/huh`):
123  powerful and attractive libs for interactive forms and prompts
124- `github.com/charmbracelet/lipgloss` (context7: `/charmbracelet/lipgloss`):
125  Terminal styling.
126- `github.com/charmbracelet/fang` (context7: `/charmbracelet/fang`): Cobra
127  wrapper with fancy styling, version injection, signal handling.
128- `github.com/zalando/go-keyring` (context7: `/zalando/go-keyring`):
129  Cross-platform interface to the system keyring
130- `github.com/BurntSushi/toml` (context7: `/burntsushi/toml`): Config file
131  parsing.
132- `github.com/spf13/cobra` (context7:
133  `/websites/pkg_go_dev_github_com_spf13_cobra`): CLI framework.
134- `github.com/markusmobius/go-dateparser` (context7:
135  `/markusmobius/go-dateparser`): Natural language date parsing.
136- `github.com/modelcontextprotocol/go-sdk/mcp` (context7:
137  `/modelcontextprotocol/go-sdk`): MCP server SDK for LLM tool integration.
138
139## Gotchas
140
141### Enum validation
142
143Use `validate.TaskStatus()`, `validate.Motivation()`, `validate.RelationshipStrength()`
144to parse and validate string inputs to `lunatask.*` types. These normalize
145case and return typed values or wrapped errors (`ErrInvalidStatus`, etc.).
146
147### Date parsing
148
149`dateutil.Parse()` accepts natural language ("yesterday", "2 days ago", "March 5")
150and ISO dates ("2024-01-15"). Empty input returns today's date. Returns
151`lunatask.Date` type.
152
153### Stdin convention
154
155Many commands accept `-` as an argument or flag value to read from stdin:
156- `task add -` reads task name from first stdin line
157- `--note -` reads entire stdin as the note content
158
159The implementations differ: name uses `bufio.Scanner` (single line), note uses
160`os.ReadFile("/dev/stdin")` (all content).
161
162### Deep link support
163
164Commands accepting IDs also accept `lunatask://` deep links. Use
165`validate.Reference()` to normalize either format to a UUID. Deep link
166parsing and building use `lunatask.ParseDeepLink()` and `lunatask.BuildDeepLink()`
167from go-lunatask.
168
169### Linter exclusions for cmd/
170
171The `.golangci.yaml` disables several linters for `cmd/`:
172
173- `gochecknoglobals`, `gochecknoinits`: Cobra requires package-level vars and
174  `init()`
175- `errcheck`, `godox`: Stub implementations deliberately print without checking,
176  TODOs are placeholders
177- `wrapcheck`: CLI returns errors for display; wrapping adds noise
178- `dupl`: Builder types (`TaskBuilder`, `TaskUpdateBuilder`) share method
179  signatures but differ in type; duplication is structural
180
181These patterns are intentional.
182
183### Config key lookups
184
185Areas, goals, notebooks, and habits are stored with user-defined `key` fields
186(short aliases like "work", "personal"). Commands accept keys, not UUIDs.
187Lookup methods: `cfg.AreaByKey()`, `area.GoalByKey()`, `cfg.NotebookByKey()`,
188`cfg.HabitByKey()`. There are also `*ByID()` methods for reverse lookups.
189
190Goals require their parent area context—use `cfg.FindGoalsByKey()` to find goals
191across all areas, which returns `[]GoalMatch{Goal, Area}` pairs.
192
193### Access token
194
195Read from system keyring via `internal/client.New()`. No interactive
196prompts—fail fast with `client.ErrNoToken` if not configured.
197
198### Output formatting
199
200Use `cmd.OutOrStdout()` and `cmd.ErrOrStderr()` for testability. Styles are in
201`internal/ui/styles.go`—use `ui.Success`, `ui.Error`, etc. rather than inline
202colors.
203
204## Testing
205
206Table-driven tests with `t.Parallel()`. Use `_test` package suffix for black-box
207testing (see `cmd/init/ui_test.go`). When testing commands, use `cmd.SetOut()` /
208`cmd.SetErr()` to capture output.
209
210### Init wizard
211
212The `cmd/init/` package implements a multi-step wizard using `huh` forms.
213Navigation uses `wizardNav` (navNext/navBack/navQuit) with sentinel errors
214(`errQuit`, `errReset`). Steps are functions returning navigation direction;
215the wizard loops through them allowing back/forward navigation.