diffbutler 0.5.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 (105) hide show
  1. diffbutler-0.5.0/LICENSE.txt +21 -0
  2. diffbutler-0.5.0/PKG-INFO +170 -0
  3. diffbutler-0.5.0/README.md +142 -0
  4. diffbutler-0.5.0/pyproject.toml +142 -0
  5. diffbutler-0.5.0/pyproject.toml.orig +112 -0
  6. diffbutler-0.5.0/src/diffbutler/__init__.py +1 -0
  7. diffbutler-0.5.0/src/diffbutler/__main__.py +6 -0
  8. diffbutler-0.5.0/src/diffbutler/config/__init__.py +1 -0
  9. diffbutler-0.5.0/src/diffbutler/config/default.toml +91 -0
  10. diffbutler-0.5.0/src/diffbutler/config/diagnostics.py +90 -0
  11. diffbutler-0.5.0/src/diffbutler/config/file.py +30 -0
  12. diffbutler-0.5.0/src/diffbutler/config/instance.py +55 -0
  13. diffbutler-0.5.0/src/diffbutler/config/settings.py +297 -0
  14. diffbutler-0.5.0/src/diffbutler/config/setup.py +92 -0
  15. diffbutler-0.5.0/src/diffbutler/config/transport.py +65 -0
  16. diffbutler-0.5.0/src/diffbutler/core/__init__.py +1 -0
  17. diffbutler-0.5.0/src/diffbutler/core/application.py +103 -0
  18. diffbutler-0.5.0/src/diffbutler/core/context.py +54 -0
  19. diffbutler-0.5.0/src/diffbutler/core/dashboard.py +393 -0
  20. diffbutler-0.5.0/src/diffbutler/core/doctor.py +200 -0
  21. diffbutler-0.5.0/src/diffbutler/core/lifecycle.py +70 -0
  22. diffbutler-0.5.0/src/diffbutler/core/managed.py +441 -0
  23. diffbutler-0.5.0/src/diffbutler/core/native.py +301 -0
  24. diffbutler-0.5.0/src/diffbutler/core/polling.py +118 -0
  25. diffbutler-0.5.0/src/diffbutler/core/process.py +64 -0
  26. diffbutler-0.5.0/src/diffbutler/core/repositories.py +260 -0
  27. diffbutler-0.5.0/src/diffbutler/core/reset.py +34 -0
  28. diffbutler-0.5.0/src/diffbutler/core/resources.py +50 -0
  29. diffbutler-0.5.0/src/diffbutler/core/review.py +331 -0
  30. diffbutler-0.5.0/src/diffbutler/core/setup.py +232 -0
  31. diffbutler-0.5.0/src/diffbutler/core/snapshot.py +70 -0
  32. diffbutler-0.5.0/src/diffbutler/core/sync.py +259 -0
  33. diffbutler-0.5.0/src/diffbutler/core/worker.py +205 -0
  34. diffbutler-0.5.0/src/diffbutler/core/workspaces.py +139 -0
  35. diffbutler-0.5.0/src/diffbutler/domain/__init__.py +1 -0
  36. diffbutler-0.5.0/src/diffbutler/domain/dashboard.py +63 -0
  37. diffbutler-0.5.0/src/diffbutler/domain/diff.py +285 -0
  38. diffbutler-0.5.0/src/diffbutler/domain/forge.py +123 -0
  39. diffbutler-0.5.0/src/diffbutler/domain/lifecycle.py +51 -0
  40. diffbutler-0.5.0/src/diffbutler/domain/polling.py +92 -0
  41. diffbutler-0.5.0/src/diffbutler/domain/process.py +29 -0
  42. diffbutler-0.5.0/src/diffbutler/domain/redaction.py +61 -0
  43. diffbutler-0.5.0/src/diffbutler/domain/resources.py +50 -0
  44. diffbutler-0.5.0/src/diffbutler/domain/review.py +167 -0
  45. diffbutler-0.5.0/src/diffbutler/domain/sync.py +196 -0
  46. diffbutler-0.5.0/src/diffbutler/forges/__init__.py +1 -0
  47. diffbutler-0.5.0/src/diffbutler/forges/base.py +39 -0
  48. diffbutler-0.5.0/src/diffbutler/forges/diagnostics.py +104 -0
  49. diffbutler-0.5.0/src/diffbutler/forges/github/__init__.py +1 -0
  50. diffbutler-0.5.0/src/diffbutler/forges/github/adapter.py +352 -0
  51. diffbutler-0.5.0/src/diffbutler/forges/github/auth.py +49 -0
  52. diffbutler-0.5.0/src/diffbutler/forges/github/http.py +326 -0
  53. diffbutler-0.5.0/src/diffbutler/forges/github/mapping.py +157 -0
  54. diffbutler-0.5.0/src/diffbutler/forges/github/setup.py +86 -0
  55. diffbutler-0.5.0/src/diffbutler/forges/registry.py +23 -0
  56. diffbutler-0.5.0/src/diffbutler/py.typed +0 -0
  57. diffbutler-0.5.0/src/diffbutler/reviewers/__init__.py +1 -0
  58. diffbutler-0.5.0/src/diffbutler/reviewers/base.py +101 -0
  59. diffbutler-0.5.0/src/diffbutler/reviewers/extraction.py +198 -0
  60. diffbutler-0.5.0/src/diffbutler/reviewers/native.py +238 -0
  61. diffbutler-0.5.0/src/diffbutler/reviewers/parsing.py +247 -0
  62. diffbutler-0.5.0/src/diffbutler/reviewers/presets.toml +12 -0
  63. diffbutler-0.5.0/src/diffbutler/reviewers/process.py +78 -0
  64. diffbutler-0.5.0/src/diffbutler/reviewers/prompt.py +148 -0
  65. diffbutler-0.5.0/src/diffbutler/reviewers/redaction.py +30 -0
  66. diffbutler-0.5.0/src/diffbutler/reviewers/resources.py +162 -0
  67. diffbutler-0.5.0/src/diffbutler/sources/__init__.py +1 -0
  68. diffbutler-0.5.0/src/diffbutler/sources/diagnostics.py +100 -0
  69. diffbutler-0.5.0/src/diffbutler/sources/fetch.py +285 -0
  70. diffbutler-0.5.0/src/diffbutler/sources/git.py +461 -0
  71. diffbutler-0.5.0/src/diffbutler/sources/remotes.py +82 -0
  72. diffbutler-0.5.0/src/diffbutler/store/__init__.py +1 -0
  73. diffbutler-0.5.0/src/diffbutler/store/base.py +88 -0
  74. diffbutler-0.5.0/src/diffbutler/store/dashboard.py +201 -0
  75. diffbutler-0.5.0/src/diffbutler/store/database.py +172 -0
  76. diffbutler-0.5.0/src/diffbutler/store/lifecycle.py +247 -0
  77. diffbutler-0.5.0/src/diffbutler/store/migrations/__init__.py +1 -0
  78. diffbutler-0.5.0/src/diffbutler/store/migrations/env.py +14 -0
  79. diffbutler-0.5.0/src/diffbutler/store/migrations/versions/0001_initial.py +147 -0
  80. diffbutler-0.5.0/src/diffbutler/store/migrations/versions/0002_monitoring.py +38 -0
  81. diffbutler-0.5.0/src/diffbutler/store/migrations/versions/0003_reviews.py +72 -0
  82. diffbutler-0.5.0/src/diffbutler/store/migrations/versions/0004_claims.py +22 -0
  83. diffbutler-0.5.0/src/diffbutler/store/migrations/versions/0005_results.py +27 -0
  84. diffbutler-0.5.0/src/diffbutler/store/migrations/versions/0006_anchors.py +17 -0
  85. diffbutler-0.5.0/src/diffbutler/store/migrations/versions/0007_lifecycle.py +36 -0
  86. diffbutler-0.5.0/src/diffbutler/store/migrations/versions/0008_processes.py +30 -0
  87. diffbutler-0.5.0/src/diffbutler/store/migrations/versions/0009_dashboard.py +43 -0
  88. diffbutler-0.5.0/src/diffbutler/store/migrations/versions/0010_run_reasons.py +17 -0
  89. diffbutler-0.5.0/src/diffbutler/store/migrations/versions/__init__.py +1 -0
  90. diffbutler-0.5.0/src/diffbutler/store/models.py +290 -0
  91. diffbutler-0.5.0/src/diffbutler/store/process.py +75 -0
  92. diffbutler-0.5.0/src/diffbutler/store/queue.py +289 -0
  93. diffbutler-0.5.0/src/diffbutler/store/review.py +583 -0
  94. diffbutler-0.5.0/src/diffbutler/ui/__init__.py +1 -0
  95. diffbutler-0.5.0/src/diffbutler/ui/cli/__init__.py +532 -0
  96. diffbutler-0.5.0/src/diffbutler/ui/cli/editor.py +33 -0
  97. diffbutler-0.5.0/src/diffbutler/ui/tui/__init__.py +1 -0
  98. diffbutler-0.5.0/src/diffbutler/ui/tui/_fake_agent.py +47 -0
  99. diffbutler-0.5.0/src/diffbutler/ui/tui/_foreground_exec.py +23 -0
  100. diffbutler-0.5.0/src/diffbutler/ui/tui/app.py +34 -0
  101. diffbutler-0.5.0/src/diffbutler/ui/tui/browse.py +119 -0
  102. diffbutler-0.5.0/src/diffbutler/ui/tui/browser.py +42 -0
  103. diffbutler-0.5.0/src/diffbutler/ui/tui/dashboard.py +926 -0
  104. diffbutler-0.5.0/src/diffbutler/ui/tui/foreground.py +103 -0
  105. diffbutler-0.5.0/src/diffbutler/ui/tui/handoff_probe.py +184 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DiffButler contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,170 @@
