Skip to main content

A wrapper around an ML coding CLI: enter at a job, get a named resumable session, optionally driven by a workflow.

Project description


gmlw

A metering wrapper around your ML coding CLI

Run claude, cursor, codex, or vibe exactly as you know them — but on a job you tag, in a named, resumable session, with every turn's tokens and cost recorded. An application that uses an ML client, not an ML client pretending to be an application.


License: Apache 2.0 CI Coverage Python

client: claude client: cursor client: codex client: vibe


Why a wrapper  •  The job  •  Install  •  Workflows  •  What it records  •  Design



Why a wrapper

The raw coding CLIs are black boxes. A session has no name you chose, so you can't find it again. A run's cost is invisible — you never learn what a task actually spent. The exact context a session launched with is gone the moment it starts. And every client does all of this differently, so nothing you learn about one transfers to the next.

gmlw is a thin, deterministic shell around the client that keeps the client's behaviour intact and adds the parts an application needs:

  • Identity — you enter at a job you tag; the wrapper mints a named, resumable session on the client.
  • Metering — a local relay sits between the client and its upstream API and records tokens and cost per turn, for any metered client, without changing a single thing about how the client runs.
  • Provenance — the compiled context a session launched with is written to disk; opt in to a full transcript and every call's request, response, and usage is kept too.
  • One surface — claude, cursor, codex, and vibe are all driven the same way and land in the same ledger.

The judgment stays in the client. The deterministic parts — session identity, launch, context compilation, persistence, metering — are Python you can read.

gmlw export — per-turn tokens and cost for a job

The job

A job is the one concept everything hangs off. It's the piece of work you're tagging — a ticket, a refactor, an investigation — and it is the primary key of the whole ledger.

job  ──►  sessions  ──►  turns ──► tokens + cost
                    └───► context.md (what it launched with)
                    └───► transcript (opt-in: in / out / usage per call)

You start work at a job and read it back by job:

gmlw start REFACTOR-42                 # mint + launch a session on the default client
gmlw start REFACTOR-42 --resume-latest # pick the latest session back up
gmlw jobs                              # every job with recorded activity
gmlw sessions REFACTOR-42              # that job's sessions, oldest first
gmlw export REFACTOR-42                # per-turn tokens + cost, totalled by model

Nothing about the client changes — you get its full TUI. The wrapper owns the identity, the launch, and the persistence around it.

Install

Requires Python 3.11–3.13 and uv. The console script is gmlw.

uv tool install generic-ml-wrapper     # or: uv sync --extra dev  (from a clone)
gmlw start MY-FIRST-JOB                # first run self-seeds ~/.gmlw (owner-only)

On first run the wrapper creates ~/.gmlw/ (mode 0700) with a commented config.toml, a SQLite ledger, and the workflow/profile/rule folders. Pick the client per run with --client claude|cursor|codex|vibe, or set a default in config.toml.

Workflows

A workflow is a small operating context you author once and launch a job with. Rather than re-explaining the same standing instructions to the client every time, you compile them once — a base, your profile, global rules, and the workflow's own steps — into the context the session opens with.

gmlw workflow new doc-review           # author a workflow (an authoring session, kept apart from work)
gmlw workflow list                     # the runnable workflows
gmlw start DOCS-1 --workflow doc-review # launch a job with that context compiled in

Workflows can carry their own credentials (gmlw creds set <workflow> <ENV_VAR>), injected into the child process at launch and stored 0600. Context compilation runs through an interceptor chain, so a step like context compression is an opt-in plug-in, not a fork of the engine.

What it records

Everything lives under ~/.gmlw/, owner-only, on your machine:

Artifact Where What
Ledger ledger.db (SQLite, WAL) jobs, sessions, per-turn token usage, per-session cost
Context contexts/<job>/<session>.context.md the exact compiled context the session launched with
Transcript (opt-in) transcripts/<job>/<session>/call_NNN.{in.json,out.sse,usage.json} every call's request, response, and usage — a portable, self-contained folder

For Claude Code, the wrapper renders a rich status line straight into the client's own status bar — the git branch, folder, model, context %, and live cost, plus the job's running total. (The status-line seam is client-agnostic; Claude is the one wired today.)

the gmlw status line rendered for claude and cursor

Develop

The gates are defined once in noxfile.py; CI is a thin caller of them, so what runs locally is byte-for-byte what runs in CI.

uv sync --extra dev          # or: nox -s dev   (builds the IDE .venv)
nox                          # lint · imports · typecheck · tests (3.11–3.13)
nox -s green                 # the whole gate in one env (lint · format · imports · pyright · coverage)

Strict ruff + pyright (over src and tests), import-linter hexagon contracts, and an 80% coverage floor all run in the gate. Every change goes on a branch (feature/… tech/… fix/… docs/… chore/… test/…), keeps nox -s green passing, and merges via PR — direct pushes to main are blocked by branch protection.

See docs/DESIGN.md for the architecture, SECURITY.md for the threat model, and CONTRIBUTING.md to get set up.

Family

Part of the generic-ml-* family, alongside generic-ml-cache (record/replay execution cache — the wrapper's context compressor records through it) and generic-ml-workflow.

License

Apache-2.0 — see LICENSE and NOTICE. Contributions welcome; see CONTRIBUTING.md.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

generic_ml_wrapper-0.1.0.tar.gz (382.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

generic_ml_wrapper-0.1.0-py3-none-any.whl (117.7 kB view details)

Uploaded Python 3

File details

Details for the file generic_ml_wrapper-0.1.0.tar.gz.

File metadata

  • Download URL: generic_ml_wrapper-0.1.0.tar.gz
  • Upload date:
  • Size: 382.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for generic_ml_wrapper-0.1.0.tar.gz
Algorithm Hash digest
SHA256 69d73dea0efc52fe43a5d53e9874b060df9891b05112a73fdcc9dbdbbbc2a16e
MD5 d4e855cada485593ced20be2e66a650f
BLAKE2b-256 3574dd9cb6d6ef55dd6cbb798b881bc5d41ed743c7cd20266aa832a3c602084d

See more details on using hashes here.

File details

Details for the file generic_ml_wrapper-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: generic_ml_wrapper-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 117.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for generic_ml_wrapper-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8c4a30a3bd573ec2ed9af96230f47078c2417707bef3b2cacded70fdb7c2f99d
MD5 7040fa76f5e27bde54e6d5d2412d3990
BLAKE2b-256 1cce961814eb2d2b4aca3e3f5b91ec9b1606344477ddf4a6dc6103addc1227fe

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page