Skip to content

CLI Reference

The PyCodeCommenter command-line interface provides three subcommands: generate, validate, and coverage. This page documents every argument and flag for each, derived directly from cli.py.

The entry point is installed as pycodecommenter when you run pip install pycodecommenter.

Jump to a subcommand

pycodecommenter generate

Reads a Python source file or directory, generates Google-style docstrings for every undocumented function and class, and either prints, saves, or patches the result. Given a directory, it recursively collects every .py file (skipping the built-in and --excluded paths described below) and processes each one the same way it would a single file.

If a function or class already has a docstring, the existing content is merged, not discarded. The existing summary, parameter descriptions, and return description are preserved and used as the base; only missing sections are filled in.

Usage

pycodecommenter generate <file-or-directory> [options]

Arguments

Argument Required Description
file Yes Path to a Python source file, or a directory to process recursively

Options / Flags

Flag Type Default Description
-i, --inplace flag off Modify the file(s) in place. Without this flag or --output, the result is printed to stdout
-o, --output string none Write the result to this file path instead of stdout. Single-file targets only — errors out if the target is a directory
--dry-run flag off Print a unified diff of what would change and exit. Does not write any files. Exits with code 1 if there are changes, 0 if not
--backup flag off Before modifying a file in place, copy it to <file>.bak. Has no effect without --inplace (a warning is printed)
-e, --exclude list .pycodecommenter.yaml's top-level exclude list, if set Patterns to exclude, added to the built-in defaults (directory targets only). Built-in defaults: __pycache__, .git, .venv, venv, env, .tox, .nox, __pypackages__, site-packages, build, dist, .eggs, .egg-info, .mypy_cache, .pytest_cache, node_modules
--ai-draft flag off Have an AI model draft the parts the code can't state: function summaries and argument, return and exception descriptions, and class summaries and Attributes: entries, that would otherwise be TODO markers or type-only. Only gaps are filled; every drafted line is labelled (AI-drafted, unreviewed)
--ai-provider choice hosted Where --ai-draft sends code: hosted (free, daily limit, no key), or your own key with gemini (GEMINI_API_KEY), openai (OPENAI_API_KEY), anthropic (ANTHROPIC_API_KEY), deepseek (DEEPSEEK_API_KEY) or openai-compatible (OPENAI_COMPATIBLE_API_KEY). Install the SDK with pip install "pycodecommenter[gemini]", [openai] or [anthropic]
--ai-model string per provider The model to use. Defaults: gemini-3.8-flash (with gemini-3.5-flash-lite, then gemini-2.5-flash, tried in turn if Google says an earlier one is unavailable to your key; a Gemini model you name yourself is never replaced), gpt-6-luna, claude-sonnet-5-5, deepseek-flash; these are only defaults, and any model your key can access works. Required for openai-compatible
--ai-base-url string none API endpoint for openai-compatible (e.g. Mistral, Groq, or a local Ollama server)
--accept-ai-drafts flag off Required with --ai-draft --inplace: an explicit acknowledgment that unreviewed AI drafts are written to your files
--yes-send-code-to-ai flag off Record consent for the chosen provider without asking (CI), and skip the "Continue?" question before a directory run. --yes-send-code-to-hosted-ai still works as an alias
--max-drafts integer none With --ai-draft, send at most N requests in the whole run (one per function or class with gaps). The rest keep their TODO markers and the summary says how many were not tried

Note: An --exclude pattern matches a path component (directory or file name) exactly; a dot-prefixed pattern (e.g. .egg-info) also matches a component it's a suffix of (covers the <name>.egg-info convention). This is exact-component matching, not a substring check against the whole path — rebuild_index.py is not skipped just because it contains build, and environment_config.py is not skipped just because it contains env. (The coverage command's -e/--exclude, documented below, adds one more rule on top of these two — see its note.)

End-of-run summary

Every generate run ends with a summary on stderr (stdout carries only generated code, so generate app.py > out.py stays clean):

Summary: 4 docstrings would be written.
  3 details taken straight from the code
  5 gaps left as "TODO(pycodecommenter)" for you to fill
Next: fill in the gaps, or add --ai-draft to have them drafted; then run `pycodecommenter validate`.

It counts docstrings written, updated (your text kept) or already complete; details taken straight from the code; lines drafted by AI; docstrings taken from the comment above a definition; and gaps left. For a directory, the numbers cover the whole run. AI status messages and consent prompts also go to stderr. On a terminal, --ai-draft also shows a live status line while each request is in flight ([3/12] product_service.py - drafting create_product (hosted), or waiting 30 s for the rate limit), so a slow first request from the free hosted service does not look like a hang. It is not shown when stderr is redirected or in CI.

