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.
Files changed (42) hide show
  1. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/PKG-INFO +1 -1
  2. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/pyproject.toml +1 -1
  3. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/src/beakr_cli/__init__.py +1 -1
  4. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/src/beakr_cli/updates.py +27 -16
  5. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/tests/test_updates.py +30 -19
  6. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/uv.lock +1 -1
  7. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/.claude/commands/kb-audit.md +0 -0
  8. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/.claude/commands/kb-research.md +0 -0
  9. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/.claude/commands/kb-search.md +0 -0
  10. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/.claude/commands/kb-write.md +0 -0
  11. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/.claude/skills/beakr/SKILL.md +0 -0
  12. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/.claude/skills/beakr/examples/answer-org-question.md +0 -0
  13. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/.claude/skills/beakr/examples/research-topic.md +0 -0
  14. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/.claude/skills/beakr/examples/write-decision-page.md +0 -0
  15. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/.claude/skills/beakr/reference/auditing.md +0 -0
  16. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/.claude/skills/beakr/reference/provenance.md +0 -0
  17. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/.claude/skills/beakr/reference/research-workflow.md +0 -0
  18. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/.claude/skills/beakr/reference/writing-pages.md +0 -0
  19. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/.github/workflows/publish.yml +0 -0
  20. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/.gitignore +0 -0
  21. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/LICENSE +0 -0
  22. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/README.md +0 -0
  23. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/install.sh +0 -0
  24. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/src/beakr_cli/citations.py +0 -0
  25. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/src/beakr_cli/client.py +0 -0
  26. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/src/beakr_cli/commands/__init__.py +0 -0
  27. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/src/beakr_cli/commands/auth.py +0 -0
  28. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/src/beakr_cli/commands/install.py +0 -0
  29. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/src/beakr_cli/commands/kb.py +0 -0
  30. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/src/beakr_cli/commands/workspace.py +0 -0
  31. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/src/beakr_cli/config.py +0 -0
  32. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/src/beakr_cli/main.py +0 -0
  33. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/src/beakr_cli/mcp_server.py +0 -0
  34. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/src/beakr_cli/output.py +0 -0
  35. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/tests/__init__.py +0 -0
  36. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/tests/test_auth_cli.py +0 -0
  37. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/tests/test_bundled_assets_vocabulary.py +0 -0
  38. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/tests/test_citations.py +0 -0
  39. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/tests/test_client.py +0 -0
  40. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/tests/test_mcp_shared_vocabulary.py +0 -0
  41. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/tests/test_mcp_source_rendering.py +0 -0
  42. {beakr_cli-0.3.1 → beakr_cli-0.3.2}/tests/test_packaging.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: beakr-cli
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Summary: CLI and MCP server for Beakr's knowledge base
5
5
  Project-URL: Homepage, https://thebeakr.com
6
6
  Project-URL: Repository, https://github.com/BeakrHub/beakr-cli
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "beakr-cli"
7
- version = "0.3.1"
7
+ version = "0.3.2"
8
8
  description = "CLI and MCP server for Beakr's knowledge base"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -1,3 +1,3 @@
1
1
  """Beakr CLI -- command-line and MCP server for Beakr's knowledge base."""
2
2
 
3
- __version__ = "0.3.1"
3
+ __version__ = "0.3.2"
@@ -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
- def _receipt_is_local_source(receipt: Path) -> bool:
168
- """True when the uv tool was installed from a directory, path, or git checkout.
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
- try:
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(receipt: Path) -> bool:
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
- try:
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
- receipt = env / "uv-receipt.toml"
204
- if _receipt_is_local_source(receipt):
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(receipt):
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 test_detects_pypi_uv_tool_install(tmp_path, monkeypatch) -> None:
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('[tool]\nrequirements = [{ name = "beakr-cli" }]\n')
165
+ (env / "uv-receipt.toml").write_text(receipt)
146
166
  monkeypatch.setenv("UV_TOOL_DIR", str(tools))
147
- info = updates.detect_install(env)
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
- tools = tmp_path / "uv" / "tools"
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
- tools = tmp_path / "uv" / "tools"
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
 
@@ -63,7 +63,7 @@ wheels = [
63
63
 
64
64
  [[package]]
65
65
  name = "beakr-cli"
66
- version = "0.3.1"
66
+ version = "0.3.2"
67
67
  source = { editable = "." }
68
68
  dependencies = [
69
69
  { name = "httpx" },
File without changes
File without changes
File without changes
File without changes
File without changes