Rust CLI (zwd) for opening multi-project Zed sessions from .code-workspace files.
Zed can open multiple folders directly, but its terminal still benefits from a single visible root in some workflows. Zed Workspace Dock can create a lock-protected cache directory where each project folder is linked, then open that dock root in Zed. Running ls in the terminal shows the linked projects without copying source code.
Install - Quick start - Usage - Workspace files - Development
Note
On Windows, symlink dock mode creates directory symbolic links. Windows may require Developer Mode, SeCreateSymbolicLinkPrivilege, or an administrator shell to create those links.
- Create registered Zed workspace files from one or more project folders.
- Open, inspect, edit, or delete a workspace by registered name or by
.code-workspacepath. - Choose between direct
foldersmode and managedsymlinkdock mode. - Add or remove one or more project folders while synchronizing an existing dock safely.
- Install global Zed tasks backed by the packaged task templates.
- Reconcile dock links safely using
.zwd-lock.jsonownership locks, preserving extra files and manual symlinks. - Use the short
zwdcommand for repeated terminal workflows.
Install the latest release on macOS or Linux:
curl --proto '=https' --tlsv1.2 -LsSf \
https://github.com/runrly/zwd/releases/latest/download/zwd-installer.sh | shInstall the latest release on Windows PowerShell:
powershell -ExecutionPolicy Bypass -c "irm https://github.com/runrly/zwd/releases/latest/download/zwd-installer.ps1 | iex"Installers place the zwd binary under Cargo's bin directory by default. Make sure that directory is on your PATH.
You can also download platform archives from GitHub Releases. Release artifacts include SHA256 checksums.
Create a registered workspace from two project folders:
zwd create ../api ../web --name workOpen it in Zed:
zwd open workList registered workspaces:
zwd listAdd another folder later:
zwd add ../docs --workspace workRemove a folder without deleting the project directory:
zwd remove ../docs --workspace workThe public command is zwd, short for Zed Workspace Dock. The repository, Cargo package, release assets, and managed state directories use the short zwd name.
Create a registered workspace with a generated name:
zwd create ../api ../webThe command prints the real .code-workspace path created under the user config directory, for example:
~/Library/Application Support/zwd/workspaces/ws-abcd0001020304ff.code-workspace
Create a workspace file in a specific output directory:
zwd create ../api ../web --name work --output ../workspacesCreate a workspace that opens folders directly instead of a dock root:
zwd create ../api ../web --name work --mode foldersOpen a workspace file by path:
zwd open ../workspaces/work.code-workspaceForce folder mode for one run:
zwd open ../workspaces/work.code-workspace --mode foldersReuse an existing Zed window:
zwd open work --reuseUse a custom Zed executable, useful for tests or alternate installs:
zwd open work --zed-bin /Applications/Zed.app/Contents/MacOS/cliFor a simple argument such as work, registered workspaces take precedence over a same-name file or directory in the current directory. Use an explicit path such as ./work.code-workspace when you want to open a local file.
Add one or more folders to a workspace:
zwd add ../api ../web --workspace workRemove one or more folders by canonical path. The projects themselves are never deleted:
zwd remove ../legacy ../docs --workspace workWhen the current directory is the root of a managed dock, add and remove infer the owning workspace from .zwd-lock.json, so the flag is optional:
zwd add ../docs
zwd remove ../legacyBoth commands are idempotent. Adding an existing folder or removing an absent one reports that state without changing the workspace or dock. A dock that already exists is reconciled immediately and incrementally; a dock that does not exist is still materialized by the next zwd open in symlink mode.
Synchronize a dock without opening Zed:
zwd sync work
zwd sync ./work.code-workspace
# From the root of a managed dock:
zwd syncopen and sync recover missing managed links and reconcile changes from the .code-workspace configuration. Correct links remain in place. Files, directories such as .dirac, and manually added symlinks are preserved without being adopted into the lock. A changed managed symlink target, a managed link replaced by a real file/directory, or an unmanaged entry occupying a required link name causes an error before link changes. Restore or move that conflicting entry manually before retrying.
sync creates an absent dock for a symlink-mode workspace. For a folder-mode workspace it synchronizes an existing dock, or succeeds with a message when there is no dock. Workspace inference uses only the lock in the current dock root; it does not search parent directories. The workspace file remains the source of truth, so synchronization does not restore projects removed from that file.
Inspect a workspace and its dock health:
zwd status workstatus reports the workspace path, registration, mode, resolved folders, and dock state without changing files. Extra dock content is healthy; missing managed links, changed destinations, stale lock membership, unresolvable folders, or invalid ownership are unhealthy. Use sync to recover missing links or reconcile configuration changes. A missing dock is normal before the first open or sync in symlink mode.
Preview workspace deletion, then confirm it:
zwd delete work
zwd delete work --forcedelete accepts a registered name or an explicit .code-workspace path. Without --force, it previews the workspace file and owned dock and states that all local dock contents would be removed. With --force, it removes the entire validated, lock-owned dock, including extra files, nested directories, and manual symlinks, then removes the workspace file. Symlinks are unlinked without following their targets, so linked project folders are preserved. Files stored only inside the dock are deleted. The command does not close applications using the dock and refuses to delete from the dock root or a real subdirectory beneath it. Missing project targets do not prevent deletion. Filesystem errors can interrupt deletion; the workspace file is kept if dock removal fails.
Install global Zed tasks:
zwd installBy default, this writes the global Zed tasks file at ~/.config/zed/tasks.json. The installer reads templates from resources/zed-tasks.json, injects the absolute command path, and merges by task label without duplicating managed tasks.
Install tasks with an explicit binary path:
zwd install --command /usr/local/bin/zwdInstall into an explicit tasks file:
zwd install --tasks-path ~/.config/zed/tasks.jsonInstalled tasks use $ZED_FILE. Run them while a .code-workspace file is open or selected in Zed.
Zed Workspace Dock accepts strict JSON .code-workspace files:
{
"folders": [
{ "name": "api", "path": "../api" },
{ "path": "../web" }
],
"zwd": {
"mode": "symlink"
}
}Supported modes are:
symlink: build and open a managed dock root.folders: pass resolved project folder paths directly to Zed.
If zwd exists, mode is required. If folders is missing, runtime parsing treats it as an empty list.
Important
Workspaces from older versions that use zed-dock must be migrated manually. Rename the top-level zed-dock object to zwd, and rename dock files from .zed-dock.json to .zwd-lock.json.
Registered workspaces are stored under the user config directory at zwd/workspaces/. Workspaces created with --output <dir> are standalone files in that directory and are opened by path.
The create command accepts one or more folder paths as positional arguments. It writes symlink mode unless --mode folders is passed. Created workspaces store canonical absolute folder paths resolved from the current working directory.
add and remove preserve all unrelated JSON properties in an existing workspace file, including Zed settings and tasks, while changing only folders. remove may leave folders empty; use delete when the workspace file and its dock should be removed.
JSON Schemas are published under resources/schemas/ using JSON Schema Draft 2020-12:
resources/schemas/code-workspace.schema.jsondescribes the.code-workspaceshape used by this tool.resources/schemas/zwd-lock.schema.jsondescribes the internal.zwd-lock.jsonlock stored inside managed docks.
The schemas are documentation, editor, and test resources. Runtime parsing still uses the Rust data model and explicit validation errors.
- Dock directories live under the platform cache directory at
zwd/docks/. - Each managed dock contains
.zwd-lock.json. - Rebuilds modify only lock-owned docks.
- Incremental edits reconcile only changed symlinks in an existing owned dock.
- Existing dock directories without a valid lock abort.
- Extra files and manual symlinks are preserved during opening and reconciliation;
delete --forceremoves all local dock contents. - Existing dock roots must be real directories and locks must be regular files, not symlinks.
- Managed link conflicts abort synchronization before any link changes.
- Project folders are symlinked, not copied.
- Symlink targets are not deleted or mutated.
folders[].namemust be one filesystem entry name, not a path.folders[].namecannot use reserved dock metadata names such as.zwd-lock.json.- Registered workspace names must be one filesystem entry name without
.code-workspace;openaccepts the name with or without that extension. - Registered workspace creation does not overwrite an existing workspace unless
--forceis passed. delete --forcenever bypasses dock ownership checks; it confirms deletion of the entire owned dock.
Use the pinned Rust toolchain from rust-toolchain.toml. If the host does not already have the toolchain and support packages, use the repo Devbox environment:
devbox shellOr run any command through Devbox without entering a shell:
devbox run -- cargo check --all-targets --lockedRun the core checks:
cargo fmt --all -- --check
cargo check --all-targets --locked
cargo test --locked
cargo clippy --all-targets --all-features --locked -- -D warningsBuild locally:
cargo buildBuild a production binary:
cargo build --release --lockedThe release binary is written to target/release/zwd.
This project uses Release PRs:
- Merge feature and fix PRs into
main. release-plzopens or updates the sole Release PR onrelease-plz/mainwith the next version and changelog.- Merge the Release PR; automation creates the signed
vX.Y.Ztag. - A maintainer manually dispatches the Release workflow for that tag.
- The protected
release-publicationenvironment requires approval beforecargo-distbuilds release archives, installers, checksums, attestations, and the GitHub Release.
To publish a tag, open Actions → Release → Run workflow, select main, and enter its vX.Y.Z tag. Then approve the release-publication environment.
Before the first release, configure the release-signing environment with the RUNRLY_ECHO_SIGNING_PRIVATE_KEY secret. Register the matching public key as an SSH signing key on the maintainer's GitHub account, where echo@runrly.dev is a verified email. Release tags use the identity runrly-echo <echo@runrly.dev>; the private key is not available to the manual publication workflow.
Versioning follows CLI SemVer. The package is currently publish = false, so distribution is through GitHub Release artifacts rather than crates.io.
This project was informed by:
fu5ha/zed-workspacesartumont/zed-workspaces- Zed issue #9459: support opening
.code-workspacefiles in the editor
No source code from those projects is copied here. If that changes later, keep the required license notices with the copied code.