# What zc-workstation is

> Set up a developer workstation on macOS, Linux and Windows from one YAML file: packages, dotfiles and runtimes, planned before anything changes.

One YAML file says what a workstation needs: the packages, the dotfiles, the language runtimes. `zc-workstation` reads what this machine has, prints the difference, and only if you ask, closes it. Every apply is checked by planning again.

```console
$ zc-workstation plan
CREATE  brew      cask:ghostty           install with brew
CREATE  brew      mas:uBlock Origin Lite install with mas
UPDATE  dotfiles  .gitconfig             update; changed outside chezmoi, apply overwrites it
CREATE  runtimes  java@temurin-25        install with mise
```

## What it manages

  
| Provider | Reads | Changes with |
| --- | --- | --- |
| 
[`packages`](providers.html#packages)

 | 

A Brewfile, an apt or dnf package list, or a winget import file

 | 

`brew`, `mas`, `apt-get`, `dnf`, `winget`

 |
| 

[`dotfiles`](providers.html#dotfiles)

 | 

A chezmoi source directory

 | 

`chezmoi apply`, one path at a time

 |
| 

[`tools`](providers.html#tools)

 | 

A mise config file

 | 

`mise install`

 |
| 

[`desktop`](providers.html#desktop)

 | 

A wallpaper and a screen saver page

 | 

`osascript`, WebViewScreenSaver

 |
| 

[`settings`](providers.html#settings)

 | 

macOS preferences

 | 

`defaults`

 |
| 

[`services`](providers.html#services)

 | 

Homebrew services and launchd agents

 | 

`brew services`, `launchctl`

 |
| 

[`localdir`](providers.html#localdir)

 | 

Files written inline in the config

 | 

Direct writes inside one directory

 |

It does not reimplement these tools. chezmoi already handles templates, secrets and per-machine differences, and mise already resolves versions. `zc-workstation` adds one plan across all of them, a dashboard, and the same rules for every surface.

## One config, three systems

A target can name the systems it is for. The same file then describes the Mac, the Linux laptop and the Windows desktop:

```yaml
targets:
  - {name: brew,   provider: packages, os: [darwin],  file: packages/Brewfile}
  - {name: apt,    provider: packages, os: [linux],   file: packages/apt.txt}
  - {name: winget, provider: packages, os: [windows], file: packages/winget.json}
  - {name: dotfiles, provider: dotfiles, source: home}
  - {name: runtimes, provider: tools, file: mise.toml}
```

A target for another system is skipped, and shown as skipped.

## The loop

1.  **Plan** asks each provider what is missing and lists actions: `CREATE`, `UPDATE`, `DELETE` and `MANUAL`. Planning writes nothing.
    
2.  **Apply** runs the actions in order, then plans again. A change that ran but did not take is reported as still outstanding, never as done.
    
3.  **Saved plans** pin what was reviewed. `plan -out` writes the plan, and `apply FILE` refuses when the machine changed since.
    
4.  **MANUAL** is what no tool should do for you, such as installing the package manager itself or trusting a mise config. The plan says what to run.
    

## Five surfaces, one contract

The CLI is the contract. The [dashboard](ui.html), the [menu bar app](app.html), the [MCP server](agents.html), the agent skills and scripts all go through the same plan and apply, with the same refusals.

On a Mac, the menu bar app also has **ZC Care**, a window that cleans junk and what AI tools leave behind, uninstalls apps with their leftovers and finds what fills the disk.

## A new Mac

Keep the config in its own repo, and a new Mac needs one command. See [A new Mac in one command](new-machine.html).
