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.