diffbutler 0.5.0__tar.gz → 0.5.2__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 → diffbutler-0.5.2}/PKG-INFO +28 -4
  2. {diffbutler-0.5.0 → diffbutler-0.5.2}/README.md +27 -3
  3. {diffbutler-0.5.0 → diffbutler-0.5.2}/pyproject.toml +1 -1
  4. {diffbutler-0.5.0 → diffbutler-0.5.2}/pyproject.toml.orig +1 -1
  5. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/store/database.py +28 -9
  6. {diffbutler-0.5.0 → diffbutler-0.5.2}/LICENSE.txt +0 -0
  7. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/__init__.py +0 -0
  8. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/__main__.py +0 -0
  9. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/config/__init__.py +0 -0
  10. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/config/default.toml +0 -0
  11. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/config/diagnostics.py +0 -0
  12. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/config/file.py +0 -0
  13. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/config/instance.py +0 -0
  14. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/config/settings.py +0 -0
  15. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/config/setup.py +0 -0
  16. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/config/transport.py +0 -0
  17. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/core/__init__.py +0 -0
  18. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/core/application.py +0 -0
  19. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/core/context.py +0 -0
  20. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/core/dashboard.py +0 -0
  21. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/core/doctor.py +0 -0
  22. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/core/lifecycle.py +0 -0
  23. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/core/managed.py +0 -0
  24. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/core/native.py +0 -0
  25. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/core/polling.py +0 -0
  26. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/core/process.py +0 -0
  27. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/core/repositories.py +0 -0
  28. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/core/reset.py +0 -0
  29. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/core/resources.py +0 -0
  30. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/core/review.py +0 -0
  31. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/core/setup.py +0 -0
  32. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/core/snapshot.py +0 -0
  33. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/core/sync.py +0 -0
  34. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/core/worker.py +0 -0
  35. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/core/workspaces.py +0 -0
  36. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/domain/__init__.py +0 -0
  37. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/domain/dashboard.py +0 -0
  38. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/domain/diff.py +0 -0
  39. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/domain/forge.py +0 -0
  40. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/domain/lifecycle.py +0 -0
  41. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/domain/polling.py +0 -0
  42. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/domain/process.py +0 -0
  43. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/domain/redaction.py +0 -0
  44. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/domain/resources.py +0 -0
  45. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/domain/review.py +0 -0
  46. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/domain/sync.py +0 -0
  47. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/forges/__init__.py +0 -0
  48. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/forges/base.py +0 -0
  49. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/forges/diagnostics.py +0 -0
  50. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/forges/github/__init__.py +0 -0
  51. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/forges/github/adapter.py +0 -0
  52. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/forges/github/auth.py +0 -0
  53. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/forges/github/http.py +0 -0
  54. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/forges/github/mapping.py +0 -0
  55. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/forges/github/setup.py +0 -0
  56. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/forges/registry.py +0 -0
  57. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/py.typed +0 -0
  58. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/reviewers/__init__.py +0 -0
  59. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/reviewers/base.py +0 -0
  60. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/reviewers/extraction.py +0 -0
  61. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/reviewers/native.py +0 -0
  62. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/reviewers/parsing.py +0 -0
  63. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/reviewers/presets.toml +0 -0
  64. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/reviewers/process.py +0 -0
  65. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/reviewers/prompt.py +0 -0
  66. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/reviewers/redaction.py +0 -0
  67. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/reviewers/resources.py +0 -0
  68. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/sources/__init__.py +0 -0
  69. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/sources/diagnostics.py +0 -0
  70. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/sources/fetch.py +0 -0
  71. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/sources/git.py +0 -0
  72. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/sources/remotes.py +0 -0
  73. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/store/__init__.py +0 -0
  74. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/store/base.py +0 -0
  75. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/store/dashboard.py +0 -0
  76. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/store/lifecycle.py +0 -0
  77. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/store/migrations/__init__.py +0 -0
  78. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/store/migrations/env.py +0 -0
  79. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/store/migrations/versions/0001_initial.py +0 -0
  80. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/store/migrations/versions/0002_monitoring.py +0 -0
  81. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/store/migrations/versions/0003_reviews.py +0 -0
  82. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/store/migrations/versions/0004_claims.py +0 -0
  83. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/store/migrations/versions/0005_results.py +0 -0
  84. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/store/migrations/versions/0006_anchors.py +0 -0
  85. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/store/migrations/versions/0007_lifecycle.py +0 -0
  86. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/store/migrations/versions/0008_processes.py +0 -0
  87. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/store/migrations/versions/0009_dashboard.py +0 -0
  88. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/store/migrations/versions/0010_run_reasons.py +0 -0
  89. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/store/migrations/versions/__init__.py +0 -0
  90. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/store/models.py +0 -0
  91. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/store/process.py +0 -0
  92. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/store/queue.py +0 -0
  93. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/store/review.py +0 -0
  94. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/ui/__init__.py +0 -0
  95. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/ui/cli/__init__.py +0 -0
  96. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/ui/cli/editor.py +0 -0
  97. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/ui/tui/__init__.py +0 -0
  98. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/ui/tui/_fake_agent.py +0 -0
  99. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/ui/tui/_foreground_exec.py +0 -0
  100. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/ui/tui/app.py +0 -0
  101. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/ui/tui/browse.py +0 -0
  102. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/ui/tui/browser.py +0 -0
  103. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/ui/tui/dashboard.py +0 -0
  104. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/ui/tui/foreground.py +0 -0
  105. {diffbutler-0.5.0 → diffbutler-0.5.2}/src/diffbutler/ui/tui/handoff_probe.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: diffbutler
