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
  mcp/               → MCP server command (stdio/sse/http transports)
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)
  mcp/
    shared/          → Provider types (AreaProvider, HabitProvider) and error helpers
    tools/           → MCP tool handlers (crud/, habit/, timeline/, timestamp/)
    resources/       → MCP resource handlers (areas/, habits/, notebooks/, task/, note/, person/)

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.

MCP server patterns

The lune mcp command starts a Model Context Protocol server for LLM tool integration. Architecture:

  • Resources (internal/mcp/resources/): Read-only data from config (areas, habits, notebooks). Each has a Handler with HandleRead method.
  • Tools (internal/mcp/tools/): Actions that call the Lunatask API. Each tool uses *Input/*Output structs with jsonschema:"required" tags for MCP schema generation.
  • Shared (internal/mcp/shared/): Provider types decouple config from MCP handlers; ErrorResult() returns structured tool errors.

Consolidated tools (7 total):

  • CRUD tools (crud/): create, update, delete, query — each accepts an entity field to specify the type (task, note, person, journal, area, etc.)
  • Action tools: track_habit (habit/), add_timeline_note (timeline/), get_timestamp (timestamp/)

Tool handler pattern: Tools use mcp.AddTool() with typed input structs. Validation happens in parse*Input() functions returning *mcp.CallToolResult for errors (not Go errors). Parse functions call lunatask.Parse*() for enums and dateutil.Parse() for dates.

Config-driven tools: cfg.MCP.Tools.* booleans enable/disable individual tools. All default to enabled via cfg.MCP.MCPDefaults() (except query, which is a fallback for agents without MCP resource support).

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.
  • github.com/modelcontextprotocol/go-sdk/mcp (context7: /modelcontextprotocol/go-sdk): MCP server SDK for LLM tool integration.

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.