AGENTS.md

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:

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
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)

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.

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.

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() accepts natural language ("yesterday", "2 days ago", "March 5") 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).

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.

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.