diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b56ff9e..3f9dfbd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -29,7 +29,7 @@ make check # fmt, vet, lint, test |---------|------|---------| | `cmd/todo` | CLI command tree (`add`, `init`, `list`, `pickup`, `release`, `remove`, `detail`, `complete`, `reopen`, `bump`, `clear`, `doctor`), flag parsing, all terminal I/O | everything below | | `internal/todo` | domain logic: task model, Markdown parse/serialize, ID allocation, filtering, note disposition | `internal/fs`, `internal/git`, `park/schema` | -| `internal/registry` | machine-local cache of tracked repo folders (`~/.config/.todocache`) | stdlib only | +| `internal/registry` | machine-local cache of tracked repo folders (`$XDG_CACHE_HOME/todo/registry.json`, falling back to `~/.cache/todo/registry.json`) | stdlib only | | `internal/git` | git CLI helpers: repo root, remote URL, remote parsing | stdlib only | | `internal/fs` | filesystem helpers (exists check) | stdlib only | @@ -74,6 +74,6 @@ Control flow: per-repo commands resolve the repo root through `requireRepoConfig ## Implementation notes - The only external dependencies are `urfave/cli/v3` (plus `urfave/cli-validation`) and the public `github.com/polymorcodeus/park/schema` package, which supplies the record frontmatter contract and render helpers. Keep `internal/*` free of new dependencies where stdlib will do. -- The registry path defaults to `~/.config/.todocache` and is overridable via `TODO_REGISTRY`. +- The registry path defaults to `$XDG_CACHE_HOME/todo/registry.json` (or `~/.cache/todo/registry.json`) and is overridable via `TODO_REGISTRY`. - Version is dual-sourced: the embedded `VERSION` file serves local and `go install` builds, while `main.version`/`main.buildTime` ldflags (set by GoReleaser, and by the Makefile for `make build`) win when present. - `AGENTS.md` is a private, symlinked file and is not part of this repository. Contributor-facing context belongs in this file and in `README.md`. diff --git a/README.md b/README.md index 033375b..08b9045 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,7 @@ Status is `[ ]` open, `[o]` in progress, `[x]` done. `claimed:` records when a t ## Registry -Every `todo init` registers the repo in a machine-local JSON cache at `~/.config/.todocache`. The registry stores the absolute repo path, project name, git remote URL, parsed host/owner, and a `last_seen` timestamp. `todo list --all` and `todo doctor` use this cache to operate across tracked folders without `cd`ing. +Every `todo init` registers the repo in a machine-local JSON cache at `$XDG_CACHE_HOME/todo/registry.json` (falling back to `~/.cache/todo/registry.json`). The registry stores the absolute repo path, project name, git remote URL, parsed host/owner, and a `last_seen` timestamp. `todo list --all` and `todo doctor` use this cache to operate across tracked folders without `cd`ing. ## Note disposition diff --git a/VERSION b/VERSION index e946d6b..3e7bcf0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v1.0.3 +v1.0.4 diff --git a/cmd/todo/cli_test.go b/cmd/todo/cli_test.go index 3286f30..db7086d 100644 --- a/cmd/todo/cli_test.go +++ b/cmd/todo/cli_test.go @@ -23,7 +23,7 @@ func setupGitRepo(t *testing.T) string { t.Fatalf("git init: %v", err) } t.Chdir(dir) - t.Setenv("TODO_REGISTRY", filepath.Join(dir, ".todocache")) + t.Setenv("TODO_REGISTRY", filepath.Join(dir, "registry.json")) return dir } diff --git a/internal/registry/registry.go b/internal/registry/registry.go index c108429..44c12a0 100644 --- a/internal/registry/registry.go +++ b/internal/registry/registry.go @@ -22,13 +22,17 @@ type Entry struct { LastSeen string `json:"last_seen"` } -// DefaultPath returns the default registry file path: ~/.config/.todocache. +// DefaultPath returns the default registry file path: +// $XDG_CACHE_HOME/todo/registry.json, or ~/.cache/todo/registry.json. func DefaultPath() string { + if cacheHome := os.Getenv("XDG_CACHE_HOME"); cacheHome != "" { + return filepath.Join(cacheHome, "todo", "registry.json") + } home, err := os.UserHomeDir() if err != nil { - return ".todocache" + return "todo-registry.json" } - return filepath.Join(home, ".config", ".todocache") + return filepath.Join(home, ".cache", "todo", "registry.json") } // Load reads the registry from path. A missing file is treated as an empty diff --git a/internal/registry/registry_test.go b/internal/registry/registry_test.go index b4511a1..6d591d2 100644 --- a/internal/registry/registry_test.go +++ b/internal/registry/registry_test.go @@ -7,7 +7,7 @@ import ( ) func TestLoadMissingReturnsEmpty(t *testing.T) { - path := filepath.Join(t.TempDir(), "missing", ".todocache") + path := filepath.Join(t.TempDir(), "missing", "registry.json") entries, err := Load(path) if err != nil { t.Fatalf("Load missing: %v", err) @@ -18,7 +18,7 @@ func TestLoadMissingReturnsEmpty(t *testing.T) { } func TestSaveAndLoad(t *testing.T) { - path := filepath.Join(t.TempDir(), ".todocache") + path := filepath.Join(t.TempDir(), "registry.json") want := []Entry{ {Path: "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/code/foo", Project: "foo", Repo: "git@example.com:org/foo", LastSeen: "2026-09-01T00:00:00Z"}, }