Skip to main content

cpms, the LeanCPMS command line

cpms manages an organization in a LeanCPMS instance from your own machine: live state, sessions, the monthly statement, and the declared state (organization, price list, sites) as YAML documents you can keep in a git repository. It talks to the instance's JSON API over HTTPS with an API key, and the same commands work for people and for AI agents.

This directory builds leancpms-cli, the command without the server. It depends on httpx, pydantic and PyYAML only.

Install

With uv, from PyPI once it is published there:

uv tool install leancpms-cli
cpms --help

Until then, from a GitHub release (a tag cli-v<version>) or straight from the repository:

gh release download cli-v0.1.0 --repo forsslund/leancpms --pattern '*.whl'
uv tool install ./leancpms_cli-0.1.0-py3-none-any.whl
# or
uv tool install "git+https://github.com/forsslund/leancpms#subdirectory=cli"
  • The repository is private for now, so both need read access to it on GitHub; over SSH the URL is git+ssh://git@github.com/forsslund/leancpms#subdirectory=cli.
  • It needs Python 3.14. uv downloads it if your machine lacks it.
  • To get a newer version, run uv tool upgrade leancpms-cli. To pin a version, add @<tag or commit> after the repository URL.
  • pipx install "git+…#subdirectory=cli" works too.
  • Do not install it into an environment that already has the server (leancpms). The server already includes cpms, and both packages carry the same files, so uninstalling one breaks the other.

Configure

Log in with the same e-mail address you use for the instance's portal:

cpms login --url https://cpms.example.com

It prints a code and opens the portal's page for it in your browser (or prints the address only: --no-browser, or no display). Log in there if you are not, check that the page shows the same code, choose your organization and whether the key may act (write) or only look (read), and approve. Your role as a member decides which you may choose: members are read unless the operator, or a colleague with a write key, made them write (cpms members set-role). cpms login waits for that and then saves a personal key, yours and that organization's, as the profile [default] (--profile NAME for another) in ~/.config/cpms/config.toml, readable only by you. Other profiles in the file stay as they were.

cpms whoami      # which key is in use, whose, for which organization
cpms logout      # revokes the key on the server and removes the profile

The key stops working when you are removed as a member, and so does every key you issued with it. Approve a code only if you just ran cpms login yourself: whoever started it gets the key.

Machine keys, for CI and agents

A pipeline or an agent of its own gets a machine key instead, which records who issued it: cpms keys issue --name ci --scope read with your personal write key. It is shown only once. An organization key sees only its own organization. A key you issue lives only as long as your membership: when you leave, it is revoked with your own. An integration that must outlive you gets its key from the operator. Members you add do stay when you leave; cpms members list shows who added whom.

Put the URL and the key in ~/.config/cpms/config.toml (or $XDG_CONFIG_HOME/cpms/, or the path in CPMS_CONFIG), readable only by you (chmod 600):

[default]
url = "https://cpms.example.com"
api_key = "lcp_…"

[test]
url = "http://localhost:8000"
api_key = "lcp_…"

Alternatively, set CPMS_URL and CPMS_API_KEY in the environment, which suits CI and agents. --profile test (or CPMS_PROFILE=test) picks a profile. Without a profile, the environment wins, then [default]. A networked command that has neither exits 2 and says what to set.

Use

cpms                                      # live state: sites, fuse groups, outlets, current per phase
cpms sessions --since 2026-10-01
cpms report statement --month 2026-10     # receipts incl./excl. VAT, sessions without one
cpms manifest export kvartersel > all.yaml
cpms manifest export kvartersel --site odalvagen > sites/odalvagen.yaml
cpms manifest validate sites/odalvagen.yaml   # offline: the schema
cpms manifest plan sites/odalvagen.yaml       # the server: the diff, references, live warnings
cpms manifest apply sites/odalvagen.yaml -m "Fused up to 50 A"
cpms manifest history kvartersel --site odalvagen

cpms --help and cpms manifest --help list the rest.

A repository of manifests

One file per document works well:

organization.yaml
pricelist.yaml
sites/odalvagen.yaml
sites/brotorp.yaml

