Implement `commitlens authors` — a report of per-author activity.
Deliverable
Module: `src/commitlens/authors.py` exposing `register(subparsers)` and `run(args, *, repo_root)` per the convention in `cli.py`. Register it in `_register_subcommands`.
Output schema (the dict returned by `run`)
```json
{
"_render": "",
"authors": [
{"name": "Alice", "email": "alice@example.com", "commits": 47, "lines_added": 1203, "lines_removed": 415, "first_commit": "2025-09-01T10:00:00+00:00", "last_commit": "2026-04-29T18:30:00+00:00"},
...
]
}
```
Sort by commit count descending. Aggregate by `(name, email)` pair (do NOT collapse different emails for the same name).
Human render
A fixed-width table. Columns: author (name + email), commits, +lines, -lines, first, last. Truncate emails over 30 chars with an ellipsis. Cap to top 30 by default (no flag for v1).
Data source
Use `git log --pretty=format:%an|%ae|%aI --numstat` and parse the alternating header / numstat lines. Skip binary entries (numstat shows `-\t-` for binaries).
Tests
`tests/test_authors.py` using the `git_repo` fixture. Build a repo with at least 3 distinct authors, varying numbers of commits, including one binary file commit. Assert:
- author count, ordering by commits desc
- lines_added / lines_removed totals match what's in the fixture
- binary commits don't blow up parsing
- first_commit / last_commit are correct
Implement `commitlens authors` — a report of per-author activity.
Deliverable
Module: `src/commitlens/authors.py` exposing `register(subparsers)` and `run(args, *, repo_root)` per the convention in `cli.py`. Register it in `_register_subcommands`.
Output schema (the dict returned by `run`)
```json
{
"_render": "",
"authors": [
{"name": "Alice", "email": "alice@example.com", "commits": 47, "lines_added": 1203, "lines_removed": 415, "first_commit": "2025-09-01T10:00:00+00:00", "last_commit": "2026-04-29T18:30:00+00:00"},
...
]
}
```
Sort by commit count descending. Aggregate by `(name, email)` pair (do NOT collapse different emails for the same name).
Human render
A fixed-width table. Columns: author (name + email), commits, +lines, -lines, first, last. Truncate emails over 30 chars with an ellipsis. Cap to top 30 by default (no flag for v1).
Data source
Use `git log --pretty=format:%an|%ae|%aI --numstat` and parse the alternating header / numstat lines. Skip binary entries (numstat shows `-\t-` for binaries).
Tests
`tests/test_authors.py` using the `git_repo` fixture. Build a repo with at least 3 distinct authors, varying numbers of commits, including one binary file commit. Assert: