From ocaml-dev
Provides OCaml project setup standards for dune files, .mli interfaces, ocamlformat, logging, licenses, CI, and structure. Use for new libraries, opam releases, or reviews.
npx claudepluginhub avsm/ocaml-claude-marketplace --plugin ocaml-devThis skill uses the workspace's default tool permissions.
Every OCaml project needs:
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Checks Next.js compilation errors using a running Turbopack dev server after code edits. Fixes actionable issues before reporting complete. Replaces `next build`.
Every OCaml project needs:
| File | Purpose |
|---|---|
dune-project | Build configuration, opam generation |
dune (root) | Top-level build rules |
.ocamlformat | Code formatting (required) |
.gitignore | VCS ignores |
LICENSE.md | License file |
README.md | Project documentation |
| CI config | GitHub Actions / GitLab CI / Tangled |
Every library module must have an .mli file for:
(* lib/user.mli *)
(** User management.
This module provides types and functions for user operations. *)
type t
(** A user. *)
val create : name:string -> email:string -> t
(** [create ~name ~email] creates a new user. *)
val name : t -> string
(** [name u] is the user's name. *)
val pp : t Fmt.t
(** [pp] is a pretty-printer for users. *)
Documentation style:
[name args] is/does ...[name] is ...For modules with a central type t:
type t
val v : ... -> t (* pure constructor *)
val create : ... -> (t, Error.t) result (* constructor with I/O *)
val pp : t Fmt.t (* pretty-printer - required *)
val equal : t -> t -> bool (* equality *)
val compare : t -> t -> int (* comparison *)
val of_json : Yojson.Safe.t -> (t, string) result
val to_json : t -> Yojson.Safe.t
Required: .ocamlformat in project root.
version = 0.28.1
Run dune fmt before every commit.
Each module using logging should declare a source:
let log_src = Logs.Src.create "project.module"
module Log = (val Logs.src_log log_src : Logs.LOG)
Log levels:
Log.app - Always shown (startup)Log.err - Critical errorsLog.warn - Potential issuesLog.info - InformationalLog.debug - Verbose debuggingRead from ~/.claude/ocaml-config.json:
{
"author": { "name": "Name", "email": "email@example.com" },
"license": "ISC",
"ci_platform": "github",
"git_hosting": { "type": "github", "org": "username" },
"ocaml_version": "5.2.0"
}
Every source file starts with license header:
(*---------------------------------------------------------------------------
Copyright (c) {{YEAR}} {{AUTHOR}}. All rights reserved.
SPDX-License-Identifier: ISC
---------------------------------------------------------------------------*)
project/
├── dune-project
├── dune
├── .ocamlformat
├── .gitignore
├── LICENSE.md
├── README.md
├── lib/
│ ├── dune
│ ├── foo.ml
│ └── foo.mli # Required for every .ml
├── bin/
│ ├── dune
│ └── main.ml
├── test/
│ ├── dune
│ ├── test.ml
│ └── test_foo.ml
├── .github/workflows/ # GitHub Actions
├── .gitlab-ci.yml # GitLab CI
└── .tangled/workflows/ # Tangled CI
(lang dune 3.21)
(name project_name)
(source (tangled handle/project_name)) ; or (github user/repo)
(license ISC)
(authors "Name <email>")
(generate_opam_files true)
(license ISC)
(authors "Name <email@example.com>")
(maintainers "Name <email@example.com>")
(source (tangled user.domain/project_name))
(package
(name project_name)
(synopsis "Short description")
(description "Longer description")
(depends
(ocaml (>= 5.2))
(alcotest (and :with-test (>= 1.7.0)))))
Source options:
(source (tangled handle/repo)) - Tangled hosting (default for monopam)(source (github user/repo)) - GitHub hosting(source (gitlab user/repo)) - GitLab hostingNote: Don't add (version ...) - added at release time.
For projects hosted on tangled.org, use the succinct source stanza:
(source (tangled user.domain/project-name))
Examples:
(source (tangled anil.recoil.org/ocaml-brotli))(source (tangled user.example.org/my-library))For projects hosted on tangled.org, create .tangled/workflows/build.yml:
when:
- event: ["push", "pull_request"]
branch: ["main"]
engine: nixery
dependencies:
nixpkgs:
- shell
- stdenv
- findutils
- binutils
- libunwind
- ncurses
- opam
- git
- gawk
- gnupatch
- gnum4
- gnumake
- gnutar
- gnused
- gnugrep
- diffutils
- gzip
- bzip2
- gcc
- ocaml
- pkg-config
steps:
- name: opam
command: |
opam init --disable-sandboxing -a -y
- name: repo
command: |
opam repo add aoah https://tangled.org/anil.recoil.org/aoah-opam-repo.git
- name: deps
command: |
opam install . --confirm-level=unsafe-yes --deps-only
- name: build
command: |
opam exec -- dune build
- name: test
command: |
opam install . --confirm-level=unsafe-yes --deps-only --with-test
opam exec -- dune runtest --verbose
| Field | Description |
|---|---|
when | Trigger conditions: event (push/pull_request) and branch |
engine | Build engine, use nixery for Nix-based builds |
dependencies.nixpkgs | List of Nix packages to include |
environment | Global or per-step environment variables |
steps | Build steps with name and command |
Per-step environment variables:
steps:
- name: test
environment:
MY_VAR: value
command: |
echo $MY_VAR
See templates/ directory for:
dune-project.templatedune-root.templateci-github.ymlci-gitlab.ymlci-tangled.ymlgitignoreocamlformatLICENSE-ISC.mdLICENSE-MIT.mdREADME.template.md