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.