Skip to content

feat(paths): follow XDG Base Directory Specification#3336

Open
Sayt-0 wants to merge 2 commits into
mainfrom
feat/xdg-base-directories
Open

feat(paths): follow XDG Base Directory Specification#3336
Sayt-0 wants to merge 2 commits into
mainfrom
feat/xdg-base-directories

Conversation

@Sayt-0

@Sayt-0 Sayt-0 commented Jun 30, 2026

Copy link
Copy Markdown
Member

Summary

docker-agent now follows the XDG Base Directory Specification for its config, data and cache directories. This resolves #1638: docker-agent polluted $HOME with ~/.cagent while also creating ~/.config/cagent, and ignored XDG and OS-native conventions.

Resolved locations

XDG_* variables are honoured on every platform (not only Linux), so users who set them, including on macOS, get the location they expect; otherwise the OS-native directory is used.

Dir Env var (all platforms) Linux default macOS default Windows default
config XDG_CONFIG_HOME ~/.config/cagent ~/Library/Application Support/cagent %AppData%\cagent
data XDG_DATA_HOME ~/.local/share/cagent ~/Library/Application Support/cagent %LocalAppData%\cagent
cache XDG_CACHE_HOME ~/.cache/cagent ~/Library/Caches/cagent %LocalAppData%\cagent

Migration of existing installs

On first run (root PersistentPreRunE), paths.MigrateLegacy() relocates the historical layout:

  • ~/.cagent to the data dir
  • ~/.config/cagent to the config dir

Properties:

  • per-entry os.Rename, merging into the destination without clobbering existing entries (on macOS config and data resolve to the same dir, so a merge is required);
  • the legacy directory is removed only once fully drained;
  • skipped for any directory overridden via --data-dir/--config-dir/--cache-dir or paths.SetRoot, so embedders (e.g. Gordon) are untouched;
  • until migration runs, the getters fall back to the legacy directory, so a failed or skipped move never loses data.

Related fixes exposed by the move

Issue Before After
OCI store, models.dev cache, prompt history hardcoded ~/.cagent go through the paths package
--session-db default baked from ~/.cagent/session.db at flag-registration time, ignoring --data-dir resolved lazily from the data dir, so --data-dir and SetRoot are honoured
sandbox delegation forwarded --data-dir/--cache-dir into the container (never bind-mounted) stripped; only the config dir is mounted

Issue #1638 expectations

Expectation Status
Support the XDG spec yes, honoured on all platforms
Stop polluting $HOME with ~/.cagent yes, data moves to the XDG/native data dir
Cross-platform, native conventions (incl. Windows) yes, OS-native fallback when XDG unset
No data loss for existing installs yes, one-time merge migration with legacy fallback

Testing

  • unit tests: resolution, XDG-env honouring, legacy fallback, migrateDir (move/merge/no-clobber/remove-empty/no-op), MigrateLegacy (default layout, macOS shared-dir collision, override skip), native default, and the --session-db regression;
  • go build ./..., go vet, gofmt, golangci-lint clean;
  • end-to-end with the real binary: a populated ~/.cagent + ~/.config/cagent were migrated into the XDG/native dir with content intact and the legacy dirs removed;
  • sandbox path verified: sbx create mounts the config dir at the resolved XDG path read-only and forwards no data/cache host path.

Note on dependency

This reproduces github.com/adrg/xdg's behaviour in three small helpers rather than taking the dependency (suggested in the issue). Swapping to adrg/xdg is a one-line change per helper if maintainers prefer the library.

docker-agent stored data under ~/.cagent and config under ~/.config/cagent,
polluting $HOME and ignoring XDG and OS-native conventions.

Resolve config, data and cache directories via the XDG Base Directory
Specification, honouring XDG_CONFIG_HOME, XDG_DATA_HOME and XDG_CACHE_HOME on
every platform with OS-native fallback otherwise (Linux ~/.config,
~/.local/share, ~/.cache; macOS ~/Library; Windows %AppData% and %LocalAppData%).

Existing installs are migrated once at startup: ~/.cagent and ~/.config/cagent
are relocated entry by entry into the new directories, merging without
clobbering (on macOS both share ~/Library/Application Support/cagent). The move
is skipped for overridden directories, and the getters fall back to the legacy
directory until it runs, so no data is lost.

Route the OCI store, models.dev cache and prompt history through the paths
package, and resolve the session-db default lazily from the data dir so the
data-dir override and SetRoot are honoured.

Closes #1638
@Sayt-0 Sayt-0 requested a review from a team as a code owner June 30, 2026 16:41
@aheritier aheritier added kind/feat PR adds a new feature (maps to feat:). Use on PRs only. area/config For configuration parsing, YAML, environment variables area/cli CLI commands, flags, output formatting area/docs Documentation changes labels Jun 30, 2026
paths.GetConfigDir now honours XDG_CONFIG_HOME (via os.UserConfigDir),
so tests that isolate user config with only t.Setenv("HOME", ...) leak
alias writes into the shared XDG config dir when XDG_CONFIG_HOME is set
(as on CI runners). A "default" alias written by pkg/config tests then
made pkg/sandbox/kit and pkg/teamloader resolve "default" to an OCI ref
and fail. Clear the XDG variables from each package's TestMain to
restore HOME-based isolation.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/cli CLI commands, flags, output formatting area/config For configuration parsing, YAML, environment variables area/docs Documentation changes kind/feat PR adds a new feature (maps to feat:). Use on PRs only.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for XDG spec

2 participants