1
+ Metadata-Version: 2.4
2
+ Name: diffbutler
3
+ Version: 0.5.0
4
+ Summary: A terminal dashboard for human-directed AI code reviews.
5
+ Keywords: code-review,github,terminal,ai
6
+ License-Expression: MIT
7
+ License-File: LICENSE.txt
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Environment :: Console
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Programming Language :: Python :: 3 :: Only
12
+ Classifier: Programming Language :: Python :: 3.14
13
+ Classifier: Programming Language :: Python :: 3.15
14
+ Classifier: Topic :: Software Development :: Quality Assurance
15
+ Classifier: Typing :: Typed
16
+ Requires-Dist: aiosqlite>=0.22.1
17
+ Requires-Dist: alembic>=1.19.1
18
+ Requires-Dist: httpx>=0.28.1
19
+ Requires-Dist: pydantic>=2.13.5
20
+ Requires-Dist: pydantic-settings>=2.15.0
21
+ Requires-Dist: rich>=15.0.0
22
+ Requires-Dist: sqlalchemy[asyncio]>=2.0.52
23
+ Requires-Dist: textual>=8.2.8
24
+ Requires-Dist: tomlkit>=0.15.1
25
+ Requires-Dist: typer>=0.27.2
26
+ Requires-Python: >=3.14
27
+ Description-Content-Type: text/markdown
28
+
29
+ # DiffButler
30
+
31
+ A terminal dashboard for human-directed AI code reviews on GitHub.
32
+
33
+ Keep track of what each pull request needs next: AI review, your review, or author
34
+ changes. Start an AI review, discuss its findings in the agent's own session, and
35
+ finish your review in the browser.
36
+
37
+ ![DiffButler dashboard showing pull requests and the next review action](diffbutler.png)
38
+
39
+ ## Install
40
+
41
+ Install [uv](https://docs.astral.sh/uv/getting-started/installation/) and Git.
42
+ From a local checkout of this repository, run:
43
+
44
+ ```sh
45
+ uv tool install --python 3.14 .
46
+ ```
47
+
48
+ uv manages Python and dependencies in an isolated environment. No virtual environment
49
+ activation is needed. If `diffbutler` is not on your PATH, run `uv tool update-shell`
50
+ and restart your shell.
51
+
52
+ Install and sign in to Codex CLI or Claude Code before requesting AI reviews.
53
+ The native presets support Codex CLI or Claude Code on Linux and macOS without version checks.
54
+ Reviews use your normal agent settings, including model and reasoning effort, with
55
+ background permission and output overrides. New conversations remain in the agent’s
56
+ own history after DiffButler cleans up its workspaces; existing private sessions
57
+ retain their original home for resume.
58
+ See [supported agents and validation](docs/native-reviews.md) for details.
59
+
60
+ ## Use
61
+
62
+ Use authenticated GitHub access, including for public repositories. If you already
63
+ use `gh auth login`, export its token before launching DiffButler:
64
+
65
+ ```sh
66
+ export GITHUB_TOKEN="$(gh auth token)"
67
+ ```
68
+
69
+ Alternatively, create a [fine-grained personal access token](https://github.com/settings/personal-access-tokens)
70
+ for the repositories you want to review with **Pull requests: Read-only** permission,
71
+ and set it as `GITHUB_TOKEN`. Anonymous access is a last resort for public repositories;
72
+ its low API quota makes it unsuitable for regular polling. See the
73
+ [authentication guide](docs/github.md#authentication-options) for host/account selection,
74
+ HTTPS versus SSH Git setup, and token permissions.
75
+
76
+ From the GitHub repository you want to review, launch:
77
+
78
+ ```sh
79
+ diffbutler
80
+ ```
81
+
82
+ First-run setup detects the local repository, looks up your GitHub username using
83
+ the selected token, asks for your preferred agent, and saves your choices. If username detection
84
+ is unavailable, setup asks for it. Choose the recommended token access for public repositories;
85
+ private repositories require it. Setup asks for the token's environment variable name
86
+ and validates access. It never saves the token.
87
+ New configs silently enable both Codex and Claude when found in `PATH`; setup asks
88
+ only which detected agent should be the default. Missing agents get commented-out
89
+ entries you can enable after installation.
90
+ Later launches reuse your configuration.
91
+
92
+ The dashboard shows active reviews, including followed PRs whose GitHub review
93
+ requests have disappeared after you posted feedback. Waiting-for-author reviews
94
+ stay visible across refreshes and restarts. If an approved PR receives new commits,
95
+ it reappears as `done+` at the end of the queue without starting another AI review.
96
+ Authenticated polling slows when API quota is scarce and speeds back up when it
97
+ recovers, accounting for refresh cost and time until the quota resets.
98
+
99
+ Select a PR with the arrow keys, then work through the review:
100
+
101
+ 1. Optionally press `n` to change the current configured AI reviewer for all PRs.
102
+ Press `r` to start a background AI review. Reviews start only when you request them.
103
+ The agent is instructed to read the PR description, discussion, and earlier review
104
+ threads and replies before reviewing the local code, reporting any unavailable context.
105
+ DiffButler caches full Git history and reviews changes from the PR merge base
106
+ to its head, excluding unrelated changes made later on the target branch.
107
+ 2. A successful review shows `approve` only when the AI explicitly reports no findings;
108
+ otherwise it shows `check`. Press `Enter` to read the results, or `c` to resume
109
+ the saved agent conversation.
110
+ If the agent exits normally but DiffButler cannot parse its output, the task also
111
+ shows `check`; use `c` to inspect the saved conversation or `L` for captured output.
112
+ Changing reviewers leaves that conversation available through `c`; pressing `r`
113
+ starts a new run with the current reviewer and replaces the current review details.
114
+ Ask questions and explicitly tell the agent which comments to post, if any.
115
+ 3. Exit the agent to return to the dashboard, which refreshes activity from GitHub.
116
+ Press `w` if you are waiting for author changes, or `o` to review in your browser.
117
+ 4. After approving on GitHub, press `m` to mark the task complete locally. DiffButler
118
+ also detects your approval, closure, or merge and cleans up owned session resources
119
+ while retaining review history.
120
+
121
+ `Space` performs the selected PR's next action: start a review, discuss findings,
122
+ or open the browser. An AI review never counts as your approval. Background analysis
123
+ uses the agent's [native permission controls](docs/native-reviews.md#native-permissions-and-authentication)
124
+ and read-only review instructions; source sent to the agent may go to its model provider.
125
+
126
+ - `/`: Filter the queue.
127
+ - `a`: Toggle monitoring all open PRs in configured repositories.
128
+ - `b`: Browse accessible PRs and follow one.
129
+ - `R`: Refresh from GitHub.
130
+ - `k`: Stop the selected PR's review and reset its local tracking and owned resources.
131
+ It disappears until a forge refresh rediscovers it; review history is retained.
132
+ - `h`: View completed review history.
133
+ - `?`: Show all keyboard shortcuts.
134
+ - `q`: Quit.
135
+
136
+ See the [dashboard guide](docs/dashboard.md) for all actions and
137
+ [configuration guide](docs/configuration.md) for settings.
138
+
139
+ Your own PRs are hidden from the review queue by default, including direct and team
140
+ requests. Set `include_own_prs = true` in `[ui]` to review them, such as PRs an agent
141
+ created using your account. PRs assigned to you are also hidden by default, even when
142
+ you are requested directly or through a team. Set `include_assigned_prs = true` in
143
+ `[ui]` to include them.
144
+
145
+ ## Reset
146
+
147
+ Close running DiffButler sessions, then run:
148
+
149
+ ```sh
150
+ diffbutler reset
151
+ ```
152
+
153
+ DiffButler refuses a second command or dashboard using the same data home, including
154
+ `reset`, while another instance is running. Its instance lock releases on process
155
+ exit; a leftover PID file does not block startup. See the
156
+ [locking details](docs/configuration.md#single-instance) before handling Git lock files
157
+ or resetting after a crash.
158
+
159
+ After confirmation, this permanently removes DiffButler's standard configuration,
160
+ data, and state directories, including review history, saved sessions, and diagnostic
161
+ logs. Custom files outside those directories are preserved. Use `diffbutler reset --force`
162
+ to skip confirmation. Run `diffbutler` again to repeat first-run setup.
163
+
164
+ ## Contributing
165
+
166
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and checks.
167
+
168
+ ## License
169
+
170
+ [MIT](LICENSE.txt).
@@ -0,0 +1,142 @@
1
+ # DiffButler
2
+
3
+ A terminal dashboard for human-directed AI code reviews on GitHub.
4
+
5
+ Keep track of what each pull request needs next: AI review, your review, or author
6
+ changes. Start an AI review, discuss its findings in the agent's own session, and
7
+ finish your review in the browser.
8
+
9
+ ![DiffButler dashboard showing pull requests and the next review action](diffbutler.png)
10
+
11
+ ## Install
12
+
13
+ Install [uv](https://docs.astral.sh/uv/getting-started/installation/) and Git.
14
+ From a local checkout of this repository, run:
15
+
16
+ ```sh
17
+ uv tool install --python 3.14 .
18
+ ```
19
+
20
+ uv manages Python and dependencies in an isolated environment. No virtual environment
21
+ activation is needed. If `diffbutler` is not on your PATH, run `uv tool update-shell`
22
+ and restart your shell.
23
+
24
+ Install and sign in to Codex CLI or Claude Code before requesting AI reviews.
25
+ The native presets support Codex CLI or Claude Code on Linux and macOS without version checks.
26
+ Reviews use your normal agent settings, including model and reasoning effort, with
27
+ background permission and output overrides. New conversations remain in the agent’s
28
+ own history after DiffButler cleans up its workspaces; existing private sessions
29
+ retain their original home for resume.
30
+ See [supported agents and validation](docs/native-reviews.md) for details.
31
+
32
+ ## Use
33
+
34
+ Use authenticated GitHub access, including for public repositories. If you already
35
+ use `gh auth login`, export its token before launching DiffButler:
36
+
37
+ ```sh
38
+ export GITHUB_TOKEN="$(gh auth token)"
39
+ ```
40
+
41
+ Alternatively, create a [fine-grained personal access token](https://github.com/settings/personal-access-tokens)
42
+ for the repositories you want to review with **Pull requests: Read-only** permission,
43
+ and set it as `GITHUB_TOKEN`. Anonymous access is a last resort for public repositories;
44
+ its low API quota makes it unsuitable for regular polling. See the
45
+ [authentication guide](docs/github.md#authentication-options) for host/account selection,
46
+ HTTPS versus SSH Git setup, and token permissions.
47
+
48
+ From the GitHub repository you want to review, launch:
49
+
50
+ ```sh
51
+ diffbutler
52
+ ```
53
+
54
+ First-run setup detects the local repository, looks up your GitHub username using
55
+ the selected token, asks for your preferred agent, and saves your choices. If username detection
56
+ is unavailable, setup asks for it. Choose the recommended token access for public repositories;
57
+ private repositories require it. Setup asks for the token's environment variable name
58
+ and validates access. It never saves the token.
59
+ New configs silently enable both Codex and Claude when found in `PATH`; setup asks
60
+ only which detected agent should be the default. Missing agents get commented-out
61
+ entries you can enable after installation.
62
+ Later launches reuse your configuration.
63
+
64
+ The dashboard shows active reviews, including followed PRs whose GitHub review
65
+ requests have disappeared after you posted feedback. Waiting-for-author reviews
66
+ stay visible across refreshes and restarts. If an approved PR receives new commits,
67
+ it reappears as `done+` at the end of the queue without starting another AI review.
68
+ Authenticated polling slows when API quota is scarce and speeds back up when it
69
+ recovers, accounting for refresh cost and time until the quota resets.
70
+
71
+ Select a PR with the arrow keys, then work through the review:
72
+
73
+ 1. Optionally press `n` to change the current configured AI reviewer for all PRs.
74
+ Press `r` to start a background AI review. Reviews start only when you request them.
75
+ The agent is instructed to read the PR description, discussion, and earlier review
76
+ threads and replies before reviewing the local code, reporting any unavailable context.
77
+ DiffButler caches full Git history and reviews changes from the PR merge base
78
+ to its head, excluding unrelated changes made later on the target branch.
79
+ 2. A successful review shows `approve` only when the AI explicitly reports no findings;
80
+ otherwise it shows `check`. Press `Enter` to read the results, or `c` to resume
81
+ the saved agent conversation.
82
+ If the agent exits normally but DiffButler cannot parse its output, the task also
83
+ shows `check`; use `c` to inspect the saved conversation or `L` for captured output.
84
+ Changing reviewers leaves that conversation available through `c`; pressing `r`
85
+ starts a new run with the current reviewer and replaces the current review details.
86
+ Ask questions and explicitly tell the agent which comments to post, if any.
87
+ 3. Exit the agent to return to the dashboard, which refreshes activity from GitHub.
88
+ Press `w` if you are waiting for author changes, or `o` to review in your browser.
89
+ 4. After approving on GitHub, press `m` to mark the task complete locally. DiffButler
90
+ also detects your approval, closure, or merge and cleans up owned session resources
91
+ while retaining review history.
92
+
93
+ `Space` performs the selected PR's next action: start a review, discuss findings,
94
+ or open the browser. An AI review never counts as your approval. Background analysis
95
+ uses the agent's [native permission controls](docs/native-reviews.md#native-permissions-and-authentication)
96
+ and read-only review instructions; source sent to the agent may go to its model provider.
97
+
98
+ - `/`: Filter the queue.
99
+ - `a`: Toggle monitoring all open PRs in configured repositories.
100
+ - `b`: Browse accessible PRs and follow one.
101
+ - `R`: Refresh from GitHub.
102
+ - `k`: Stop the selected PR's review and reset its local tracking and owned resources.
103
+ It disappears until a forge refresh rediscovers it; review history is retained.
104
+ - `h`: View completed review history.
105
+ - `?`: Show all keyboard shortcuts.
106
+ - `q`: Quit.
107
+
108
+ See the [dashboard guide](docs/dashboard.md) for all actions and
109
+ [configuration guide](docs/configuration.md) for settings.
110
+
111
+ Your own PRs are hidden from the review queue by default, including direct and team
112
+ requests. Set `include_own_prs = true` in `[ui]` to review them, such as PRs an agent
113
+ created using your account. PRs assigned to you are also hidden by default, even when
114
+ you are requested directly or through a team. Set `include_assigned_prs = true` in
115
+ `[ui]` to include them.
116
+
117
+ ## Reset
118
+
119
+ Close running DiffButler sessions, then run:
120
+
121
+ ```sh
122
+ diffbutler reset
123
+ ```
124
+
125
+ DiffButler refuses a second command or dashboard using the same data home, including
126
+ `reset`, while another instance is running. Its instance lock releases on process
127
+ exit; a leftover PID file does not block startup. See the
128
+ [locking details](docs/configuration.md#single-instance) before handling Git lock files
129
+ or resetting after a crash.
130
+
131
+ After confirmation, this permanently removes DiffButler's standard configuration,
132
+ data, and state directories, including review history, saved sessions, and diagnostic
133
+ logs. Custom files outside those directories are preserved. Use `diffbutler reset --force`
134
+ to skip confirmation. Run `diffbutler` again to repeat first-run setup.
135
+
136
+ ## Contributing
137
+
138
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and checks.
139
+
140
+ ## License
141
+
142
+ [MIT](LICENSE.txt).
@@ -0,0 +1,142 @@
1
+ [project]
2
+ name = "diffbutler"
3
+ version = "0.5.0"
4
+ description = "A terminal dashboard for human-directed AI code reviews."
5
+ readme = "README.md"
6
+ requires-python = ">=3.14"
7
+ license = "MIT"
8
+ license-files = ["LICENSE.txt"]
9
+ keywords = [
10
+ "code-review",
11
+ "github",
12
+ "terminal",
13
+ "ai",
14
+ ]
15
+ classifiers = [
16
+ "Development Status :: 3 - Alpha",
17
+ "Environment :: Console",
18
+ "Intended Audience :: Developers",
19
+ "Programming Language :: Python :: 3 :: Only",
20
+ "Programming Language :: Python :: 3.14",
21
+ "Programming Language :: Python :: 3.15",
22
+ "Topic :: Software Development :: Quality Assurance",
23
+ "Typing :: Typed",
24
+ ]
25
+ dependencies = [
26
+ "aiosqlite>=0.22.1",
27
+ "alembic>=1.19.1",
28
+ "httpx>=0.28.1",
29
+ "pydantic>=2.13.5",
30
+ "pydantic-settings>=2.15.0",
31
+ "rich>=15.0.0",
32
+ "sqlalchemy[asyncio]>=2.0.52",
33
+ "textual>=8.2.8",
34
+ "tomlkit>=0.15.1",
35
+ "typer>=0.27.2",
36
+ ]
37
+
38
+ [project.scripts]
39
+ diffbutler = "diffbutler.ui.cli:main"
40
+
41
+ [build-system]
42
+ requires = ["uv_build>=0.12.13,<0.13.0"]
43
+ build-backend = "uv_build"
44
+
45
+ [dependency-groups]
46
+ dev = [
47
+ "import-linter>=2.14",
48
+ "markdown-it-py>=4.2.0",
49
+ "pip-audit>=2.10.1",
50
+ "pyright[nodejs]>=1.1.411",
51
+ "pytest>=9.1.1",
52
+ "pytest-asyncio>=1.4.0",
53
+ "pytest-cov>=7.1.0",
54
+ "pytest-xdist>=3.8.0",
55
+ "ruff>=0.16.5",
56
+ ]
57
+
58
+ [tool.uv]
59
+ required-version = ">=0.12.13,<0.13"
60
+ exclude-newer = "15 days"
61
+
62
+ [tool.ruff]
63
+ target-version = "py314"
64
+ line-length = 100
65
+ src = ["src"]
66
+
67
+ [tool.ruff.lint]
68
+ select = [
69
+ "E",
70
+ "F",
71
+ "I",
72
+ "UP",
73
+ "B",
74
+ "SIM",
75
+ "C4",
76
+ "RUF",
77
+ ]
78
+
79
+ [tool.pyright]
80
+ include = [
81
+ "src",
82
+ "tests",
83
+ "scripts",
84
+ ]
85
+ pythonVersion = "3.14"
86
+ typeCheckingMode = "strict"
87
+ venvPath = "."
88
+ venv = ".venv"
89
+
90
+ [tool.pytest.ini_options]
91
+ minversion = "9.0"
92
+ testpaths = ["tests"]
93
+ addopts = [
94
+ "--strict-config",
95
+ "--strict-markers",
96
+ "--import-mode=importlib",
97
+ "--dist=worksteal",
98
+ ]
99
+ markers = ["serial: timing-sensitive UI, terminal, or native-agent integration; run without xdist"]
100
+ filterwarnings = ["error"]
101
+ asyncio_mode = "strict"
102
+ asyncio_default_fixture_loop_scope = "function"
103
+
104
+ [tool.coverage.run]
105
+ branch = true
106
+ source = ["diffbutler"]
107
+ omit = ["src/diffbutler/__main__.py"]
108
+
109
+ [tool.coverage.report]
110
+ show_missing = true
111
+ skip_empty = true
112
+
113
+ [tool.importlinter]
114
+ root_package = "diffbutler"
115
+ include_external_packages = true
116
+
117
+ [[tool.importlinter.contracts]]
118
+ name = "Layered architecture"
119
+ type = "layers"
120
+ layers = [
121
+ "diffbutler.ui",
122
+ "diffbutler.core",
123
+ "diffbutler.forges | diffbutler.reviewers | diffbutler.sources | diffbutler.store",
124
+ "diffbutler.domain",
125
+ ]
126
+
127
+ [[tool.importlinter.contracts]]
128
+ name = "Terminal rendering stays out of the core and adapters"
129
+ type = "forbidden"
130
+ source_modules = [
131
+ "diffbutler.core",
132
+ "diffbutler.domain",
133
+ "diffbutler.forges",
134
+ "diffbutler.reviewers",
135
+ "diffbutler.sources",
136
+ "diffbutler.store",
137
+ "diffbutler.config",
138
+ ]
139
+ forbidden_modules = [
140
+ "textual",
141
+ "rich",
142
+ ]
@@ -0,0 +1,112 @@
1
+ [project]
2
+ name = "diffbutler"
3
+ version = "0.5.0"
4
+ description = "A terminal dashboard for human-directed AI code reviews."
5
+ readme = "README.md"
6
+ requires-python = ">=3.14"
7
+ license = "MIT"
8
+ license-files = ["LICENSE.txt"]
9
+ keywords = ["code-review", "github", "terminal", "ai"]
10
+ classifiers = [
11
+ "Development Status :: 3 - Alpha",
12
+ "Environment :: Console",
13
+ "Intended Audience :: Developers",
14
+ "Programming Language :: Python :: 3 :: Only",
15
+ "Programming Language :: Python :: 3.14",
16
+ "Programming Language :: Python :: 3.15",
17
+ "Topic :: Software Development :: Quality Assurance",
18
+ "Typing :: Typed",
19
+ ]
20
+ dependencies = [
21
+ "aiosqlite>=0.22.1",
22
+ "alembic>=1.19.1",
23
+ "httpx>=0.28.1",
24
+ "pydantic>=2.13.5",
25
+ "pydantic-settings>=2.15.0",
26
+ "rich>=15.0.0",
27
+ "sqlalchemy[asyncio]>=2.0.52",
28
+ "textual>=8.2.8",
29
+ "tomlkit>=0.15.1",
30
+ "typer>=0.27.2",
31
+ ]
32
+
33
+ [project.scripts]
34
+ diffbutler = "diffbutler.ui.cli:main"
35
+
36
+ [build-system]
37
+ requires = ["uv_build>=0.12.13,<0.13.0"]
38
+ build-backend = "uv_build"
39
+
40
+ [dependency-groups]
41
+ dev = [
42
+ "import-linter>=2.14",
43
+ "markdown-it-py>=4.2.0",
44
+ "pip-audit>=2.10.1",
45
+ "pyright[nodejs]>=1.1.411",
46
+ "pytest>=9.1.1",
47
+ "pytest-asyncio>=1.4.0",
48
+ "pytest-cov>=7.1.0",
49
+ "pytest-xdist>=3.8.0",
50
+ "ruff>=0.16.5",
51
+ ]
52
+
53
+ [tool.uv]
54
+ required-version = ">=0.12.13,<0.13"
55
+ # Give new dependency releases time to settle before routine upgrades.
56
+ exclude-newer = "15 days"
57
+
58
+ [tool.ruff]
59
+ target-version = "py314"
60
+ line-length = 100
61
+ src = ["src"]
62
+
63
+ [tool.ruff.lint]
64
+ select = ["E", "F", "I", "UP", "B", "SIM", "C4", "RUF"]
65
+
66
+ [tool.pyright]
67
+ include = ["src", "tests", "scripts"]
68
+ pythonVersion = "3.14"
69
+ typeCheckingMode = "strict"
70
+ venvPath = "."
71
+ venv = ".venv"
72
+
73
+ [tool.pytest.ini_options]
74
+ minversion = "9.0"
75
+ testpaths = ["tests"]
76
+ addopts = ["--strict-config", "--strict-markers", "--import-mode=importlib", "--dist=worksteal"]
77
+ markers = ["serial: timing-sensitive UI, terminal, or native-agent integration; run without xdist"]
78
+ filterwarnings = ["error"]
79
+ asyncio_mode = "strict"
80
+ asyncio_default_fixture_loop_scope = "function"
81
+
82
+ [tool.coverage.run]
83
+ branch = true
84
+ source = ["diffbutler"]
85
+ omit = ["src/diffbutler/__main__.py"]
86
+
87
+ [tool.coverage.report]
88
+ show_missing = true
89
+ skip_empty = true
90
+
91
+ [tool.importlinter]
92
+ root_package = "diffbutler"
93
+ include_external_packages = true
94
+
95
+ [[tool.importlinter.contracts]]
96
+ name = "Layered architecture"
97
+ type = "layers"
98
+ layers = [
99
+ "diffbutler.ui",
100
+ "diffbutler.core",
101
+ "diffbutler.forges | diffbutler.reviewers | diffbutler.sources | diffbutler.store",
102
+ "diffbutler.domain",
103
+ ]
104
+
105
+ [[tool.importlinter.contracts]]
106
+ name = "Terminal rendering stays out of the core and adapters"
107
+ type = "forbidden"
108
+ source_modules = [
109
+ "diffbutler.core", "diffbutler.domain", "diffbutler.forges",
110
+ "diffbutler.reviewers", "diffbutler.sources", "diffbutler.store", "diffbutler.config",
111
+ ]
112
+ forbidden_modules = ["textual", "rich"]
@@ -0,0 +1 @@
1
+ """DiffButler: human-directed AI code reviews."""
@@ -0,0 +1,6 @@
1
+ """Support ``python -m diffbutler``."""
2
+
3
+ from diffbutler.ui.cli import main
4
+
5
+ if __name__ == "__main__":
6
+ main()
@@ -0,0 +1 @@
1
+ """Configuration loading and validation."""