beakr-cli 0.3.1__tar.gz → 0.3.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.
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/PKG-INFO +1 -1
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/pyproject.toml +1 -1
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/src/beakr_cli/__init__.py +1 -1
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/src/beakr_cli/updates.py +27 -16
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/tests/test_updates.py +30 -19
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/uv.lock +1 -1
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/.claude/commands/kb-audit.md +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/.claude/commands/kb-research.md +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/.claude/commands/kb-search.md +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/.claude/commands/kb-write.md +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/.claude/skills/beakr/SKILL.md +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/.claude/skills/beakr/examples/answer-org-question.md +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/.claude/skills/beakr/examples/research-topic.md +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/.claude/skills/beakr/examples/write-decision-page.md +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/.claude/skills/beakr/reference/auditing.md +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/.claude/skills/beakr/reference/provenance.md +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/.claude/skills/beakr/reference/research-workflow.md +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/.claude/skills/beakr/reference/writing-pages.md +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/.github/workflows/publish.yml +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/.gitignore +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/LICENSE +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/README.md +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/install.sh +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/src/beakr_cli/citations.py +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/src/beakr_cli/client.py +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/src/beakr_cli/commands/__init__.py +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/src/beakr_cli/commands/auth.py +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/src/beakr_cli/commands/install.py +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/src/beakr_cli/commands/kb.py +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/src/beakr_cli/commands/workspace.py +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/src/beakr_cli/config.py +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/src/beakr_cli/main.py +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/src/beakr_cli/mcp_server.py +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/src/beakr_cli/output.py +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/tests/__init__.py +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/tests/test_auth_cli.py +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/tests/test_bundled_assets_vocabulary.py +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/tests/test_citations.py +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/tests/test_client.py +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/tests/test_mcp_shared_vocabulary.py +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/tests/test_mcp_source_rendering.py +0 -0
- {beakr_cli-0.3.1 → beakr_cli-0.3.2}/tests/test_packaging.py +0 -0
|
@@ -15,6 +15,7 @@ from __future__ import annotations
|
|
|
15
15
|
|
|
16
16
|
import json
|
|
17
17
|
import os
|
|
18
|
+
import re
|
|
18
19
|
import shutil
|
|
19
20
|
import subprocess
|
|
20
21
|
import sys
|
|
@@ -164,30 +165,40 @@ def _uv_tool_dir() -> Path:
|
|
|
164
165
|
return base / "uv" / "tools"
|
|
165
166
|
|
|
166
167
|
|
|
167
|
-
|
|
168
|
-
|
|
168
|
+
#: The ``{ name = "beakr-cli", ... }`` inline table in a uv tool receipt's
|
|
169
|
+
#: ``requirements``. Parsed from the requirement entry alone: the receipt also
|
|
170
|
+
#: carries ``entrypoints`` with ``install-path = ...``, and matching keys against
|
|
171
|
+
#: the whole file made every uv tool install look like a local checkout.
|
|
172
|
+
_RECEIPT_REQUIREMENT = re.compile(r'\{\s*name\s*=\s*"' + re.escape(PACKAGE_NAME) + r'"([^}]*)\}')
|
|
173
|
+
_SOURCE_KEYS = re.compile(r"(?<![\w-])(directory|path|git|url|editable)\s*=")
|
|
174
|
+
_PINNED_SPECIFIER = re.compile(r'(?<![\w-])specifier\s*=\s*"\s*==')
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _receipt_requirement(receipt: Path) -> str | None:
|
|
178
|
+
"""The body of this package's requirement entry in a uv receipt, if readable."""
|
|
179
|
+
try:
|
|
180
|
+
match = _RECEIPT_REQUIREMENT.search(receipt.read_text())
|
|
181
|
+
except OSError:
|
|
182
|
+
return None
|
|
183
|
+
return match.group(1) if match else None
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _receipt_is_local_source(requirement: str) -> bool:
|
|
187
|
+
"""True when the uv tool was installed from a directory, path, URL, or git.
|
|
169
188
|
|
|
170
189
|
Upgrading such an install from PyPI would silently swap a developer's local
|
|
171
190
|
build for the release, so it is reported rather than done.
|
|
172
191
|
"""
|
|
173
|
-
|
|
174
|
-
text = receipt.read_text()
|
|
175
|
-
except OSError:
|
|
176
|
-
return False
|
|
177
|
-
return any(marker in text for marker in ("directory =", "path =", "git ="))
|
|
192
|
+
return _SOURCE_KEYS.search(requirement) is not None
|
|
178
193
|
|
|
179
194
|
|
|
180
|
-
def _receipt_pins_version(
|
|
195
|
+
def _receipt_pins_version(requirement: str) -> bool:
|
|
181
196
|
"""True when the uv tool was installed as ``beakr-cli==X``.
|
|
182
197
|
|
|
183
198
|
``uv tool upgrade`` honours the pin and does nothing, so offering it would
|
|
184
199
|
report an upgrade that never happens.
|
|
185
200
|
"""
|
|
186
|
-
|
|
187
|
-
text = receipt.read_text()
|
|
188
|
-
except OSError:
|
|
189
|
-
return False
|
|
190
|
-
return f'name = "{PACKAGE_NAME}", specifier = "==' in text
|
|
201
|
+
return _PINNED_SPECIFIER.search(requirement) is not None
|
|
191
202
|
|
|
192
203
|
|
|
193
204
|
def detect_install(prefix: Path | None = None) -> InstallInfo:
|
|
@@ -200,8 +211,8 @@ def detect_install(prefix: Path | None = None) -> InstallInfo:
|
|
|
200
211
|
except OSError:
|
|
201
212
|
in_uv_tools = False
|
|
202
213
|
if in_uv_tools or ("uv" in parts and "tools" in parts):
|
|
203
|
-
|
|
204
|
-
if _receipt_is_local_source(
|
|
214
|
+
requirement = _receipt_requirement(env / "uv-receipt.toml") or ""
|
|
215
|
+
if _receipt_is_local_source(requirement):
|
|
205
216
|
return InstallInfo(
|
|
206
217
|
InstallMethod.uv_tool_source,
|
|
207
218
|
None,
|
|
@@ -209,7 +220,7 @@ def detect_install(prefix: Path | None = None) -> InstallInfo:
|
|
|
209
220
|
"`uv tool install --force <checkout>`, or switch to the published "
|
|
210
221
|
f"release with `uv tool install --force {PACKAGE_NAME}`.",
|
|
211
222
|
)
|
|
212
|
-
if _receipt_pins_version(
|
|
223
|
+
if _receipt_pins_version(requirement):
|
|
213
224
|
return InstallInfo(
|
|
214
225
|
InstallMethod.uv_tool_pinned,
|
|
215
226
|
None,
|
|
@@ -137,14 +137,39 @@ def test_running_ahead_of_pypi_is_not_an_update() -> None:
|
|
|
137
137
|
# Install detection
|
|
138
138
|
# ---------------------------------------------------------------------------
|
|
139
139
|
|
|
140
|
+
#: Receipts exactly as uv 0.11 writes them. The entrypoints block matters: its
|
|
141
|
+
#: ``install-path = ...`` made every PyPI install look like a local checkout when
|
|
142
|
+
#: detection searched the whole file for ``path =``, so `beakr update` refused to
|
|
143
|
+
#: upgrade any uv tool install in 0.3.0 and 0.3.1.
|
|
144
|
+
_ENTRYPOINTS = """entrypoints = [
|
|
145
|
+
{ name = "beakr", install-path = "/Users/u/.local/bin/beakr", from = "beakr-cli" },
|
|
146
|
+
]
|
|
147
|
+
"""
|
|
148
|
+
PYPI_RECEIPT = '[tool]\nrequirements = [{ name = "beakr-cli" }]\n' + _ENTRYPOINTS
|
|
149
|
+
PINNED_RECEIPT = (
|
|
150
|
+
'[tool]\nrequirements = [{ name = "beakr-cli", specifier = "==0.3.0" }]\n' + _ENTRYPOINTS
|
|
151
|
+
)
|
|
152
|
+
RANGE_RECEIPT = (
|
|
153
|
+
'[tool]\nrequirements = [{ name = "beakr-cli", specifier = ">=0.3" }]\n' + _ENTRYPOINTS
|
|
154
|
+
)
|
|
155
|
+
SOURCE_RECEIPT = (
|
|
156
|
+
'[tool]\nrequirements = [{ name = "beakr-cli", directory = "/src/beakr-cli" }]\n'
|
|
157
|
+
'constraints = [{ name = "mcp", specifier = "<2" }]\n' + _ENTRYPOINTS
|
|
158
|
+
)
|
|
159
|
+
|
|
140
160
|
|
|
141
|
-
def
|
|
161
|
+
def _uv_tool_env(tmp_path, monkeypatch, receipt: str) -> Path:
|
|
142
162
|
tools = tmp_path / "uv" / "tools"
|
|
143
163
|
env = tools / "beakr-cli"
|
|
144
164
|
env.mkdir(parents=True)
|
|
145
|
-
(env / "uv-receipt.toml").write_text(
|
|
165
|
+
(env / "uv-receipt.toml").write_text(receipt)
|
|
146
166
|
monkeypatch.setenv("UV_TOOL_DIR", str(tools))
|
|
147
|
-
|
|
167
|
+
return env
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
@pytest.mark.parametrize("receipt", [PYPI_RECEIPT, RANGE_RECEIPT])
|
|
171
|
+
def test_detects_pypi_uv_tool_install(receipt, tmp_path, monkeypatch) -> None:
|
|
172
|
+
info = updates.detect_install(_uv_tool_env(tmp_path, monkeypatch, receipt))
|
|
148
173
|
assert info.method is updates.InstallMethod.uv_tool
|
|
149
174
|
# --reinstall-package implies a refresh of the cached index; without one an
|
|
150
175
|
# upgrade shortly after a release resolved the old version and still exited 0.
|
|
@@ -155,14 +180,7 @@ def test_detects_pypi_uv_tool_install(tmp_path, monkeypatch) -> None:
|
|
|
155
180
|
|
|
156
181
|
def test_version_pinned_uv_tool_is_reported_not_upgraded(tmp_path, monkeypatch) -> None:
|
|
157
182
|
"""`uv tool upgrade` keeps a `==X` pin, so running it would upgrade nothing."""
|
|
158
|
-
|
|
159
|
-
env = tools / "beakr-cli"
|
|
160
|
-
env.mkdir(parents=True)
|
|
161
|
-
(env / "uv-receipt.toml").write_text(
|
|
162
|
-
'[tool]\nrequirements = [{ name = "beakr-cli", specifier = "==0.3.0" }]\n'
|
|
163
|
-
)
|
|
164
|
-
monkeypatch.setenv("UV_TOOL_DIR", str(tools))
|
|
165
|
-
info = updates.detect_install(env)
|
|
183
|
+
info = updates.detect_install(_uv_tool_env(tmp_path, monkeypatch, PINNED_RECEIPT))
|
|
166
184
|
assert info.method is updates.InstallMethod.uv_tool_pinned
|
|
167
185
|
assert info.upgrade_command is None
|
|
168
186
|
assert "uv tool install --force beakr-cli" in info.instructions
|
|
@@ -170,14 +188,7 @@ def test_version_pinned_uv_tool_is_reported_not_upgraded(tmp_path, monkeypatch)
|
|
|
170
188
|
|
|
171
189
|
def test_local_checkout_uv_tool_is_never_upgraded_from_pypi(tmp_path, monkeypatch) -> None:
|
|
172
190
|
"""A developer's local build must not be silently replaced by the release."""
|
|
173
|
-
|
|
174
|
-
env = tools / "beakr-cli"
|
|
175
|
-
env.mkdir(parents=True)
|
|
176
|
-
(env / "uv-receipt.toml").write_text(
|
|
177
|
-
'[tool]\nrequirements = [{ name = "beakr-cli", directory = "/src/beakr-cli" }]\n'
|
|
178
|
-
)
|
|
179
|
-
monkeypatch.setenv("UV_TOOL_DIR", str(tools))
|
|
180
|
-
info = updates.detect_install(env)
|
|
191
|
+
info = updates.detect_install(_uv_tool_env(tmp_path, monkeypatch, SOURCE_RECEIPT))
|
|
181
192
|
assert info.method is updates.InstallMethod.uv_tool_source
|
|
182
193
|
assert info.upgrade_command is None
|
|
183
194
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|