Out-of-tree subcommands for the m-cli toolchain.
m-cli's core stays focused on the canonical 5 commands
(fmt/lint/test/coverage/doc). m-cli-extras is the bucket
for niche, opinionated, or third-party-flavored utilities that
shouldn't bloat core but are still worth having a place for.
Plugins register against m-cli via the m_cli.plugins Python
entry-point group. After installing this package, each plugin shows
up as a regular m <name> subcommand:
$ pip install m-cli-extras # or: uv add m-cli-extras
$ m plugins
m-cli plugin API v1
Registered plugins (1):
m corpus-stats (m-cli-extras 0.1.0)
| Command | What it does |
|---|---|
m corpus-stats |
Walk a directory of .m files, report file / line / label / parse-error counts. |
More to come — see the m-cli-extras roadmap below for the candidate list.
$ m corpus-stats /path/to/corpus
corpus /path/to/corpus
files 1234
total_lines 287654
total_labels 9821
files_with_parse_errors 12
$ m corpus-stats /path/to/corpus --json
{
"corpus": "/path/to/corpus",
"files": 1234,
"total_lines": 287654,
"total_labels": 9821,
"files_with_parse_errors": 12
}
Use cases:
- Sizing rule profiles before lint runs (find out how big a regression gate would be).
- Sanity-checking a corpus acquisition step (did we really get all 39,330 VistA routines?).
- Quick "how big is this codebase" answers without spinning up a full lint pass.
Standard pip / uv install once the package is on PyPI. Until then, install from a clone alongside m-cli:
git clone https://github.com/m-dev-tools/m-cli ~/projects/m-cli
git clone https://github.com/m-dev-tools/m-cli-extras ~/projects/m-cli-extras
cd ~/projects/m-cli-extras
uv sync --extra devpyproject.toml declares m-cli as a sibling path-dep
({ path = "../m-cli", editable = true }). Once m-cli ships its own
wheel via the same release-asset pattern tree-sitter-m uses, this
will switch to a URL pin.
The plugin contract is documented at
m-cli/docs/plugin-development.md.
To add a new subcommand to this package:
-
Create
src/m_cli_extras/<name>/cli.pywith aregister(subparsers)function (seecorpus_stats/cli.pyas the worked example). -
Add the entry-point to
pyproject.toml:[project.entry-points."m_cli.plugins"] <name> = "m_cli_extras.<name>.cli:register"
-
Write tests in
tests/test_<name>.py. -
Update this README's "Shipped subcommands" table.
The integration boundary is the entry-point — that's where m-cli
calls into the plugin. Test the register() function with a fresh
argparse.ArgumentParser per test; you don't need to install the
plugin to test it.
Candidates from the m-dev-tools sprint plan (Tier 6b):
m bench— perf benchmarking m-cli operations against a corpusm diff— semantic AST diff between two.mfilesm migrate <preset>— bulk-apply Phase A formatter translationm audit— license/header audit for a project's.mfiles
These will land incrementally as the need surfaces. Open an issue if you want to prioritize one.
AGPL-3.0 — parity with m-cli and the rest of the m-dev-tools toolchain.