Hi everyone,
I’m a newcomer to OCaml, and one of the things that has slowed me down is the tooling setup: understanding opam switches, remembering to eval $(opam env), knowing when to regenerate .opamfiles. So (together with Claude Code) I built oc - a small CLI that wraps opam and dune and gives them a Cargo- or uv-like interface:
oc new my_app # scaffold project + initialise isolated switch
oc add yojson # add dependency, update oc.toml, install
oc build # ensure deps, dune build
oc run # ensure deps, dune exec
No opam switch create, no eval $(opam env), no hand-editing .opam files.
What it is:
A small, thin orchestration layer with no dependencies beyond opam and dune themselves. It calls opam to manage switches and resolve packages, and dune to build - wrapping the tools the community already uses and trusts rather than replacing them. It deliberately does less: if you want full control over your environment, opam gives you that directly.
What it isn’t:
A replacement for opam or dune. The intention is to offer a simpler surface for those who prefer it, whether you’re just starting out or you’ve been writing OCaml for years and want less ceremony for everyday tasks.
A note on AI assistance:
I should be upfront: this project was developed almost entirely with the help of Claude Code. I wrote the specs, made the design decisions, and reviewed everything - but the code is almost entirely AI-generated. I think that’s worth naming, both for transparency and because it’s an interesting case study in what AI-assisted development looks like for a small tool.
Prior art:
I’m aware of a few similar efforts. esy is the closest in spirit - sandboxed builds, a lock file, a simpler manifest - but it makes some different choices: it requires Node.js (npm install -g esy) and brings its own dependency resolver rather than delegating to opam. oc
is a narrower tool: no additional runtime dependencies, stays on top of standard opam and dune, and trades power for simplicity. dune init project handles scaffolding but leaves switch management to you. opam’s own local switches (opam switch create . 5.2.0) are essentially what oc automates, but you still have to drive them manually. If there’s something I’ve missed that already solves this cleanly, I’d genuinely like to know.
Why not just shell aliases?
You probably could get close with a few shell functions. The reasons I went further: a cross-platform binary needs no shell setup, the lockfile gives reproducible builds across machines, and having a single oc.toml as the source of truth is cleaner than remembering which opam commands to run. Whether that’s worth it is a fair question.
Why Go, not OCaml:
Partly the bootstrapping problem — it felt odd to write a tool that helps you set up OCaml before you can set up OCaml. More practically, Go made it easy to ship a single static binary with no runtime dependencies, and it let me build and iterate on the tool quickly. I’m not against a port to OCaml if someone wanted to take that on.
Current state:
The tool works for my personal use, has a test suite, a lockfile, and basic CI. It’s rough around the edges and I’m sure there are things that would make an experienced OCaml developer wince. That’s part of why I’m posting - I’d genuinely value feedback on:
- Does this solve a real problem, or does the community have better answers I missed?
- Are there correctness issues with how
ocmanages switches or generates.opamfiles? - What’s missing before this would be useful to you?
The homepage is at emilkloeden.github.io/oc and the source is at github.com/emilkloeden/oc.
Thanks for reading and I look forward to your feedback.