gitpro 0.3.4__tar.gz → 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. {gitpro-0.3.4 → gitpro-0.4.0}/PKG-INFO +30 -24
  2. {gitpro-0.3.4 → gitpro-0.4.0}/README.md +29 -23
  3. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/__init__.py +1 -1
  4. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/agents.py +24 -2
  5. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/args.py +10 -0
  6. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/audit.py +17 -6
  7. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/cli.py +243 -26
  8. gitpro-0.4.0/src/gitpro/commitcheck.py +113 -0
  9. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/completion.py +2 -2
  10. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/config.py +3 -3
  11. gitpro-0.4.0/src/gitpro/deprecation.py +36 -0
  12. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/discover.py +40 -9
  13. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/guard.py +8 -1
  14. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/hot.py +2 -1
  15. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/jsonfile.py +13 -4
  16. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/jsonout.py +3 -0
  17. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/keys.py +3 -2
  18. gitpro-0.4.0/src/gitpro/learn.py +116 -0
  19. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/mcp.py +89 -120
  20. gitpro-0.4.0/src/gitpro/pin.py +166 -0
  21. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/plan.py +11 -6
  22. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/resolve.py +149 -19
  23. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/scan.py +117 -9
  24. gitpro-0.4.0/src/gitpro/setup.py +668 -0
  25. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/view.py +22 -3
  26. gitpro-0.3.4/src/gitpro/pin.py +0 -89
  27. gitpro-0.3.4/src/gitpro/setup.py +0 -260
  28. {gitpro-0.3.4 → gitpro-0.4.0}/.gitignore +0 -0
  29. {gitpro-0.3.4 → gitpro-0.4.0}/LICENSE +0 -0
  30. {gitpro-0.3.4 → gitpro-0.4.0}/pyproject.toml +0 -0
  31. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/__main__.py +0 -0
  32. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/approval_policy.py +0 -0
  33. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/change_contract.py +0 -0
  34. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/changes.py +0 -0
  35. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/clone.py +0 -0
  36. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/devcontainer.py +0 -0
  37. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/doctor.py +0 -0
  38. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/envcmd.py +0 -0
  39. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/example.toml +0 -0
  40. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/files.py +0 -0
  41. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/forge.py +0 -0
  42. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/health.py +0 -0
  43. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/incident.py +0 -0
  44. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/lint.py +0 -0
  45. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/lost.py +0 -0
  46. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/operation_contract.py +0 -0
  47. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/operation_io.py +0 -0
  48. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/operation_reconcile.py +0 -0
  49. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/operations.py +0 -0
  50. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/policy.py +0 -0
  51. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/prompt.py +0 -0
  52. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/render.py +0 -0
  53. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/report.py +0 -0
  54. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/scanroots.py +0 -0
  55. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/trailers.py +0 -0
  56. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/tui.py +0 -0
  57. {gitpro-0.3.4 → gitpro-0.4.0}/src/gitpro/undo.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: gitpro
3
- Version: 0.3.4
3
+ Version: 0.4.0
4
4
  Summary: Per-remote git identities (name, email, SSH key, commit signing) from one TOML file
5
5
  Project-URL: Homepage, https://gitpro.dev
6
6
  Project-URL: Source, https://github.com/Yantrakar/gitpro
