Skip to content

CLI: build each command's parser beside its handler - #804

Draft
Deicyde wants to merge 1 commit into
mainfrom
refactor/cli-parsers-beside-handlers
Draft

Deicyde wants to merge 1 commit into
mainfrom
refactor/cli-parsers-beside-handlers

Conversation

@Deicyde

@Deicyde Deicyde commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

Move-only. Each command's parser now sits beside its handler, and main() goes from 640 lines to 79. This is a draft until #795 and #796 merge: both edit main()'s parser blocks, and Git can't carry an edit across a move. Once they merge, the branch is regenerated on main by the same script. Its conflicts are never resolved by hand.

Premise

  • User: maintainers and agents who edit the CLI.
  • Problem: main() in autoform_cli/__main__.py built every top-level command's parser inline. It ran 640 of the file's 2,496 lines, with 47 add_parser and 147 add_argument calls. Each command's parser block sat 652 to 1,783 lines above its first handler, so changing one command meant editing two distant places. The file also changes often: 46 commits in the last 30 days touched main()'s lines.
  • Replaces: the inline blocks. Each one moves, byte for byte, into _add_<command>_parser(subparsers), directly above the command's first handler. main() keeps everything else unchanged: the SIGCHLD reset, the root parser and --version, the builder calls in the old registration order (the order --help lists commands in), the parse, page_workers, and the dispatch. Five whole functions also move, unchanged:
    • _add_claim_board_arguments goes above _add_claim_parser, its only user;
    • _add_probe_timeout_argument goes above _add_skeleton_parser, the first of its three users;
    • _work_assumptions, _work_impact and _debrief_orchestrator go below the handlers that dispatch to them.
  • Deletes: nothing. Every module-level name stays importable under the same name, including the ones tests import or patch through autoform_cli.__main__. No import changes.

Checks

The change was generated by a script, not edited by hand, then checked three ways:

  • Syntax trees: inlining each builder call back into main() gives the same AST as the old main(). Every other top-level statement is unchanged apart from the five whole-function moves. No builder reads a name the old main() bound in another block, reads a local before it is bound, or binds a name main() binds. Result: equivalent.
  • The parser main() builds: captured the way tests/test_skill_examples.py does, so no handler runs. Compared on all 52 command paths: help and usage at 80 columns, prog, exclusive groups, and every action's option strings, dest, nargs, const, default, type, choices, required flag, help and metavar, with AUTOFORM_WORKER_ID set so the defaults that read it show. No path differs. This covers the check, render and work assumptions help that the generated workflows grep.
  • Lint and tests: ruff check autoform_cli servers tests (ruff 0.15.17) passes. Run directly, since load was over the limit for pytest, these pass: test_documented_autoform_commands_parse, test_cli_reference_documents_only_commands_that_exist, the version tests in test_release.py, and test_ui_boundary.py. CI runs the rest; no test changes.

To review, read git diff --histogram --color-moved beside this. Of its 718 removed lines, all but three show as moved: two ) lines and a blank line at block edges. Git's default algorithm counts 730 removed lines and leaves six of them unmarked. Each of those six has an identical unmarked twin on the added side, so it's an artifact of the diff, not a change. The only new lines are the 19 def _add_<command>_parser(subparsers: argparse._SubParsersAction) -> None: headers and the 19 calls. GitHub's diff shows no move markers.

No CHANGELOG line: nothing users run or import changes.

After this merges

A PR that edits a command's options edits its _add_<command>_parser. A PR that is already open and edits main()'s old parser region does one of two things: it lands first, and this branch is regenerated, or it reapplies its hunk inside the builder (same lines, same indentation).

Posted by Claude Code, at Jack's request.

main() built all 19 top-level commands' parsers inline: 640 lines, each block 652 to 1,783 lines above the handler it feeds. Each block now moves, byte for byte, into _add_<command>_parser(subparsers), placed directly above the command's first handler, and main() calls the builders in the same order. The two shared argument helpers move above their first builder, and _work_assumptions, _work_impact and _debrief_orchestrator move below the handlers that dispatch to them. main() goes from 640 to 79 lines. Nothing else changes: with the builders inlined, main() has the same syntax tree as before, and the parser main() builds is the same on all 52 command paths.
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Nov 13, 2026
@Deicyde Deicyde mentioned this pull request Nov 14, 2026
2 tasks done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant