Skip to content

session-env

Usage
session-env COMMAND [ARGS...]
session-env install [GROUP...] [--var NAME]... [--except NAME]... [--now]
session-env add|remove ITEM... [--now]
session-env sync [--now] | apply | preview [GROUP...] | uninstall
session-env select [--now] | status | list | setup | dismiss | help
session-env desktop install|uninstall|preview NAME | status

Exports fish-config variables to the whole login session, not just fish. Variables set in config.fish only reach processes started from fish; anything launched from the desktop (IDEs, the Claude desktop app, systemd user services) never sees them. install writes the selected variables from data/session-env.tsv to ~/.config/environment.d/fish-config.conf, which systemd applies to the user session at the next login. uninstall removes that file. The name has no numeric prefix so it sorts after numbered files such as 99-environment.conf (/etc/environment, which may set an absolute PATH).

With no groups, install uses the catalog’s default groups (only xdg, the XDG base directories). Each install replaces the whole file, so name every group you want each time. Entries that copy a fish value (value “-” in the catalog) write fish’s resolved value, and are skipped when fish does not export that variable. Every other entry falls back to a value already present in the session, so it never overrides one set elsewhere.

Selection is per variable. A group is shorthand for all its variables; --var adds one variable to an install and --except leaves one out. add and remove change what is installed by variable or group and keep the rest. The file records the choice in its header: “# groups:” names the groups that have a selected variable, and a “# vars:” line (written only when some group is partly selected) is the exact variable list. status, list, sync and every later add or remove read the same header, so the selection round-trips. A file with only “# groups:”, as written before per-variable selection, means every variable of those groups.

A copied value is a snapshot taken at install time. status compares the file with what install would write now and lists every difference (a copied value that changed, a variable fish stopped exporting, an entry the catalog gained); sync rewrites the file from its own selection to refresh it, without resetting to the default groups.

environment.d is read at login. With --now (or the apply command) session-env also pushes the selection into the running user manager (systemctl --user set-environment) and the D-Bus activation environment (dbus-update-activation-environment), so programs started afterwards see it without a re-login. Each is used only when installed, nothing runs at shell startup, and programs already running keep their environment. A catalog value is applied only when the session does not already have the variable, like the file’s fallback.

An interactive shell with no environment.d file yet is told once, at startup, that setup exists (conf.d/session-env-offer.fish, part of the C6 first-run greeting). setup asks whether to install the default group, never blocks without a terminal, and records the answer; dismiss records “no” without asking.

desktop manages user-level copies of .desktop launchers whose Exec lines start with “env -u VAR …”, for apps that must not see some of the exported variables. The motivating case is the privacy group: DO_NOT_TRACK and DISABLE_TELEMETRY switch off claude-code’s feature flags, so the Claude desktop app’s launcher needs them unset. Only files in $XDG_DATA_HOME/applications (~/.local/share/applications) are written, never the system copy, and each starts with a marker line so uninstall removes exactly what install added and never a launcher you edited yourself.

install [GROUP...] Write the given groups (default groups when none)
--var NAME install: include one more variable (repeatable)
--except NAME install: leave a variable out (repeatable)
add ITEM... Add variables or groups to the installed selection
remove ITEM... Remove variables or groups from the installed selection
sync Rewrite the file from its own selection
apply Push the installed selection into the running session
--now install, sync, add, remove: also apply immediately
preview [GROUP...] Print what install would write, without writing it
uninstall Remove the managed file
select Pick variables in a checkbox list (needs a terminal)
status Show the file, its selection and whether it is stale
list Show every group and variable, marking the selection
setup Ask once whether to install the default group
dismiss Record that setup is not wanted
desktop install NAME [--var VAR]... [--group GROUP]... Create the override (default: unset the privacy group)
desktop uninstall NAME Remove the override
desktop preview NAME Print the override without writing it
desktop status List overrides this tool manages
help, -h, --help Show this help
0 Success, or help was shown
1 Missing catalog, nothing installed, or a write or apply failure
2 Unknown command, group or variable, or a usage error

Known limits. environment.d applies at login only; --now reaches programs started after it, not running ones. Hooks and MCP servers that claude spawns do not get the privacy variables: the CLAUDE_CODE_SHELL_PREFIX helper covers only the Bash tool, and putting the variables in claude’s own environment (settings.json env, or the session) is what disables its feature flags. There is no central setting for spawned hooks and MCP servers, so set them where they are spawned: the “env” map of each stdio server in .mcp.json (or claude mcp add --env NAME=value), and an “env NAME=value” prefix in a hook’s command string.

Terminal window
session-env install
session-env install xdg xdg-tools editor
session-env install xdg privacy --except DISABLE_TELEMETRY
session-env add CARGO_HOME GOPATH
session-env remove telemetry
session-env status
session-env sync --now
session-env desktop install claude-desktop
session-env desktop uninstall claude-desktop

Dependencies: __session_env_apply_now, __session_env_desktop

Classification: bypasses-shadow(mkdir,mv,rm)