Run multiple Cloud Foundry CLI sessions and targets in parallel without
CF_HOME conflicts.
The official cf CLI keeps its active API, organization, space, and
authentication state in $HOME/.cf, so changing the target in one terminal
changes it everywhere. cfs gives each project or Git worktree an isolated
CF_HOME while preserving the normal cf command. Optional named contexts let
one project use several targets without a shared current selection.
The demo runs the real cfs shim with a local, credential-free CF fixture.
View the source.
Already using environment variables or another context manager?
Compare cfs with manual CF_HOME, cf-targets-plugin, and cfctx.
Install the official CF CLI, then install
cfs with Go 1.26.10+:
go install github.com/zongqichen/cloud-foundry-cli-contexts/cmd/cfs@latest
cfs setupPrebuilt binaries support Linux and macOS on x86-64 and arm64. Put the shim
directory printed by cfs setup first on PATH, open a new shell, and run
cfs doctor.
From a project, log in normally if you do not have a saved global login:
cd ~/work/orders
cf login --sso -a https://api.example.com -o commerce -s development
cf appsAnother project or Git worktree receives a separate context automatically. Terminals and agents in the same worktree intentionally share its default context. A fresh default context can reuse your existing login.
Check for a newer published release without changing the installed binary:
cfs updateFor scripts and coding agents, use cfs update --json. When an update is
available, follow the printed command or update through the same installation
channel. A Go installation can be updated with:
go install github.com/zongqichen/cloud-foundry-cli-contexts/cmd/cfs@latest
cfs setup
cfs doctorSuccessful interactive cfs control commands check at most once every 24 hours
and show one notice per new version. Checks never run from the transparent cf
shim, JSON output, CI, or non-interactive processes. Set
CFS_NO_UPDATE_CHECK=1 to disable notices. Version checks contact only the public
GitHub Releases API and send no Cloud Foundry or workspace data.
The normal cf command always uses the workspace's default context. Create a
named context only when the same workspace needs another independent target:
cfs context create prod
cfs -c prod login --sso -a https://api.example.com -o commerce -s production
cfs -c prod appsEach name has its own CF_HOME and lock. Names are workspace-local and selected
per command; there is no mutable current context to race over. Inspect them
with cfs context list or cfs context status prod --json --redact. Unknown
names fail without creating state.
An ordinary managed cf command can copy your saved global API and authentication
configuration into the workspace's fresh default context without prompting.
This happens only when its managed .cf/config.json is absent while the shim
holds the context lock and the global configuration has a nonblank API Target.
The copy clears the organization and space, so choose them in the workspace:
cd ~/work/orders
cf target -o commerce -s developmentThe shim skips automatic copying if it finds an existing workspace configuration
while holding the context lock, even if the configuration has no API target.
Named invocations such as cfs -c prod ... require explicit import or login.
To copy the global organization and space as well, run cfs import in the
workspace. To import into a named context, create the context first:
cfs context create prod
cfs import --context prodExplicit import requires confirmation or --yes for non-interactive automation.
Use --force to replace an existing target. Unset CF_HOME before importing.
Both modes require a valid, private global configuration file and leave it
unchanged. Each copy is an independent snapshot that may contain credentials.
Token refreshes and later target changes do not synchronize between contexts.
A saved API target does not establish that the copied login is still valid.
No agent integration is required. Start Codex, Claude Code, or an IDE agent in
its project or worktree and let it run ordinary cf commands. Give an agent an
exact named command such as cfs -c prod apps when it needs a non-default
target. To pin the workspace regardless of the agent's working directory, set
CFS_WORKSPACE_ROOT; to run one command against the global target, use cfs global <cf ...>. For diagnostics safe to attach to agent logs, use:
cfs describe --json
cfs status --json --redact
cfs context status prod --json --redactcfs describe --json is the one-call self-discovery surface: version, exit-code
contract, environment variables, workspace resolution, and the contexts in the
current workspace. It never invokes cf. The state-changing commands
(cfs context create / remove, cfs import, cfs reset) also accept --json
and print a structured result for the agent to parse. cfs context list --targets --json lists every context with its api/org/space in one call. For
throwaway or parallel work, cfs context create --ephemeral mints a uniquely
named context that cfs gc reaps once it has been idle past CFS_EPHEMERAL_TTL
(default 24h).
The optional cfs Agent Skill teaches agents to discover existing contexts, fail closed on ambiguity, and preserve user authorization. Install it for Codex, Claude Code, or another supported agent:
npx skills add zongqichen/cloud-foundry-cli-contexts --skill cfsTo make invocation explicit, add Use $cfs for Cloud Foundry context selection. to AGENTS.md, or Use /cfs for Cloud Foundry context selection.
to CLAUDE.md.
cf command -> cfs shim -> workspace default CF_HOME -> official cf CLI
cfs -c NAME ... -> cfs -> workspace named CF_HOME -> official cf CLI
cfs setup places a transparent cf shim on PATH. For each invocation, the
shim resolves the current workspace, selects its private state, and delegates to
the official CLI. Named commands take the same path with an explicit context.
Authentication, token refresh, plugins, API calls, signals, and exit codes
remain the official CLI's responsibility.
Git worktrees are detected automatically. For a directory that is not a Git worktree, create this marker at its root:
version = 1Alternatively, select a workspace for one command:
CFS_WORKSPACE_ROOT=/workspace cf appsAn explicit CF_HOME bypasses workspace management unless
CFS_WORKSPACE_ROOT is set. Without a workspace or an explicit bypass, cfs
refuses to fall back to the global CF home.
cfs setup Install the transparent cf shim
cfs status Show the current workspace and target
cfs describe Print the cfs contract (version, exit codes, env, contexts)
cfs context Create, list, inspect, or remove named contexts
cfs global Run one cf command against the global target
cfs import Import the global context into default or a named context
cfs doctor Diagnose the installation and workspace
cfs reset Move this workspace's state to trash
cfs gc Find stale workspace state
cfs uninstall Remove the shim without deleting state
cfs version Print version information
cfs update Check for a newer published release
cfs help [command] Show help
Run cfs help context for the named-context commands.
cfs doctor checks local configuration and workspace state. It does not verify
a live CF login.
Entered the wrong workspace, or need to log in somewhere else for a moment? Run one command against the global target without leaving the directory:
cfs global login --sso
cfs global targetThis is the discoverable form of CFS_DISABLE=1 cf .... cfs collects no
telemetry. Workspace state and recoverable trash can contain active tokens; see
Security.
make check
make security
make release-check
make smoke
make agent-smoke
CFS_REAL_CF=/path/to/official/cf make e2eSee testing, design, the changelog, and the release guide. Contributions are welcome; read CONTRIBUTING.md. Licensed under Apache-2.0, like the official CF CLI.
