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  •  Clients  •  The job  •  Install  •  Workflows  •  What it records  •  Guides  •  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

Clients at a glance

Four clients, one surface — but they are not identical. What each supports today:

Client Metering Resume Status line Context delivery
claude (claude) native --append-system-prompt-file
cursor (cursor-agent) context-file instruction
codex (codex) initial instruction
vibe (vibe) initial instruction

Cursor isn't metered by the open-source wrapper (its usage isn't on an interceptable API); Codex and Vibe don't resume. See docs/CLIENTS.md for the per-client detail and setup.

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.14 and uv, plus one supported coding CLI already installed and logged in (claude, cursor-agent, codex, or vibe). 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 and Cursor, 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 and Cursor are wired today, and other parsers can be added.)

the gmlw status line rendered for claude and cursor

Your operating context, carried across clients

Beyond metering, gmlw builds a portable operating context that follows you from one client to the next — none of it locked inside a single tool:

  • Profile & company — who you are and your project's conventions (profile/me, profile/company), composed into every session.
  • Learned notebook — what your tools notice about how you work, in one file they all mirror into; negatives ("what to avoid") are first-class.
  • Personas — a selectable tone with a free, local greeting at launch (gmlw persona list).
  • Rules — corrections you've demanded, captured as reusable reflexes (~/.gmlw/rules/*.rule.md).
  • Mode-aware packaging — a [startup.<mode>] matrix decides which sources compose for a plain session, a workflow, or authoring, with optional typed compression.
  • Plugins & overrides — swap a client's caller by id (gmlw plugins list, [callers]).

Workflows (above) are optional and separate from this personal layer. Every listing/reporting command also speaks --json for automation.

What gmlw does not do

  • It is not a sandbox — it launches the real client with your credentials (see SECURITY.md).
  • It does not meter Cursor, and does not resume Codex or Vibe (see the matrix).
  • It does not call models itself — the optional context compressor records through generic-ml-cache.
  • It ships no compression prompts — the compressor stays inert until you configure one.

Guides

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.14)
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.6.0.tar.gz (553.5 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.6.0-py3-none-any.whl (249.1 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: generic_ml_wrapper-0.6.0.tar.gz
  • Upload date:
  • Size: 553.5 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.6.0.tar.gz
Algorithm Hash digest
SHA256 67dbd7255787892c65525c993876f98264a50c7995e893197d6ce098528582d1
MD5 4b4c2ce49e059923e74f16fa888c4740
BLAKE2b-256 38c516bf9f51b4e4b1f2ffa92638813306173aac46e1052b329bdbd33d5701f7

See more details on using hashes here.

File details

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

File metadata

  • Download URL: generic_ml_wrapper-0.6.0-py3-none-any.whl
  • Upload date:
  • Size: 249.1 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.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 cadeebdd5be074a7517fe1d4167d4cfd66ee1c90ff590aa647f86a47a0c8e48d
MD5 e347134cd824a064722eb397b4755006
BLAKE2b-256 23e410a7c7c4c12bdb2602fad7d3653514ce3ac9294c8ceeb7479b82f15c8f91

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