Most developers never question where their CLI tools come from. You run brew install swiftlint, it works, and you move on. But in teams of any size, the question "which version of SwiftLint are you running?" surfaces regularly. Someone upgrades globally, a build breaks on CI, and the team gets distracted for hours to figure out what changed.

At Just Eat Takeaway, I built an internal tool called ToolManager that solved exactly this problem for our iOS teams. It pinned tool versions per project, downloaded pre-built binaries, and stayed out of the way. It worked well — well enough that I wrote about the concept in How to Implement a Decentralised CLI Tool Manager. That article documented the design principles and the reasoning behind a decentralised approach. The experience I built at JET and the feedback from the community convinced me to take the idea further: **Luca is a proper open-source project, built from scratch**, expanding the original concept into something broader — including skills management for AI coding agents.

The problem with centralized tool management

Homebrew is excellent at what it does. But it was designed to manage system-wide software, not project-specific developer tools. It installs one version of SwiftLint globally. If project A needs 0.53.0 and project B needs 0.62.0, you are left with some confusing Homebrew gymnastics.

Mint solves the versioning problem for Swift packages — and it does it well. But it builds from source, which requires a full Swift toolchain and can be slow. It is also limited to Swift packages. In practice, teams use tools written in Go, Rust, Python, Zig, and more. A version manager that only covers one language leaves gaps.

Tools like mise take a broader approach, combining tool management with environment variables, task running, and a plugin system. It is powerful, but that power comes with complexity that not every team needs.

The point is not to replace Homebrew, Mint, or mise. It is to fill a gap they were never designed to fill: **project-local, version-pinned installation of pre-built binaries from any source**, with zero configuration overhead.

Introducing Luca

Luca is a lightweight tool and skills manager for macOS and Linux, written in Swift. It reads a YAML file called a Lucafile, downloads pre-built binaries from GitHub Releases or any URL, and symlinks them into .luca/tools/ inside the project directory. No central registry. No building from source. No global PATH pollution. Read the Manifesto.

Install it with a single command:

curl -fsSL https://luca.tools/install.sh | bash

Then create a Lucafile in your project:

---
tools:
  - name: FirebaseCLI
    version: 14.12.1
    url: https://github.com/firebase/firebase-tools/releases/download/v14.12.1/firebase-tools-macos
  - name: SwiftLint
    binaryPath: SwiftLintBinary.artifactbundle/swiftlint-0.61.0-macos/bin/swiftlint
    version: 0.61.0
    url: https://github.com/realm/SwiftLint/releases/download/0.61.0/SwiftLintBinary.artifactbundle.zip
  - name: Tuist
    binaryPath: tuist
    version: 4.80.0
    url: https://github.com/tuist/tuist/releases/download/4.80.0/tuist.zip

The binaryPath field handles nested archives — when the binary lives inside a subdirectory of the zip. For direct executables, you can omit it entirely. Optional checksum and algorithm fields (supporting MD5, SHA1, SHA256, SHA512) let you verify integrity.

How it works

Run luca install and Luca downloads each tool to ~/.luca/tools/{ToolName}/{version}/ — a global cache shared across projects — and creates symlinks in .luca/tools/ inside your project directory. Different projects can pin different versions of the same tool without conflict.

luca install

# Tools are immediately available
swiftlint --version   # 0.61.0
tuist --help

Two automation features make it practical for daily use. A **shell hook** (sourced from ~/.luca/shell_hook.sh) automatically prepends .luca/tools/ to your PATH when you cd into a project — and removes it when you leave. A **git post-checkout hook** runs luca install automatically after git checkout or git switch, so tools stay in sync with the branch. Switch to a branch that pins SwiftLint 0.62.0 and it is there without thinking about it.

You can also install tools directly from GitHub Releases without a Lucafile:

luca install TogglesPlatform/ToggleGen@1.0.0

Skills management: the second dimension

Tool management alone would justify Luca's existence, but the developer landscape has shifted. AI coding agents — Claude Code, Cursor, GitHub Copilot, Gemini CLI, Windsurf, and dozens more — now read project-local Markdown files as "skills" or "instructions" that shape their behaviour.

The problem is fragmentation. Each agent stores skills in a different directory: .claude/skills/, .cursor/skills/, .agents/skills/, .windsurf/skills/, and so on. Luca currently supports **45 agents**. Installing the same skill for multiple agents means copying files to multiple locations — a tedious and error-prone process that no one should do manually.

Luca solves this with the skills and agents sections of the Lucafile:

---
tools:
  - name: SwiftLint
    binaryPath: SwiftLintBinary.artifactbundle/swiftlint-0.61.0-macos/bin/swiftlint
    version: 0.61.0
    url: https://github.com/realm/SwiftLint/releases/download/0.61.0/SwiftLintBinary.artifactbundle.zip

repos:
  vercel: vercel-labs/agent-skills

agents:
  - claude-code
  - cursor

skills:
  - name: swift-testing-expert
    repository: AvdLee/Swift-Testing-Agent-Skill
  - name: frontend-design
    repository: vercel
  - name: skill-creator
    repository: vercel

The repos section defines shorthand aliases so you do not repeat full repository paths. Skills are Markdown files with YAML frontmatter, hosted in Git repositories — following the convention established by Vercel Labs' agent-skills. The agents list controls which agents receive the skill files; omit it to target all the known agents.

You can also install skills directly from the command line:

luca install vercel-labs/agent-skills
luca install AvdLee/Swift-Concurrency-Agent-Skill \
  --skill swift-concurrency \
  --agent claude-code

CI integration

Luca ships a GitHub Action — setup-luca — that installs Luca and your project's tools in two lines:

steps:
  - uses: actions/checkout@v4
  - uses: LucaTools/setup-luca@v1
    with:
      spec: Lucafile
  - run: swiftlint --version

For tool authors, the companion repository LucaWorkflows provides ready-to-use GitHub Actions workflows to build, package, and publish Luca-compatible releases. Push a tag and get a release with macOS universal and Linux binaries — templates are available for Swift, Go, Rust, Python, C#, and Zig.

Where Luca sits today

Luca is a young project and I want to be upfront about that. It works well for the use cases it targets — pre-built binary distribution and AI agent skill management — but it is not trying to replace Homebrew or any general-purpose package manager.

The entire ecosystem — Luca, setup-luca, LucaWorkflows — is open source under the Apache 2.0 license. Luca is written in Swift 6 with strict concurrency checking and has full test coverage on both macOS and Linux. Documentation and tutorials are available at luca.tools.

What started as an internal tool at Just Eat Takeaway — one that proved the concept in production across multiple iOS teams — became a blog post that documented the design principles, and eventually an open-source project that takes those ideas further. At JET, we eventually replaced ToolManager with Luca to leverage its feature set. In the latest release at the time of writing (April 2026), Luca appears solid with various edge cases covered.

I believe the right tool manager is the one that stays out of your way. It pins versions in a file, downloads pre-built binaries, and disappears into the background. If it does its job, you forget it is there — until you switch branches and everything just works.

The project lives at github.com/LucaTools, with documentation at luca.tools. Contributions and feedback are welcome.

Find me on X / Bluesky / LinkedIn.