Make the CLI reachable without PATH: python -m control_plane - #14
Merged
Merged
Conversation
pip installs the console script into the interpreter's scripts directory and cannot put that directory on PATH -- no wheel can, since wheels have no install-time hooks. On Windows with the Python Install Manager this is the default outcome: only the python.exe shim is on PATH, so `control-plane` installs successfully and is then not found. Add __main__.py so `python -m control_plane` works as a documented fallback, and derive argparse's prog from argv[0] so a user who cannot reach the console script is not told to run it -- `control-plane ui` previously printed a hint naming the very command that was missing. README gains two subsections under Install: what to do when `control-plane` isn't found (pipx / uv / module form / the sysconfig one-liner that prints the directory to add), and what to expect after a Python upgrade -- both forms stop working, since installs belong to one interpreter, and a hand-added scripts directory is usually version-scoped. Quick start keeps the console script as the primary form; the equivalence is stated once at the top rather than doubling every example. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EMQxc7WjBTqdUJu18LAskK
susheem-k
added a commit
that referenced
this pull request
Sep 13, 2026
0.2.0 is the version people actually install, and it has neither the __main__.py nor the README guidance from #14. Both exist for someone who has just run `pip install` and found no `control-plane` command -- and that person gets the PyPI wheel and the PyPI project page, so until this ships the fix reaches only people installing from a clone, who never had the problem. Patch, not minor: purely additive. [project.scripts] is untouched and the console script's output is byte-identical -- prog only differs for the module form, which is new in this release. RELEASING.md described required reviewers on the `pypi` environment as optional. They are now configured on all three AgentPlane repos, so the upload pauses for approval; say so, and say why it matters (PyPI versions are immutable, so that prompt is the last point a bad build can be stopped). Verified against the built artifacts, not just the source tree: wheel and sdist pass `twine check` (which also confirms the edited README renders on PyPI), __main__.py is packaged, entry_points.txt is unchanged, and a clean venv installed from the wheel reports 0.2.1 and runs `python -m control_plane`. Claude-Session: https://claude.ai/code/session_01EMQxc7WjBTqdUJu18LAskK Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
pip install agentplane-control-planesucceeds, thencontrol-planeis not found.This is not a bug we can fix in the package. pip writes the console script into the
interpreter's scripts directory and cannot add that directory to
PATH— wheels haveno install-time hooks, and
setup.py's hooks don't run for wheel installs. It is thedefault outcome on Windows with the Python Install Manager,
which puts only its
python.exeshim onPATHand leaves each interpreter'sScripts\off it. pip's warning about this is easy to miss in install output.Hit while setting up the plane for the mini-SWE-agent governance work.
What this changes
python -m control_planenow works. There was no__main__.py, so the documentedfallback for "not on PATH" didn't exist — only
python -m control_plane.cli, which isan internal path no one would guess.
Hints follow the invocation.
progis derived fromargv[0], so--helpand theuisubcommand name whichever form you used. Previouslycontrol-plane uiprintedRun: control-plane serve --port 8800— telling a user to run the exact command theyhad just failed to run.
README gains two subsections under Install:
control-planeisn't found —pipx/uv tool(which own a bin directoryalready on
PATH), the module form, and asysconfigone-liner that prints thedirectory to add by hand.
break after an upgrade; a hand-added scripts directory is usually version-scoped
(
...\pythoncore-3.14-64\Scripts) and needs updating too.Quick start still leads with
control-plane. The equivalence is stated once near thetop rather than doubling every example — the console script is the better UX when it
works, and the fallback only needs to be findable.
Tests
tests/test_cli_entrypoints.py(5 cases): the module form runs, names itself in usage,emits hints in its own form, and
_prog()is checked against bothargv[0]shapes.Suite 42 → 47, all passing.
ruff formatclean on touched files. The one pre-existingruff checkerror (ui/store_client.py:9, unusedos) is onmainalready and leftalone.
Non-scope
No packaging or entry-point changes —
[project.scripts]is untouched and the consolescript behaves exactly as before. Nothing here affects the Docker image, whose
entrypoint stays
control-plane serve.🤖 Generated with Claude Code
https://claude.ai/code/session_01EMQxc7WjBTqdUJu18LAskK