Oc — a Cargo-like CLI for OCaml beginners (AI-assisted, feedback very welcome)

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 oc manages 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.

A lot of languages provide this type of scaffolding to create a new project. But I always wonder: is this not quite similar to starting from a template Git repository? A Git template repository does not depend on new tools and is easy to maintain. The moment where we have to edit the generated files is arriving sooner or later. So in my view a scaffolding tool is only useful at the start of a project.

Welcome to OCaml and thanks for sharing your project! :slight_smile:

Does this solve a real problem, or does the community have better answers I missed?

This is still an open problem in the ecosystem. You may be interested in comparing with these related efforts as well:

There are also some unpublished/un-publicized efforts in this space which you may come across :slight_smile:

Hi!

Thanks for taking the time to give me feedback :grinning_face:.

Whilst I would summarise your feedback as “not something I would use”, I appreciate you making the effort to suggest an alternate workflow. I’ll admit you can go a long way with a good template. I think tools such as uv in the python system (for which I’m better acquainted) provide one advantage over the template which is the facility to iteratively add dependencies as you come across a need for them. Obviously you don’t need a tool (or perhaps more accurately another tool) for that.

Thanks again!

Hi :waving_hand:

Thanks for the feedback and warm welcome. I appreciate you validating I’ve at least hit on a real problem. Thank you also for sharing additional prior-art. I’m not naive enough to think I’d be the first one to try to tackle a problem, but I didn’t expect there to be this many previous attempts :sweat_smile:. Does kinda give me XKCD standards vibes :smiley: .It makes me think the underlying reason while it remains a problem must be an interesting one.

I have some reading to do, thanks again!

The key thing in these tools (which are great to see!) is to avoid creating new metadata formats like oc.toml.

If your tool instead directly probed for dune-project and/or *.opam files and updated the right thing (e.g. depending on whether dune opam file generation is active or not), you would interoperate with existing projects and not have another file format (which gets out of sync very fast).

Thank you for the constructive criticism. What you say makes sense to me. A toml file might initially be more intuitive to someone with a background like mine, but less intuitive to others. It isn’t necessary for any user (the workflow being to use the add subcommand rather than manipulate a config file) so I have created an issue and tagged it for the next release.

Thanks for taking the time to provide input :grinning_face:!

fyi the command name oc is already taken by redhat’s openshift

People started using npm as a system package manager, I guess because it’s easier than shipping to apt and yum […]

easier and much less secure :slight_smile:
(Edit: well I didn’t even get to catch my breath on that one)

Hi! Thanks for the feedback :grinning_face:. Given the uptake on this is likely to be extremely low, i can live with the clash :sweat_smile: (and two characters is nice).