Edit a file, run validate and plan, commit, then run apply with the commit's reason as -m. The server holds the truth and the files are a way to say things: if someone changed a price in the portal, plan shows that applying your file would undo it, and export brings the file up to date. apply refuses a document that changed on the server since your plan; plan again. Removing anything needs --prune. validate needs no key, so CI can run it on every pull request; plan needs one.

In your editor: the JSON Schema

The documents have a JSON Schema, so an editor can complete field names and flag mistakes as you type. That works in editors that use yaml-language-server: VS Code with Red Hat's YAML extension, Neovim, Helix, Zed. Add this line at the top of each file:

# yaml-language-server: $schema=https://raw.githubusercontent.com/forsslund/leancpms/main/schema/manifest.cpms-v1.schema.json

That URL works once the repository is public; replace main with a tag to pin a version. Until then, use a local copy, written by the command itself and kept in your manifest repository:

cpms manifest schema > cpms-v1.schema.json
# yaml-language-server: $schema=../cpms-v1.schema.json

The path is relative to the YAML file. export does not write the line, so add it again after an export.

The schema checks types, required and unknown fields, allowed values, key spelling, and that amounts are quoted ("4.70", not 4.70). It does not check anything that spans several fields: a budget above the fuse, duplicate keys or ocpp ids, a plan naming a missing tariff, or an unknown time zone. validate and plan check those.

For agents

  • --json works on every command, before or after the subcommand.
  • Exit codes: 0 ok, 1 the action failed (the server said no, the server could not be reached, a plan has errors), 2 usage or configuration.
  • cpms manifest apply never prompts without a TTY. It prints the plan and exits 2 unless you pass --yes. Show the plan to a person first, then apply with --yes -m "why".
  • session … stop|close acts at once, with no confirmation step.
  • Pass the key in the environment or the config file, never on the command line or in a transcript.

Releasing (maintainers)

Raise version in both cli/pyproject.toml and the root pyproject.toml (a test keeps them equal), then tag and push:

git tag cli-v0.1.1 && git push origin cli-v0.1.1

.github/workflows/release-cli.yml builds the wheel and sdist, smoke-tests the wheel, and attaches both to a GitHub release. It also publishes to PyPI when the repository variable PYPI_PUBLISH is true. That needs, once: a PyPI account with two-factor authentication; a pending trusted publisher on PyPI for the project leancpms-cli (owner forsslund, repository leancpms, workflow release-cli.yml, environment pypi); and an environment pypi in the GitHub repository's settings. A pending publisher does not reserve the name: if someone else registers leancpms-cli first, it becomes invalid. The first release creates the project and so reserves the name. Trusted publishing uses GitHub's OIDC token, so no PyPI token is stored anywhere. A file published to PyPI cannot be replaced, only yanked: a wrong release is fixed with a new version.

Metadata

Release files for leancpms-cli 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for leancpms-cli 0.1.0
File Size Uploaded Provenance
leancpms_cli-0.1.0.tar.gz 40.9 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for leancpms-cli 0.1.0
File Interpreter ABI Platform
leancpms_cli-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 81.8 kB

Release files / leancpms_cli-0.1.0.tar.gz

Download URL leancpms_cli-0.1.0.tar.gz
Size 40.9 kB
Tags Source
SHA-256 checksum
How to use checksums
27e608c8e1e1dde576bb2ccadc4a4b628f6455b96a60a57347bda5e88b97f007
BLAKE2b-256 checksum
How to use checksums
16bb88a641a6bd61cd23a84dbbdb1ccefa0e5558988ef9374d999f56ec60cfb8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 23, 2026.

Transparency log

Release files / leancpms_cli-0.1.0-py3-none-any.whl

Download URL leancpms_cli-0.1.0-py3-none-any.whl
Size 40.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9baf4df29198c09ccd0cea091633ef774d93866415ea560ea44b0f3c59cc5f2a
BLAKE2b-256 checksum
How to use checksums
3aae7f80670a7a573c776912b74920395fc742a648dc7b24e82fdc807916d7c6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 23, 2026.

Transparency log

Security for leancpms-cli 0.1.0

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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for leancpms_cli-0.1.0.tar.gz

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 Oct 23, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page