lmer
lmer is a containerized harness for running Claude Code — or an alternative agent harness — against external repositories.
It clones a target repo into a sandbox container, mounts a known set of development rules and slash commands, and hands the session to the agent with a task-shaped prompt. Tests, pre-commit, and git operations all run inside the container, so the host stays clean and credentials never leak into the target repo's working tree.
Claude Code is the default and most fully integrated harness. Codex and pi are supported at a core tier — select one with --harness <name> or LMER_HARNESS; see docs/HARNESSES.md for the capability matrix and per-harness setup.
Only chat ships as a built-in task in taskdef/. Additional tasks can be loaded from the work-repo or from directories listed in LMER_TASKDEF_PATHS — see docs/TASKDEFS.md.
Note on GitLab vs GitHub support: Up to this point the priority has been making lmer behave nicely against GitLab — the
gitlab-reviewtoolset does a lot of heavy lifting to smooth that integration. GitHub generally works, but with this GitHub release the next focus is bringing GitHub interaction up to the same level of polish. Expect rough edges on the GitHub side until that work lands.
What you get
- A reproducible container image (Oracle Linux 9 Slim, FIPS-capable) with
claude(plus thecodexandpiharnesses),git,uv, and a pinned Python toolchain pre-installed. - A consistent ruleset (
AGENTS.md+rules/) injected into Claude's system prompt so behavior is the same across projects: tests before commits, no force-push, show command output, pytest-only, etc. - Gate commands (
gate-check,gate-commit,gate-push) that enforce those rules at commit-time. - Task-specific workflows that load instructions, set up the work environment, and (optionally) drive a follow-up review cycle on the resulting MR.
Quick Start
The lmer CLI provides a safe, containerized environment for AI-assisted development. The typical setup uses an existing Claude.ai subscription (no API key required) and installs from the prep-release branch for the latest features.
1. Authenticate Claude on the host (subscription users)
If you already use Claude Code with a Claude.ai subscription, run /login once on your host machine:
claude # then run /login inside the Claude Code session
This creates ~/.claude/.credentials.json, which lmer automatically mounts into the container — no API key needed.
If you instead authenticate via API key, place it in ~/.lmer/.env (see API key alternative below).
2. Install lmer
Pick the branch that matches your needs:
# Latest features — may be unstable
uv tool install lmer --from git+https://github.com/lmer2/lmer@prep-release
# Stable release
uv tool install lmer --from git+https://github.com/lmer2/lmer@main
# Local / editable (developer mode)
uv tool install -e lmer --from .
To upgrade later: uv tool upgrade lmer.
The container image is pulled automatically on first run from ghcr.io/lmer2/lmer — no manual build step is needed. If you can't reach GHCR, or want to develop on the container itself, build it locally from a clone:
git clone https://github.com/lmer2/lmer /tmp/lmer-src
lmer build --local /tmp/lmer-src
Override the pull source with LMER_REGISTRY=<host>/<path> for self-hosted registries.
3. Configure a work repo
lmer requires a work repo — a git repository it uses to persist worklogs, gate-check results, and per-project notes across sessions. The work CLI inside the container actively pulls and pushes to it, so a remote git repository is required (a bind-mounted local directory would have nothing to push to).
Set up:
-
Create an empty repository on a git host you can read/write — GitLab (cloud or self-hosted) or GitHub both work. A personal/private repo is fine.
-
Point
lmerat it and supply a token the container will use for clone/pull/push, via~/.lmer/.env:mkdir -p ~/.lmer cat >> ~/.lmer/.env <<'EOF' LMER_WORK_REPO=git@github.com:<you>/lmer-work.git # Dedicated work-repo token. Provider-agnostic — works for GitLab, # GitHub, or self-hosted. Highest-priority lookup for the work repo, # so it stays isolated from any per-host tokens used to clone the # target repos you point `lmer chat` at. LMER_WORK_REPO_TOKEN=<your-pat> EOF
The token needs read + write on the work repo. lmer will rewrite the SSH URL to HTTPS-with-token automatically when a token is present.
SSH instead of HTTPS works too — keep the
git@…URL, drop the token line, and forward an SSH agent into the container (lmer prints the exact commands when no key is configured).
Subsequent lmer chat <repo> runs will clone the work repo into the container at /work and push updates back at the end of the session.
Common options
Targets can be a repo URL, a PR/MR/issue URL (lmer extracts the base repo), a local git path, or omitted to infer from the current directory's git remote:
# Remote repo
lmer chat https://gitlab.com/group/project.git
# GitHub PR or GitLab MR/issue URL — base repo is auto-extracted
lmer chat https://github.com/org/repo/pull/123
lmer chat https://gitlab.com/group/project/-/merge_requests/456
lmer chat https://gitlab.com/group/project/-/issues/31
# Newer GitLab serves issues at /-/work_items/<id> — also supported (treated as the issue)
lmer chat https://gitlab.com/group/project/-/work_items/70
# Local checkout (extracts remote URL)
lmer chat /path/to/repo
# Infer from current directory
cd /path/to/repo && lmer chat
Other useful flags:
# Checkout specific branch or ref
lmer chat https://gitlab.com/group/project.git --branch feature/x
# Get shell access
lmer chat https://gitlab.com/group/project.git --exec -- bash
# Match host UID for SSH agent permissions
lmer chat https://gitlab.com/group/project.git --match-uid --exec -- bash
See docs/LMER-CLI.md for complete CLI documentation.
API key alternative
If you don't have a Claude subscription, place your API key in ~/.lmer/.env:
mkdir -p ~/.lmer
cat > ~/.lmer/.env <<'EOF'
CLAUDE_API_KEY=your-claude-key
GITLAB_TOKEN=your-gitlab-token
EOF
See docs/AUTHENTICATION.md for the full authentication reference.
See docs/CONTAINER.md for detailed container documentation.
Why Oracle Linux 9 / FIPS?
The container is built on oraclelinux:9-slim-fips. This base satisfies a FIPS 140-2 compliance requirement: cryptographic operations (TLS, hashing for signed commits, etc.) inside the container must run against a validated module. Oracle Linux 9 ships a validated OpenSSL and exposes /proc/sys/crypto/fips_enabled, which make fips-check verifies.
If you do not need FIPS, the image still works fine — it just trades a smaller base layer for a validated one. Slimmer Debian/Alpine-based images are not currently supported out of the box; the Containerfile is the single source of truth and contributions for an opt-in alternative base are welcome.
Developer setup
For working on lmer itself (rather than just using it), clone the repo and build from source:
git clone https://github.com/lmer2/lmer ~/Agents/global
cd ~/Agents/global
# Container workflow (FIPS-compliant)
make build # Build the container image
make lmer # Run lmer with Claude in a fresh container
make test # Run tests locally
make help # See all targets
# Or local Python environment
uv sync
uv run pre-commit install
Path note:
~/Agents/globalis the conventional host checkout location. Hooks and scripts inside the container refer to/Agents/global— that's the in-container mount path, populated by lmer at start. You do not need to create/Agents/globalon the host.
Rules outside lmer
Rules are injected automatically when running via lmer (which uses libexec/claude-runner.sh internally). For standalone claude sessions, pass the rules file explicitly:
claude --append-system-prompt-file ~/Agents/global/AGENTS.md
User-specific additions: create ~/.lmer/AGENTS.md — the runner appends it after the workspace config automatically.
Releasing
Releases to PyPI are driven by SSH-signed git tags on lmer2/lmer. The full pipeline lives in .github/workflows/release.yml; the full flow (topology, signing model, error paths) is documented in docs/RELEASE-FLOW.md.
To cut a release:
-
Bump
versioninpyproject.toml(e.g.0.1.0→0.1.1). -
Update
CHANGELOG.yamlwith the release notes. -
Commit and merge to
mainthrough the normal PR flow. -
From an up-to-date
main, create a signed tag at its head and push it:git switch main && git pull --ff-only git -c gpg.format=ssh -c user.signingkey=<key> tag -s v0.1.1 <sha> git push origin v0.1.1
Tag from an up-to-date
main, never from a side branch or a stale checkout. Tags are signed so history carries evidence of who cut a release — but note that CI does not verify the signature: the workflow publishes what any pushedv*tag names, and what actually restricts that is the mirror'sv*tag ruleset plus the required reviewer on thepypienvironment (see below). -
Watch the
Release to PyPIworkflow in the Actions tab. Its four jobs run in order:- checks — the repository's own CI workflow, on the tagged commit. A tag on a red commit fails here before anything is built.
- build — verifies the tag version matches
pyproject.toml, builds the wheel and sdist withuv build, then runs two pre-publish gates: the build-backend constraint must have bound, and the distributions must pass the publish action's own twine. Everything up to here holds no token. - publish-pypi — deploys to the
pypienvironment, so it pauses for the required reviewer's approval, then publishes with Trusted Publishing (no stored secrets) and PEP 740 attestations. There is noskip-existing: an upload refused because the version already exists is the version-reuse gate, so a re-run cannot publish over a released version. - github-release — creates the GitHub Release with the artifacts attached, only after the upload was accepted.
If a job fails, recover with "Re-run failed jobs" (
gh run rerun <run-id> --failed) — never a full re-run, which after a successful publish can no longer converge.
Prerequisites (one-time)
- A PyPI Trusted Publisher binding must exist for project
lmerpointing at workflowrelease.ymlonlmer2/lmer, environmentpypi. Configure at https://pypi.org/manage/project/lmer/settings/publishing/. - A GitHub environment named
pypimust exist onlmer2/lmer(Settings → Environments). - That
pypienvironment must carry a deployment tag-pattern policy (v*refs only) and a required reviewer. These are what authorize a release now that CI no longer verifies tag signatures, and they live in repository settings where a tag-borne workflow cannot rewrite them. - A
v*tag ruleset onlmer2/lmer, with a bypass entry for the release bot. The workflow fires on anyv*tag push, so this ruleset is the gate on who may trigger a release at all.
These are set up by the maintainer; they don't need to be touched per release.
Documentation
See docs/INDEX.md for the full documentation index. Common starting points: LMER-CLI.md, CONTAINER.md, AUTHENTICATION.md, TASKDEFS.md.
The rules Claude follows live in AGENTS.md and rules/ (split by topic: git, testing, code-quality, security, documentation, ci-cd, dependencies). Rule-reading and commit-gate commands (rgr, rgr-git, rgrpc, pc, …) are documented in hooks/README.md.
Metadata
Release files for lmer 0.9.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| lmer-0.9.0.tar.gz | 2.5 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| lmer-0.9.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 3.6 MB
Release files / lmer-0.9.0.tar.gz
| Download URL | lmer-0.9.0.tar.gz |
|---|---|
| Size | 2.5 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
194fd9ba835d14875bfdec674b6c6e60a43a7819b9fac1545e256b187322c6b1
|
|
BLAKE2b-256 checksum How to use checksums |
ccb457ce4271bccf663c8dfa27b51518fdd2154b90e14ff1c75eb9197504d587
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 3, 2026.
Transparency logRelease files / lmer-0.9.0-py3-none-any.whl
| Download URL | lmer-0.9.0-py3-none-any.whl |
|---|---|
| Size | 1.1 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fcc05f026b557985fcbfaab052c92e48afcec27dd79a9d26cfef597b76258cb8
|
|
BLAKE2b-256 checksum How to use checksums |
03aeafd2b2f4acbbc779a76fee555fc5d051a09cb9ad0b45d22a89a927d74474
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 3, 2026.
Transparency log