// docs/platforms/macos.md docs online

vincent on macOS

Apple silicon and Intel are both released and both exercised in CI.


Install

brew install lezli01/tap/vincent

Or double-click vincent_*_darwin_universal.pkg from the release page — one universal installer for both architectures, putting the binary at /usr/local/bin/vincent.

Or unpack the release archive by hand:

tar -xzf vincent_*_darwin_arm64.tar.gz      # or _darwin_amd64 on Intel
sudo mv vincent /usr/local/bin/
vincent version

~/.local/bin works just as well if you would rather avoid sudo. Full detail, including signature verification: Installation.

Gatekeeper

There is nothing to do. Every macOS artifact — the binaries inside both archives, and the .pkg — is signed with an Apple Developer ID identity under the hardened runtime, and notarized by Apple. Gatekeeper clears a downloaded release on its own, so there is no “cannot be opened because it is from an unidentified developer” dialog and no quarantine attribute to strip.

If you have older instructions that say to run xattr -d com.apple.quarantine, delete them. Stripping the attribute now only turns off the check that would have told you the file had been tampered with.

Check for yourself, on the binary you are about to run:

codesign --verify --strict --verbose=2 /usr/local/bin/vincent
spctl --assess --type execute -vv /usr/local/bin/vincent

spctl should report accepted with source=Notarized Developer ID. That check reaches Apple over the network: the ticket for a bare binary is served by Apple rather than carried inside it, because a Mach-O has nowhere to staple one.

The .pkg is stapled, which is the one thing it does that the archive cannot: its ticket travels inside the file, so a first launch works on a machine that is offline or behind a filtered network. Verify it before installing with:

pkgutil --check-signature vincent_*_darwin_universal.pkg
spctl --assess --type install -vv vincent_*_darwin_universal.pkg

com.apple.provenance is unrelated — macOS adds it to everything and it does not block execution.

Directories

macOS is the one platform where config and data are nested under the same root:

Purpose Location
Config ~/Library/Application Support/vincent/
Data ~/Library/Application Support/vincent/data/
~/Library/Application Support/vincent/config.yaml         # 0600, dir 0700
~/Library/Application Support/vincent/workflows/*.yaml
~/Library/Application Support/vincent/data/vincent.db
~/Library/Application Support/vincent/data/token          # 0600
~/Library/Application Support/vincent/data/daemon.json
~/Library/Application Support/vincent/data/logs/daemon.log
~/Library/Application Support/vincent/data/worktrees/{task_id}/
~/Library/Application Support/vincent/data/transcripts/{task_id}/

Note the space in the path when scripting — quote it, or use ~/Library/Application\ Support/vincent. Both directories are overridable with VINCENT_CONFIG_DIR and VINCENT_DATA_DIR; see Files and directories.

Running at login

vincent service install
vincent service status

That writes a LaunchAgent to ~/Library/LaunchAgents — a per-user agent, never a root LaunchDaemon, and no elevation is required. The OS user is vincent’s trust boundary: an agent gets your privileges, your agent-CLI logins and your git identity, and nothing more.

KeepAlive is conditional on a non-clean exit. A daemon that exits 0 was asked to stop, and relaunching it would make vincent daemon stop impossible.

The agent starts at login, which is the same promise the Linux and Windows backends make.

The PATH problem

This is the macOS-specific gotcha, and it is worth understanding rather than just working around.

launchd hands a service a minimal PATH/usr/bin:/bin:/usr/sbin:/sbin. Every agent CLI installs outside it: Homebrew (/opt/homebrew/bin on Apple silicon, /usr/local/bin on Intel), an npm prefix, an nvm shim directory, ~/.local/bin.

Left alone, an installed service therefore resolves no agent CLI at all, while the same daemon started by hand finds every one — the daemon runs, the TUI lists every adapter as missing, and nothing in either says why.

So vincent service install captures the PATH of the shell that ran it, along with the config and data directories, and writes them into the plist. The shell you install from has, by construction, the PATH that works.

Two consequences:

  • It goes stale. Install an agent CLI somewhere new and run vincent service install again to recapture. That is the same “reinstall to recapture” contract the directories have.
  • It is not the only answer. agents.<name>.path in config.yaml is absolute and never consults PATH:

    agents:
      claude: { path: "/opt/homebrew/bin/claude" }
    

Verify with vincent daemon status or the TUI’s daemon view: if the adapter list is empty under a service that is running, this is why.

Terminal notes

  • Native text selection needs the TUI’s mouse handling off: press M, or hold ⇧ while dragging.
  • Cmd+V pastes; it arrives as a bracketed paste and lands in the focused field with no key involved.
  • $EDITOR is honored for edit-and-retry and description editing. Use something that runs in the terminal and blocks until you close it — vim, nano, or code --wait.
  • Everything on this platform behaves the same as Linux, including restricted permission mode for all three adapters.

See also