{"id":"code-dev-py-argparse-build-parser-only","audience":"developers","category":"coding","track":"coding","technique":"constraints","title":"Write an argparse parser from a CLI spec, and nothing more","free":false,"task":"Turn the ledgerctl spec into a build_parser() function using argparse only (no click or typer). Set prog='ledgerctl' and the description exactly 'Summarize ledger exports into a CSV report.'. Parse --since with date.fromisoformat. build_parser must return the parser without calling parse_args or defining main. Imports: only argparse and date. Exactly 1 code block of at most 14 lines, under 20 words of text.","materialLabel":"The CLI spec from the README (attached to your prompt automatically)","material":"## ledgerctl - command line (from README.md, v2 draft)\n\nledgerctl reads a ledger export (CSV) and writes a summary report.\n\n    ledgerctl INPUT [-o OUT] [--since YYYY-MM-DD] [--dry-run]\n\nArguments\n  INPUT               path to the exported ledger CSV (required)\n\nOptions\n  -o, --out OUT       where to write the report. Default: report.csv\n                      (v1 used summary.csv; changed in v2)\n  --since DATE        only include entries on or after this date\n                      (ISO format, e.g. 2026-10-01). Default: all entries\n  --dry-run           print what would be written instead of writing it\n\nExit codes\n  0  success\n  2  bad arguments (argparse handles this)\n\nCurrent code in ledgerctl/cli.py (to be replaced):\n\n    import sys\n    def main():\n        args = sys.argv[1:]\n        if not args:\n            print(\"usage: ledgerctl INPUT\"); sys.exit(2)\n        input_path = args[0]\n        out = \"summary.csv\"\n        if \"-o\" in args:\n            out = args[args.index(\"-o\") + 1]\n        ...\n\nTicket: LED-77, \"replace hand-rolled arg parsing\".\nOwner: Kalani. Python 3.11.","rules":[{"type":"codeBlocks","count":1},{"type":"maxCodeLines","max":14},{"type":"maxProseWords","max":20}],"mistakes":[{"title":"Getting a whole new CLI","why":"The AI writes main(), calls parse_args and wires up the report logic too.","fix":"Say: \"Only build_parser(), which returns the parser. No parse_args, no main.\"","checks":["r2","maxCodeLines"]},{"title":"Missing the exact strings","why":"Help output is user-facing; the AI writes its own description and skips prog.","fix":"Give them: \"prog='ledgerctl', description exactly 'Summarize ledger exports into a CSV report.'\"","checks":["r0"]},{"title":"A string date or old default","why":"The AI leaves --since as a string or picks up v1's summary.csv.","fix":"Add: \"--since type=date.fromisoformat; --out default report.csv.\"","checks":["r1"]},{"title":"A different library","why":"The AI may suggest click or typer, which aren't installed.","fix":"Add: \"argparse only; imports only argparse and date.\"","checks":["r2","codeBlocks","maxProseWords"]}],"promptHints":[{"label":"Names the library","patterns":["argparse","no click"]},{"label":"Gives prog and description","patterns":["prog","description","Summarize ledger exports"]},{"label":"Types the date","patterns":["fromisoformat","type="]},{"label":"Rules out parse_args","patterns":["no parse_args","don'?t (call|parse)","return the parser","no main"]},{"label":"Limits imports","patterns":["only (import|argparse)","imports"]}],"hint":"A parser is easy to test only if it's built separately from running it. Tell the AI the exact strings, the date type and what the function should and shouldn't do.","rubricLabels":["Creates argparse.ArgumentParser(prog=\"ledgerctl\", description=\"Summarize ledger exports into a CSV report.\") with that exact description","Adds a positional input, -o/--out with default \"report.csv\" (not summary.csv), --since with type=date.fromisoformat and default None, and --dry-run with action=\"store_true\"","build_parser() returns the parser and never calls parse_args, there is no main(), and the only imports are argparse and date (from datetime)"]}