3
- Version: 0.5.0
3
+ Version: 0.5.2
4
4
  Summary: A terminal dashboard for human-directed AI code reviews.
5
5
  Keywords: code-review,github,terminal,ai
6
6
  License-Expression: MIT
@@ -38,17 +38,29 @@ finish your review in the browser.
38
38
 
39
39
  ## Install
40
40
 
41
- Install [uv](https://docs.astral.sh/uv/getting-started/installation/) and Git.
42
- From a local checkout of this repository, run:
41
+ Install [uv](https://docs.astral.sh/uv/getting-started/installation/) and Git, then
42
+ install DiffButler from [PyPI](https://pypi.org/project/diffbutler/):
43
43
 
44
44
  ```sh
45
- uv tool install --python 3.14 .
45
+ uv tool install --python 3.14 diffbutler
46
46
  ```
47
47
 
48
48
  uv manages Python and dependencies in an isolated environment. No virtual environment
49
49
  activation is needed. If `diffbutler` is not on your PATH, run `uv tool update-shell`
50
50
  and restart your shell.
51
51
 
52
+ To upgrade a PyPI installation:
53
+
54
+ ```sh
55
+ uv tool upgrade diffbutler
56
+ ```
57
+
58
+ To install from a local checkout of this repository instead, run from its root:
59
+
60
+ ```sh
61
+ uv tool install --python 3.14 .
62
+ ```
63
+
52
64
  Install and sign in to Codex CLI or Claude Code before requesting AI reviews.
53
65
  The native presets support Codex CLI or Claude Code on Linux and macOS without version checks.
54
66
  Reviews use your normal agent settings, including model and reasoning effort, with
@@ -161,6 +173,18 @@ data, and state directories, including review history, saved sessions, and diagn
161
173
  logs. Custom files outside those directories are preserved. Use `diffbutler reset --force`
162
174
  to skip confirmation. Run `diffbutler` again to repeat first-run setup.
163
175
 
176
+ ## Uninstall
177
+
178
+ Remove the uv-installed command and its isolated environment:
179
+
180
+ ```sh
181
+ uv tool uninstall diffbutler
182
+ ```
183
+
184
+ This keeps your DiffButler configuration, review history, and diagnostic logs.
185
+ To remove those too, run `diffbutler reset` **before uninstalling**; see
186
+ [Reset](#reset) for what it deletes.
187
+
164
188
  ## Contributing
165
189
 
166
190
  See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and checks.
@@ -10,17 +10,29 @@ finish your review in the browser.
10
10
 
11
11
  ## Install
12
12
 
13
- Install [uv](https://docs.astral.sh/uv/getting-started/installation/) and Git.
14
- From a local checkout of this repository, run:
13
+ Install [uv](https://docs.astral.sh/uv/getting-started/installation/) and Git, then
14
+ install DiffButler from [PyPI](https://pypi.org/project/diffbutler/):
15
15
 
16
16
  ```sh
17
- uv tool install --python 3.14 .
17
+ uv tool install --python 3.14 diffbutler
18
18
  ```
19
19
 
20
20
  uv manages Python and dependencies in an isolated environment. No virtual environment
21
21
  activation is needed. If `diffbutler` is not on your PATH, run `uv tool update-shell`
22
22
  and restart your shell.
23
23
 
24
+ To upgrade a PyPI installation:
25
+
26
+ ```sh
27
+ uv tool upgrade diffbutler
28
+ ```
29
+
30
+ To install from a local checkout of this repository instead, run from its root:
31
+
32
+ ```sh
33
+ uv tool install --python 3.14 .
34
+ ```
35
+
24
36
  Install and sign in to Codex CLI or Claude Code before requesting AI reviews.
25
37
  The native presets support Codex CLI or Claude Code on Linux and macOS without version checks.
26
38
  Reviews use your normal agent settings, including model and reasoning effort, with
@@ -133,6 +145,18 @@ data, and state directories, including review history, saved sessions, and diagn
133
145
  logs. Custom files outside those directories are preserved. Use `diffbutler reset --force`
134
146
  to skip confirmation. Run `diffbutler` again to repeat first-run setup.
135
147
 
148
+ ## Uninstall
149
+
150
+ Remove the uv-installed command and its isolated environment:
151
+
152
+ ```sh
153
+ uv tool uninstall diffbutler
154
+ ```
155
+
156
+ This keeps your DiffButler configuration, review history, and diagnostic logs.
157
+ To remove those too, run `diffbutler reset` **before uninstalling**; see
158
+ [Reset](#reset) for what it deletes.
159
+
136
160
  ## Contributing
137
161
 
138
162
  See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and checks.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "diffbutler"
3
- version = "0.5.0"
3
+ version = "0.5.2"
4
4
  description = "A terminal dashboard for human-directed AI code reviews."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.14"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "diffbutler"
3
- version = "0.5.0"
3
+ version = "0.5.2"
4
4
  description = "A terminal dashboard for human-directed AI code reviews."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.14"
@@ -5,6 +5,7 @@ import sqlite3
5
5
  from contextlib import closing
6
6
  from dataclasses import dataclass
7
7
  from datetime import UTC, datetime
8
+ from functools import partial
8
9
  from pathlib import Path
9
10
  from uuid import uuid4
10
11
 
@@ -56,6 +57,29 @@ def sqlite_url(value: str) -> URL:
56
57
  return url.set(database=str(Path(url.database).expanduser().absolute()))
57
58
 
58
59
 
60
+ async def _finish[T](task: asyncio.Future[T]) -> tuple[T, bool]:
61
+ """Finish owned I/O despite cancellation, reporting it to the resource owner."""
62
+ cancelled = False
63
+ while not task.done():
64
+ try:
65
+ await asyncio.shield(task)
66
+ except asyncio.CancelledError:
67
+ cancelled = True
68
+ return task.result(), cancelled
69
+
70
+
71
+ async def connect_sqlite(path: Path) -> aiosqlite.Connection:
72
+ # Cancelling aiosqlite's initial await can discard the connection its worker
73
+ # thread is still opening, before SQLAlchemy can own or dispose of it.
74
+ connection, cancelled = await _finish(
75
+ asyncio.ensure_future(aiosqlite.connect(path, check_same_thread=False))
76
+ )
77
+ if cancelled:
78
+ await _finish(asyncio.create_task(connection.close()))
79
+ raise asyncio.CancelledError
80
+ return connection
81
+
82
+
59
83
  async def configure_sqlite(connection: aiosqlite.Connection) -> None:
60
84
  """Enable WAL without blocking the event loop during startup contention."""
61
85
  try:
@@ -115,7 +139,9 @@ class Database:
115
139
  self.url = sqlite_url(url)
116
140
  assert self.url.database is not None
117
141
  self.path = Path(self.url.database)
118
- self.engine: AsyncEngine = create_async_engine(self.url)
142
+ self.engine: AsyncEngine = create_async_engine(
143
+ self.url, async_creator=partial(connect_sqlite, self.path)
144
+ )
119
145
  event.listen(self.engine.sync_engine, "connect", _configure_sqlite)
120
146
  self.sessions = async_sessionmaker(self.engine, expire_on_commit=False)
121
147
 
@@ -160,13 +186,6 @@ class Database:
160
186
  # Cancellation during disposal can interrupt aiosqlite.close after the
161
187
  # pool has discarded the connection. Finish closing it before propagating
162
188
  # cancellation, including repeated cancellation during app shutdown.
163
- closing = asyncio.create_task(self.engine.dispose())
164
- cancelled = False
165
- while not closing.done():
166
- try:
167
- await asyncio.shield(closing)
168
- except asyncio.CancelledError:
169
- cancelled = True
170
- closing.result()
189
+ _, cancelled = await _finish(asyncio.create_task(self.engine.dispose()))
171
190
  if cancelled:
172
191
  raise asyncio.CancelledError
File without changes