@@ -48,26 +48,38 @@ Install from [PyPI](https://pypi.org/project/gitpro/) with pipx.
48
48
 
49
49
  ```sh
50
50
  pipx install gitpro
51
- gitpro setup --write
51
+ gitpro setup
52
52
  ```
53
53
 
54
- The wizard helps you choose identities, Git hosts and owners, existing SSH keys,
55
- and optional signing. **No TOML editing is needed for basic setup.** Review the
56
- configuration before saving; nothing is applied to Git or SSH yet.
57
-
58
- ```sh
59
- gitpro plan # see exactly what would change
60
- gitpro apply -y # explicitly apply the reviewed changes
54
+ Setup offers to look through your repos and proposes your identities. Accept,
55
+ edit or answer the questions yourself; **no TOML editing is needed.** One
56
+ preview shows everything, including the commit guard and, with Claude Code, its
57
+ hook and MCP server. One yes applies it all. Setup never changes your repos.
58
+
59
+ ```mermaid
60
+ flowchart LR
61
+ S["gitpro setup"] --> HC{"Config <br/>exists?"}
62
+ HC -->|no| L["Read your repos, <br/>SSH config and <br/>editor recents, <br/>if you agree"]
63
+ L --> Q["Propose <br/>identities; you <br/>accept or edit"]
64
+ Q --> I
65
+ HC -->|yes| I["Inspect config, <br/>Git and SSH files, <br/>keys, guard, <br/>agent hook, MCP"]
66
+ I -->|all current| OK["Nothing <br/>to do"]
67
+ I -->|something to do| PV["One preview, <br/>each part with <br/>its undo"]
68
+ PV --> ASK{"Apply <br/>all?"}
69
+ ASK -->|no| NONE["Nothing <br/>written"]
70
+ ASK -->|yes| AP["Write it, print <br/>the public keys <br/>to register"]
71
+ AP --> NX["Next: <br/>gitpro scan"]
61
72
  ```
62
73
 
63
- Then, inside a repository:
74
+ Then:
64
75
 
65
76
  ```sh
66
- gitpro why # see the expected identity and anything overriding it
77
+ gitpro why # inside a repository: the expected identity and anything overriding it
78
+ gitpro scan # repos that need a fix, with the command for each
67
79
  ```
68
80
 
69
- Already have a config? Run `gitpro setup --write` to add another identity without losing
70
- your existing settings or comments. Prefer editing files? The
81
+ Already have a config? `gitpro setup` brings this machine up to date with it.
82
+ `gitpro setup -n` only shows what is left to do. Prefer editing files? The
71
83
  [user guide](docs/guide.md#configuration) covers the full TOML format.
72
84
 
73
85
  ## What you get
@@ -79,20 +91,17 @@ your existing settings or comments. Prefer editing files? The
79
91
 
80
92
  Gitpro generates ordinary Git and SSH configuration; it needs no background
81
93
  service. Changes can be previewed, existing hand-written files are backed up,
82
- and key creation or uploads are never part of the setup wizard.
94
+ and key uploads are never part of setup.
83
95
 
84
96
  ## Using VS Code?
85
97
 
86
- Run **gitpro: Set Up Identities** from the Command Palette to open the same wizard
87
- in a terminal. The extension also shows the current identity and helps find
88
- repository-level overrides. [Install the extension](vscode/README.md).
98
+ The extension shows the current identity and helps find repository-level
99
+ overrides. It runs the same commands as the terminal. [Install the extension](vscode/README.md).
89
100
 
90
101
  ## Go further when you need to
91
102
 
92
- For agents, [the versioned workflow](docs/agent-workflows.md) provides structured
93
- preflight, identity-scoped execution and durable idempotency for commits, pushes
94
- and GitHub PRs. Persistent changes use exact proposals, human approval by default,
95
- optional operator-scoped preauthorization, and recoverable apply. MCP execution is opt-in.
103
+ AI agents get the same answers through the MCP server and the Claude Code hook:
104
+ see [agent workflows](docs/agent-workflows.md).
96
105
 
97
106
  | I want to… | Start here |
98
107
  |---|---|
@@ -103,9 +112,6 @@ optional operator-scoped preauthorization, and recoverable apply. MCP execution
103
112
  | Look up a command or undo changes | [Command reference](docs/guide.md#commands) · [Undo](docs/guide.md#undo-everything) |
104
113
  | Troubleshoot or contribute | [Troubleshooting](docs/guide.md#troubleshooting) · [Development](docs/guide.md#development) |
105
114
 
106
- The detailed TOML, YAML and command examples live in the guide, not on the
107
- getting-started path.
108
-
109
115
  ## Problems and questions
110
116
 
111
117
  Run `gitpro report` to draft an issue with the versions and a redacted
@@ -22,26 +22,38 @@ Install from [PyPI](https://pypi.org/project/gitpro/) with pipx.
22
22
 
23
23
  ```sh
24
24
  pipx install gitpro
25
- gitpro setup --write
25
+ gitpro setup
26
26
  ```
27
27
 
28
- The wizard helps you choose identities, Git hosts and owners, existing SSH keys,
29
- and optional signing. **No TOML editing is needed for basic setup.** Review the
30
- configuration before saving; nothing is applied to Git or SSH yet.
31
-
32
- ```sh
33
- gitpro plan # see exactly what would change
34
- gitpro apply -y # explicitly apply the reviewed changes
28
+ Setup offers to look through your repos and proposes your identities. Accept,
29
+ edit or answer the questions yourself; **no TOML editing is needed.** One
30
+ preview shows everything, including the commit guard and, with Claude Code, its
31
+ hook and MCP server. One yes applies it all. Setup never changes your repos.
32
+
33
+ ```mermaid
34
+ flowchart LR
35
+ S["gitpro setup"] --> HC{"Config <br/>exists?"}
36
+ HC -->|no| L["Read your repos, <br/>SSH config and <br/>editor recents, <br/>if you agree"]
37
+ L --> Q["Propose <br/>identities; you <br/>accept or edit"]
38
+ Q --> I
39
+ HC -->|yes| I["Inspect config, <br/>Git and SSH files, <br/>keys, guard, <br/>agent hook, MCP"]
40
+ I -->|all current| OK["Nothing <br/>to do"]
41
+ I -->|something to do| PV["One preview, <br/>each part with <br/>its undo"]
42
+ PV --> ASK{"Apply <br/>all?"}
43
+ ASK -->|no| NONE["Nothing <br/>written"]
44
+ ASK -->|yes| AP["Write it, print <br/>the public keys <br/>to register"]
45
+ AP --> NX["Next: <br/>gitpro scan"]
35
46
  ```
36
47
 
37
- Then, inside a repository:
48
+ Then:
38
49
 
39
50
  ```sh
40
- gitpro why # see the expected identity and anything overriding it
51
+ gitpro why # inside a repository: the expected identity and anything overriding it
52
+ gitpro scan # repos that need a fix, with the command for each
41
53
  ```
42
54
 
43
- Already have a config? Run `gitpro setup --write` to add another identity without losing
44
- your existing settings or comments. Prefer editing files? The
55
+ Already have a config? `gitpro setup` brings this machine up to date with it.
56
+ `gitpro setup -n` only shows what is left to do. Prefer editing files? The
45
57
  [user guide](docs/guide.md#configuration) covers the full TOML format.
46
58
 
47
59
  ## What you get
@@ -53,20 +65,17 @@ your existing settings or comments. Prefer editing files? The
53
65
 
54
66
  Gitpro generates ordinary Git and SSH configuration; it needs no background
55
67
  service. Changes can be previewed, existing hand-written files are backed up,
56
- and key creation or uploads are never part of the setup wizard.
68
+ and key uploads are never part of setup.
57
69
 
58
70
  ## Using VS Code?
59
71
 
60
- Run **gitpro: Set Up Identities** from the Command Palette to open the same wizard
61
- in a terminal. The extension also shows the current identity and helps find
62
- repository-level overrides. [Install the extension](vscode/README.md).
72
+ The extension shows the current identity and helps find repository-level
73
+ overrides. It runs the same commands as the terminal. [Install the extension](vscode/README.md).
63
74
 
64
75
  ## Go further when you need to
65
76
 
66
- For agents, [the versioned workflow](docs/agent-workflows.md) provides structured
67
- preflight, identity-scoped execution and durable idempotency for commits, pushes
68
- and GitHub PRs. Persistent changes use exact proposals, human approval by default,
69
- optional operator-scoped preauthorization, and recoverable apply. MCP execution is opt-in.
77
+ AI agents get the same answers through the MCP server and the Claude Code hook:
78
+ see [agent workflows](docs/agent-workflows.md).
70
79
 
71
80
  | I want to… | Start here |
72
81
  |---|---|
@@ -77,9 +86,6 @@ optional operator-scoped preauthorization, and recoverable apply. MCP execution
77
86
  | Look up a command or undo changes | [Command reference](docs/guide.md#commands) · [Undo](docs/guide.md#undo-everything) |
78
87
  | Troubleshoot or contribute | [Troubleshooting](docs/guide.md#troubleshooting) · [Development](docs/guide.md#development) |
79
88
 
80
- The detailed TOML, YAML and command examples live in the guide, not on the
81
- getting-started path.
82
-
83
89
  ## Problems and questions
84
90
 
85
91
  Run `gitpro report` to draft an issue with the versions and a redacted
@@ -1,4 +1,4 @@
1
1
  """Per-remote git identities generated from one TOML file (see cli.py)."""
2
2
  from __future__ import annotations
3
3
 
4
- __version__ = "0.3.4"
4
+ __version__ = "0.4.0"
@@ -34,7 +34,7 @@ MAX_SCAN = 1024 * 1024 # the regex scan reads at most this much
34
34
  MAX_FOR = 20 # `for x in w1 .. wN` with more words: the body is judged once, x unknown
35
35
  MAX_VALUE = 4096 # a longer expanded $VAR value counts as unknown
36
36
  ALIAS_TIMEOUT = 3 # seconds for the `git config --get alias.X` lookup on the hook path
37
- TRIGGERS = ("override", "pin_stale", "no_identity", "env_override")
37
+ TRIGGERS = ("ambiguous", "override", "pin_stale", "no_identity", "env_override")
38
38
  SKIP_ENV = "GITPRO_SKIP"
39
39
  BYPASS_HELP = (f"{SKIP_ENV}=1 in the environment the agent starts with turns the check off for that session; "
40
40
  "[agents] hook = \"warn\" or \"off\" in the config changes it for good. The deny message never "
@@ -1322,6 +1322,8 @@ def _judge_one(c: Call, rep, cfg, own: set, pushed_to: Optional[str] = None) ->
1322
1322
  from .resolve import redact_url
1323
1323
  url = redact_url(next(iter(rep.effective.remotes.values()), None)) or top
1324
1324
  headline = f"no identity matches this repo ({url}) and [default] has no email"
1325
+ elif any(p.code == "ambiguous" for p in triggers):
1326
+ headline = f"this {what} runs in a repo whose remotes belong to different identities"
1325
1327
  elif c_bad or a_bad:
1326
1328
  email, verb = (committer, "committed") if c_bad else (author, "authored")
1327
1329
  email = email or "(no email)"
@@ -1812,11 +1814,31 @@ def _edit(args: argparse.Namespace, removing: bool) -> int:
1812
1814
  view.step("added .claude/settings.local.json to .git/info/exclude so it is not committed")
1813
1815
  view.ok(f"removed the gitpro hook from {shown}" if removing else
1814
1816
  f"installed; {title} picks it up on its next session")
1815
- restores = f"restores from {undo.tilde(res.backup)}" if res.backup else f"removes {shown}"
1817
+ restores = f"restores from {undo.tilde(res.backup)}" if res.backup else f"removes {shown} unless it changed since"
1816
1818
  print(f"↩ undo: sh {undo.tilde(res.undo)} ({restores})")
1817
1819
  return 0
1818
1820
 
1819
1821
 
1822
+ HOOK_AGENTS = ("claude", "cursor", "gemini")
1823
+
1824
+
1825
+ def plugin_enabled(home: Path) -> Optional[str]:
1826
+ """The enabled gitpro plugin in ~/.claude/settings.json (it ships the hook and the MCP server), or None."""
1827
+ t = target("claude", "user", home, None)
1828
+ return _plugin(_read_lenient(t.path)) if t.path.is_file() else None
1829
+
1830
+
1831
+ def hook_change(agent: str, home: Path) -> Optional[str]:
1832
+ """For gitpro setup: what `agent install --agent AGENT` would do at user scope ("create PATH" or
1833
+ "update PATH"), or None when the hook is in place. ConfigError for a file gitpro must not edit."""
1834
+ from . import undo
1835
+ t = target(agent, "user", home, gitpro_path())
1836
+ obj = _precheck(t.path, t, _snippet(t))
1837
+ if obj is not None and add_hook(copy.deepcopy(obj), t) == obj:
1838
+ return None
1839
+ return f"{'update' if obj is not None else 'create'} {undo.tilde(t.path)}"
1840
+
1841
+
1820
1842
  def install(args: argparse.Namespace) -> int:
1821
1843
  """`gitpro agent install`."""
1822
1844
  return _edit(args, removing=False)
@@ -43,7 +43,17 @@ def confirm() -> argparse.ArgumentParser:
43
43
  return p
44
44
 
45
45
 
46
+ def dry_run(p: argparse.ArgumentParser) -> None:
47
+ """-n/--dry-run for a command that already previews without -y (pin, scan, audit; spec 22 R4)."""
48
+ p.add_argument("-n", "--dry-run", action="store_true", help="show only, change nothing (as without -y)")
49
+
50
+
46
51
  def preview_only(args: object) -> bool:
52
+ if getattr(args, "dry_run", False):
53
+ if getattr(args, "yes", False):
54
+ from .config import ConfigError
55
+ raise ConfigError("choose one: -n shows the change, -y makes it")
56
+ return True
47
57
  return not (getattr(args, "yes", False) or getattr(args, "write", False))
48
58
 
49
59
 
@@ -7,6 +7,7 @@ keeping every date; it never runs `rebase`, never touches pushed commits, the in
7
7
  """
8
8
  from __future__ import annotations
9
9
 
10
+ import argparse
10
11
  import os
11
12
  import re
12
13
  import shlex
@@ -18,7 +19,7 @@ from pathlib import Path
18
19
  from typing import Dict, List, Optional, Sequence, Set, Tuple
19
20
 
20
21
  from . import config, jsonout, resolve, trailers, undo
21
- from .args import capability
22
+ from .args import capability, dry_run, preview_only
22
23
  from .config import Config, ConfigError
23
24
 
24
25
  PROG = "gitpro"
@@ -31,7 +32,7 @@ FORMAT = "%H%x00%an%x00%ae%x00%cn%x00%ce%x00%cI%x00%P%x00%s%x00%(trailers:only,u
31
32
  # Options --range may contain besides revisions; anything else (--output=..., say) is refused.
32
33
  RANGE_OPTIONS = ("--not", "--all", "--remotes", "--branches", "--tags", "--first-parent", "--no-merges",
33
34
  "--merges", "--since", "--until", "--after", "--before", "--max-count")
34
- _CAUSES = ("url_form", "override", "pin_stale", "global_override", "not_applied", "env_override")
35
+ _CAUSES = ("ambiguous", "url_form", "override", "pin_stale", "global_override", "not_applied", "env_override")
35
36
 
36
37
 
37
38
  @dataclass
@@ -715,9 +716,11 @@ def _preview(data: dict, exp) -> List[str]:
715
716
 
716
717
  def cmd_audit(args) -> int:
717
718
  as_json = jsonout.wants_json(args)
719
+ args.rewrite = args.rewrite or getattr(args, "rewrite_flag", False)
718
720
  if args.rewrite and getattr(args, "agents", False):
719
721
  raise ConfigError("--rewrite needs the author audit; drop --agents")
720
- if args.rewrite and as_json and not args.yes:
722
+ show_only = bool(args.rewrite and args.dry_run and preview_only(args))
723
+ if args.rewrite and as_json and not args.yes and not show_only:
721
724
  raise ConfigError("--json needs -y with --rewrite (there is no terminal to confirm on)")
722
725
  home = Path(args.home).expanduser().resolve()
723
726
  path, how = config.find(args.config, home)
@@ -746,6 +749,12 @@ def cmd_audit(args) -> int:
746
749
  print()
747
750
  print("\n".join(_preview(data, exp)))
748
751
  n = data["rewrite"]["commits"]
752
+ if show_only:
753
+ if as_json:
754
+ jsonout.emit("audit", dict(_public(data), dry_run=True))
755
+ else:
756
+ print(f"Dry run: nothing written. Rewrite the {n} unpushed commit{'s' * (n != 1)} with -y.")
757
+ return 0 if data["ok"] else 1
749
758
  if not _confirm(f"rewrite {n} unpushed commit{'s' * (n != 1)}?", args.yes):
750
759
  print("✗ aborted, nothing written")
751
760
  return 1
@@ -773,7 +782,7 @@ def cmd_audit(args) -> int:
773
782
  def register(sub, common, confirm) -> None:
774
783
  sp = sub.add_parser(
775
784
  "audit", parents=[common, confirm],
776
- help="find commits made as the wrong identity or with an AI agent; fix unpushed ones with --rewrite",
785
+ help="find commits made as the wrong identity or with an AI agent; fix unpushed ones with --fix",
777
786
  description="Audit the history of a repo: commits whose committer (or your own other email as author) is "
778
787
  "not the repo's identity, commits made with an AI agent (Co-Authored-By trailers, bot "
779
788
  "emails) and the [*.trailers] policy. Default: the unpushed commits (as of the last fetch) "
@@ -792,9 +801,11 @@ def register(sub, common, confirm) -> None:
792
801
  w = sp.add_mutually_exclusive_group()
793
802
  w.add_argument("--agents", action="store_true", help="only the agent / trailer audit")
794
803
  w.add_argument("--authors", action="store_true", help="only the wrong-author audit")
795
- sp.add_argument("--rewrite", action="store_true",
804
+ sp.add_argument("--fix", dest="rewrite", action="store_true",
796
805
  help="fix the unpushed wrong-author commits (asks first; writes an undo script)")
806
+ sp.add_argument("--rewrite", dest="rewrite_flag", action="store_true", help=argparse.SUPPRESS) # 0.4: --fix
807
+ dry_run(sp)
797
808
  sp.add_argument("--json", action="store_true", help="machine-readable output")
798
809
  sp.add_argument("-v", "--verbose", action="store_true", help="list every commit, not the first ten")
799
810
  sp.set_defaults(func=cmd_audit)
800
- capability("audit", "audit.rewrite", "trailers")
811
+ capability("audit", "audit.rewrite", "audit.fix", "trailers")