Before a directory run with --ai-draft, the tool counts the requests it would make (nothing is sent to find out) and prints, for example, AI drafting: 32 files, 121 functions and classes have gaps to draft (one request each). In a terminal it then asks Continue? [y/N] (the default is no), so running it in the wrong directory does not quietly spend your allowance. --yes-send-code-to-ai or a run without a terminal skips the question, and --max-drafts caps the total.

Why gaps can remain after an AI run. The summary says so: parts the AI declined to write (the code did not make them clear), requests that failed (network or service error), functions and classes not tried because drafting stopped (a spent limit or --max-drafts), and any not sent because their source looks like it holds a secret. Nothing that looks like a key, password or token is ever sent.

Output modes (mutually used in order)

  1. If --dry-run is given, always print a diff (per file, for a directory target) and exit. No other flag matters.
  2. If --inplace is given, write back to the original file(s) (and create .bak per file if --backup is set).
  3. If --output <path> is given (without --inplace, single-file targets only), write to the specified path.
  4. If none of the above, print the result to stdout (per file, prefixed with # File: <path>, for a directory target).

Examples

Preview what would be added (safe, no writes):

pycodecommenter generate mymodule.py --dry-run

Write docstrings directly into the file:

pycodecommenter generate mymodule.py --inplace

Create a backup first, then patch:

pycodecommenter generate mymodule.py --inplace --backup

Write to a separate output file:

pycodecommenter generate mymodule.py --output mymodule_documented.py

Patch every file in a directory, excluding migrations:

pycodecommenter generate src/ --inplace --backup --exclude migrations

Exit Codes

Code Meaning
0 Success — file(s) processed (or, in --dry-run mode, no changes detected)
1 Parse error — a file could not be read or parsed (in --dry-run mode: changes were detected). For a directory target, any file failing to parse is enough to exit 1.

pycodecommenter review

Go through what needs a person's attention after generate, one item at a time.

Usage

pycodecommenter review <file_or_directory> [--list] [--exclude PATTERN ...]

What it asks about

Item Choices
A line drafted by AI ((AI-drafted, unreviewed)) accept (keeps the text, removes the label), edit (type your own wording), skip
A gap (TODO(pycodecommenter): describe) fill (type the text), skip
A # comment the docstring below it now repeats yes to remove it, or no (the default) to keep it

When a function has two or more AI-drafted lines, review first shows its whole docstring, and offers A (capital) to accept all of that function's remaining AI lines at once, after you have read them together. It covers that one function only; gaps and comments are still asked one by one, and there is deliberately no option to accept everything in a file, so removing the label always means a person looked at the text.

q quits at any point; what you decided so far is saved.

Safety

  • Only docstring lines change, plus comment blocks you said yes to removing.
  • Before a file is saved, it's checked to still parse and to have exactly the same code (only docstrings and comments may differ). If either check fails, the file is left unchanged.
  • Typed text can't contain triple quotes or backslashes (they would break the docstring); you're asked again.
  • The file's line endings are kept.

Options / Flags

Flag Type Default Description
--list flag off Only list what needs review; change nothing. This is also what happens without a terminal (e.g. in CI)
--exclude list .pycodecommenter.yaml's exclude Patterns to exclude (directory targets only)

Example

$ pycodecommenter review app.py --list
app.py:2  AI-drafted  in add(): Add two numbers.
app.py:6  gap  in add(): b (Any): TODO(pycodecommenter): describe.

1 AI-drafted line, 1 gap, 0 repeated comments to review in 1 file.

pycodecommenter validate

Reads a Python source file or directory and checks every function and class docstring for consistency with the actual code. Given a directory, it recursively validates every .py file it finds (skipping the same built-in and --excluded paths generate does).

Usage

pycodecommenter validate <file-or-directory>

Arguments

Argument Required Description
file Yes Path to a Python source file, or a directory to validate recursively

Options / Flags

Flag Type Default Description
-e, --exclude list .pycodecommenter.yaml's top-level exclude list, if set Patterns to exclude, added to the built-in defaults (directory targets only) — same matching rules and defaults as generate's -e/--exclude, above
--fail-on-todo flag off Exit with code 1 if any docstring still holds a TODO(pycodecommenter) marker or other placeholder text (by default these are only warnings)
--fail-on-ai-draft flag off Exit with code 1 if any docstring still holds an unreviewed (AI-drafted, unreviewed) line (see review)
--output-format text | json text Output format. text prints one human-readable report per file. json prints a single JSON array of per-file report objects to stdout for a directory target (a single object for a single-file target).

What the validator checks

Six categories of checks are run on every documented function:

  1. Signature matching — every parameter in the function signature must appear in the Args: section, and vice versa.
  2. Type consistency — if a parameter has a type annotation and is not in the docstring, that is flagged.
  3. Exception documentation — if the function body contains raise statements, a Raises: section is expected (Google or Sphinx style).
  4. Return documentation — if the function has a return <value> statement, a Returns: section is expected, and vice versa.
  5. Format compliance — the docstring must have a summary line; non-standard section headers are flagged.
  6. Content quality — placeholder text (TODO, FIXME, Description of, etc.), very short summaries, and duplicate parameter descriptions are flagged.

Output format

Text mode (default):

============================================================
VALIDATION REPORT
============================================================
File: mymodule.py
Coverage: 75.0%
Total Issues: 3
  - Errors: 1
  - Warnings: 2
  - Info: 0

ISSUES:

[ERROR] mymodule.py:12:process_data: Parameter 'timeout' is not documented in docstring
  → Suggestion: Add 'timeout' to the Args section

JSON mode (--output-format json):

{
  "file": "mymodule.py",
  "stats": {
    "total": 3,
    "errors": 1,
    "warnings": 2,
    "info": 0,
    "coverage_percentage": 75.0
  },
  "issues": [
    {
      "line": 12,
      "severity": "ERROR",
      "check": "signature",
      "message": "Parameter 'timeout' is not documented in docstring"
    }
  ]
}

Examples

Validate a single file:

pycodecommenter validate src/api.py

Use in a shell script with exit code check:

pycodecommenter validate src/api.py && echo "All good"

Validate an entire directory:

pycodecommenter validate src/

Exit Codes

Code Meaning
0 No ERROR-level issues found (warnings and info do not trigger a non-zero exit)
1 One or more ERROR-level issues were found. For a directory target, this is true if any file in the tree has an ERROR-level issue.

pycodecommenter coverage

Analyzes documentation coverage for a Python file or an entire directory tree.

Usage

pycodecommenter coverage <path> [options]

Arguments

Argument Required Description
path Yes A Python file or a directory to analyze recursively

Options / Flags

Flag Type Default Description
-e, --exclude list none One or more patterns to exclude, added to the built-in defaults (they don't replace them). Built-in defaults when not provided: __pycache__, .git, .venv, venv, env, .tox, .nox, __pypackages__, site-packages, build, dist, .eggs, .egg-info, .mypy_cache, .pytest_cache, node_modules, tests, test_
--output-format text | json text Output format. text prints the human-readable coverage table. json prints a machine-readable JSON object to stdout.
--strict flag off Count a function or class as documented only if its docstring holds no TODO(pycodecommenter) placeholder and no unreviewed AI-drafted line. By default any non-empty docstring counts, so a project of generated stubs reads as 100%
--fail-below float none (or coverage.threshold from .pycodecommenter.yaml, if set) Exit with code 1 if the overall coverage percentage is below THRESHOLD
--badge-output path none Write a shields.io endpoint-badge JSON file for the coverage percentage to PATH

Note: An --exclude pattern matches a path component (directory or file name) exactly; a dot-prefixed pattern (e.g. .egg-info) also matches a component it's a suffix of, and an underscore-suffixed pattern (e.g. test_) also matches a component it's a prefix of. They are not glob expressions, and they are not arbitrary substring matches against the full path either — matching is per path component.

Output

Directory mode prints a per-file table followed by a project total:

================================================================================
DOCUMENTATION COVERAGE REPORT
================================================================================
[OK] api.py                                                 100.0%
[!!] utils.py                                                50.0%
[!!] models.py                                                0.0%
--------------------------------------------------------------------------------
TOTAL                                                        60.0%
================================================================================

Single file mode prints one line:

Coverage for src/api.py: 87.5%

--badge-output writes a shields.io endpoint-badge JSON file, independent of --output-format (the text/JSON console output above is unaffected):

{
  "schemaVersion": 1,
  "label": "docs coverage",
  "message": "78%",
  "color": "green"
}

color is brightgreen at ≥90%, green at ≥75%, yellow at ≥50%, and red below that.

Examples

Analyze a whole project:

pycodecommenter coverage .

Analyze only the src subdirectory:

pycodecommenter coverage ./src

Exclude test and migration files:

pycodecommenter coverage . --exclude tests migrations

Analyze a single file:

pycodecommenter coverage src/api.py

Exit Codes

Code Meaning
0 Analysis complete, and either --fail-below was not given or coverage was at or above the threshold
1 --fail-below THRESHOLD was given and the overall coverage percentage is below it

Global behaviour

If pycodecommenter is called with no subcommand, or with an unrecognised subcommand, it prints the top-level help message and exits with code 0.

pycodecommenter
# prints usage and subcommand list

--version prints the installed version and exits 0:

pycodecommenter --version

  • Getting Started — walkthrough with real examples
  • Python API — programmatic access to the same functionality
  • Recipes — complete CI/CD and pre-commit integrations