webbee 0.7.6__tar.gz → 0.7.8__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 (160) hide show
  1. {webbee-0.7.6 → webbee-0.7.8}/CHANGELOG.md +14 -0
  2. {webbee-0.7.6 → webbee-0.7.8}/PKG-INFO +1 -1
  3. {webbee-0.7.6 → webbee-0.7.8}/install.sh +1 -1
  4. {webbee-0.7.6 → webbee-0.7.8}/pyproject.toml +1 -1
  5. webbee-0.7.8/src/webbee/__init__.py +1 -0
  6. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/autoupdate.py +37 -18
  7. {webbee-0.7.6 → webbee-0.7.8}/tests/test_autoupdate.py +37 -15
  8. {webbee-0.7.6 → webbee-0.7.8}/tests/test_packaging.py +1 -1
  9. webbee-0.7.6/src/webbee/__init__.py +0 -1
  10. {webbee-0.7.6 → webbee-0.7.8}/.github/workflows/publish.yml +0 -0
  11. {webbee-0.7.6 → webbee-0.7.8}/.gitignore +0 -0
  12. {webbee-0.7.6 → webbee-0.7.8}/LICENSE +0 -0
  13. {webbee-0.7.6 → webbee-0.7.8}/README.md +0 -0
  14. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/__main__.py +0 -0
  15. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/_launch.py +0 -0
  16. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/account.py +0 -0
  17. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/active_sessions.py +0 -0
  18. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/banner_art.py +0 -0
  19. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/boot.py +0 -0
  20. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/browser.py +0 -0
  21. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/checkpoints.py +0 -0
  22. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/cli.py +0 -0
  23. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/clipboard.py +0 -0
  24. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/clipboard_read.py +0 -0
  25. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/clipboard_session.py +0 -0
  26. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/coding_context.py +0 -0
  27. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/commands.py +0 -0
  28. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/config.py +0 -0
  29. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/consent.py +0 -0
  30. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/details.py +0 -0
  31. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/events.py +0 -0
  32. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/frames.py +0 -0
  33. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/home.py +0 -0
  34. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/home_view.py +0 -0
  35. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/http.py +0 -0
  36. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/instance_lock.py +0 -0
  37. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/intel/__init__.py +0 -0
  38. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/intel/graph.py +0 -0
  39. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/intel/outline.py +0 -0
  40. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/intel/query.py +0 -0
  41. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/intel/service.py +0 -0
  42. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/intel/store.py +0 -0
  43. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/intel/tree.py +0 -0
  44. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/intel/vectors.py +0 -0
  45. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/intel/watch.py +0 -0
  46. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/media.py +0 -0
  47. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/modals.py +0 -0
  48. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/mode_store.py +0 -0
  49. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/native_files.py +0 -0
  50. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/native_read.py +0 -0
  51. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/newtab_mode.py +0 -0
  52. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/output_pane.py +0 -0
  53. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/pricing_constants.py +0 -0
  54. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/queue_panel.py +0 -0
  55. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/reflow.py +0 -0
  56. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/remote.py +0 -0
  57. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/render.py +0 -0
  58. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/repl.py +0 -0
  59. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/repo.py +0 -0
  60. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/selection.py +0 -0
  61. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/session.py +0 -0
  62. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/sessions.py +0 -0
  63. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/sizing.py +0 -0
  64. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/slots.py +0 -0
  65. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/steer.py +0 -0
  66. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/stream.py +0 -0
  67. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/tab_store.py +0 -0
  68. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/tabs.py +0 -0
  69. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/thread.py +0 -0
  70. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/tier_store.py +0 -0
  71. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/todo_panel.py +0 -0
  72. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/tokens.py +0 -0
  73. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/tools.py +0 -0
  74. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/tui.py +0 -0
  75. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/update.py +0 -0
  76. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/urlopen.py +0 -0
  77. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/wallet.py +0 -0
  78. {webbee-0.7.6 → webbee-0.7.8}/src/webbee/worktrees.py +0 -0
  79. {webbee-0.7.6 → webbee-0.7.8}/tests/__init__.py +0 -0
  80. {webbee-0.7.6 → webbee-0.7.8}/tests/conftest.py +0 -0
  81. {webbee-0.7.6 → webbee-0.7.8}/tests/test_account.py +0 -0
  82. {webbee-0.7.6 → webbee-0.7.8}/tests/test_active_sessions.py +0 -0
  83. {webbee-0.7.6 → webbee-0.7.8}/tests/test_audit_2026_07_25_findings.py +0 -0
  84. {webbee-0.7.6 → webbee-0.7.8}/tests/test_checkpoints.py +0 -0
  85. {webbee-0.7.6 → webbee-0.7.8}/tests/test_checkpoints_nested_1030.py +0 -0
  86. {webbee-0.7.6 → webbee-0.7.8}/tests/test_cli.py +0 -0
  87. {webbee-0.7.6 → webbee-0.7.8}/tests/test_clipboard.py +0 -0
  88. {webbee-0.7.6 → webbee-0.7.8}/tests/test_clipboard_read.py +0 -0
  89. {webbee-0.7.6 → webbee-0.7.8}/tests/test_code_map_fresh.py +0 -0
  90. {webbee-0.7.6 → webbee-0.7.8}/tests/test_coding_context.py +0 -0
  91. {webbee-0.7.6 → webbee-0.7.8}/tests/test_commands.py +0 -0
  92. {webbee-0.7.6 → webbee-0.7.8}/tests/test_config.py +0 -0
  93. {webbee-0.7.6 → webbee-0.7.8}/tests/test_cpc_contract_stable.py +0 -0
  94. {webbee-0.7.6 → webbee-0.7.8}/tests/test_cpc_outline.py +0 -0
  95. {webbee-0.7.6 → webbee-0.7.8}/tests/test_cross_loop_token.py +0 -0
  96. {webbee-0.7.6 → webbee-0.7.8}/tests/test_details.py +0 -0
  97. {webbee-0.7.6 → webbee-0.7.8}/tests/test_events.py +0 -0
  98. {webbee-0.7.6 → webbee-0.7.8}/tests/test_freeze_fix.py +0 -0
  99. {webbee-0.7.6 → webbee-0.7.8}/tests/test_home.py +0 -0
  100. {webbee-0.7.6 → webbee-0.7.8}/tests/test_home_view.py +0 -0
  101. {webbee-0.7.6 → webbee-0.7.8}/tests/test_home_view_mouse.py +0 -0
  102. {webbee-0.7.6 → webbee-0.7.8}/tests/test_inspect_page.py +0 -0
  103. {webbee-0.7.6 → webbee-0.7.8}/tests/test_instance_lock.py +0 -0
  104. {webbee-0.7.6 → webbee-0.7.8}/tests/test_intel_any_folder.py +0 -0
  105. {webbee-0.7.6 → webbee-0.7.8}/tests/test_intel_cache_and_sync_worker.py +0 -0
  106. {webbee-0.7.6 → webbee-0.7.8}/tests/test_intel_checksum_tree.py +0 -0
  107. {webbee-0.7.6 → webbee-0.7.8}/tests/test_intel_chunker.py +0 -0
  108. {webbee-0.7.6 → webbee-0.7.8}/tests/test_intel_embed.py +0 -0
  109. {webbee-0.7.6 → webbee-0.7.8}/tests/test_intel_folder_1106.py +0 -0
  110. {webbee-0.7.6 → webbee-0.7.8}/tests/test_intel_graph.py +0 -0
  111. {webbee-0.7.6 → webbee-0.7.8}/tests/test_intel_no_numpy.py +0 -0
  112. {webbee-0.7.6 → webbee-0.7.8}/tests/test_intel_query.py +0 -0
  113. {webbee-0.7.6 → webbee-0.7.8}/tests/test_intel_service.py +0 -0
  114. {webbee-0.7.6 → webbee-0.7.8}/tests/test_intel_store.py +0 -0
  115. {webbee-0.7.6 → webbee-0.7.8}/tests/test_intel_vectors.py +0 -0
  116. {webbee-0.7.6 → webbee-0.7.8}/tests/test_intel_watch.py +0 -0
  117. {webbee-0.7.6 → webbee-0.7.8}/tests/test_launch.py +0 -0
  118. {webbee-0.7.6 → webbee-0.7.8}/tests/test_marathon.py +0 -0
  119. {webbee-0.7.6 → webbee-0.7.8}/tests/test_modals_e2e.py +0 -0
  120. {webbee-0.7.6 → webbee-0.7.8}/tests/test_mode_and_remote_0_6_8.py +0 -0
  121. {webbee-0.7.6 → webbee-0.7.8}/tests/test_mode_store.py +0 -0
  122. {webbee-0.7.6 → webbee-0.7.8}/tests/test_native_files.py +0 -0
  123. {webbee-0.7.6 → webbee-0.7.8}/tests/test_newtab_mode.py +0 -0
  124. {webbee-0.7.6 → webbee-0.7.8}/tests/test_probe_cost.py +0 -0
  125. {webbee-0.7.6 → webbee-0.7.8}/tests/test_queue_manage_0337.py +0 -0
  126. {webbee-0.7.6 → webbee-0.7.8}/tests/test_reflow.py +0 -0
  127. {webbee-0.7.6 → webbee-0.7.8}/tests/test_remote_code_reads.py +0 -0
  128. {webbee-0.7.6 → webbee-0.7.8}/tests/test_render.py +0 -0
  129. {webbee-0.7.6 → webbee-0.7.8}/tests/test_repl.py +0 -0
  130. {webbee-0.7.6 → webbee-0.7.8}/tests/test_repo.py +0 -0
  131. {webbee-0.7.6 → webbee-0.7.8}/tests/test_resilient_http.py +0 -0
  132. {webbee-0.7.6 → webbee-0.7.8}/tests/test_review_fixes_1030.py +0 -0
  133. {webbee-0.7.6 → webbee-0.7.8}/tests/test_session.py +0 -0
  134. {webbee-0.7.6 → webbee-0.7.8}/tests/test_session_review_1030.py +0 -0
  135. {webbee-0.7.6 → webbee-0.7.8}/tests/test_sessions.py +0 -0
  136. {webbee-0.7.6 → webbee-0.7.8}/tests/test_sizing.py +0 -0
  137. {webbee-0.7.6 → webbee-0.7.8}/tests/test_slots.py +0 -0
  138. {webbee-0.7.6 → webbee-0.7.8}/tests/test_steer.py +0 -0
  139. {webbee-0.7.6 → webbee-0.7.8}/tests/test_stream.py +0 -0
  140. {webbee-0.7.6 → webbee-0.7.8}/tests/test_tab_bar_stability.py +0 -0
  141. {webbee-0.7.6 → webbee-0.7.8}/tests/test_tab_restore_0337.py +0 -0
  142. {webbee-0.7.6 → webbee-0.7.8}/tests/test_tabs.py +0 -0
  143. {webbee-0.7.6 → webbee-0.7.8}/tests/test_terminal_ux_0336.py +0 -0
  144. {webbee-0.7.6 → webbee-0.7.8}/tests/test_thread.py +0 -0
  145. {webbee-0.7.6 → webbee-0.7.8}/tests/test_tier_invalidation.py +0 -0
  146. {webbee-0.7.6 → webbee-0.7.8}/tests/test_tier_store.py +0 -0
  147. {webbee-0.7.6 → webbee-0.7.8}/tests/test_todo_panel.py +0 -0
  148. {webbee-0.7.6 → webbee-0.7.8}/tests/test_tokens.py +0 -0
  149. {webbee-0.7.6 → webbee-0.7.8}/tests/test_tools.py +0 -0
  150. {webbee-0.7.6 → webbee-0.7.8}/tests/test_tools_eol_cancel_1030.py +0 -0
  151. {webbee-0.7.6 → webbee-0.7.8}/tests/test_tools_vision_websearch.py +0 -0
  152. {webbee-0.7.6 → webbee-0.7.8}/tests/test_tui.py +0 -0
  153. {webbee-0.7.6 → webbee-0.7.8}/tests/test_tui_hardening.py +0 -0
  154. {webbee-0.7.6 → webbee-0.7.8}/tests/test_update.py +0 -0
  155. {webbee-0.7.6 → webbee-0.7.8}/tests/test_update_badge_0340.py +0 -0
  156. {webbee-0.7.6 → webbee-0.7.8}/tests/test_urlopen.py +0 -0
  157. {webbee-0.7.6 → webbee-0.7.8}/tests/test_version.py +0 -0
  158. {webbee-0.7.6 → webbee-0.7.8}/tests/test_version_badge_corner.py +0 -0
  159. {webbee-0.7.6 → webbee-0.7.8}/tests/test_wallet.py +0 -0
  160. {webbee-0.7.6 → webbee-0.7.8}/tests/test_worktrees.py +0 -0
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.8
4
+
5
+ * The webbee beside the shell's imperal-mcp (its code tools) follows webbee's releases. Live: webbee 0.7.7 was out,
6
+ imperal-mcp had no new release, and its environment kept webbee 0.7.6 because only imperal-mcp's version was
7
+ compared.
8
+
9
+ ## 0.7.7
10
+
11
+ * `pipx reinstall webbee` brings the newest release for real (live check of 0.7.6: it still gave 0.7.5).
12
+ * imperal-mcp is installed beside webbee without its code extra. The extra requires webbee, and on a reinstall
13
+ pipx (uv backend) took that webbee from PyPI over the git one while PyPI's index still showed the release before.
14
+ webbee's own dependencies already bring what the extra holds.
15
+ * An install recorded with that extra is moved once onto the corrected record.
16
+
3
17
  ## 0.7.6
4
18
 
5
19
  * **Always the newest release, from git** (06.11: `pipx reinstall webbee` put back 0.6.7 while 0.7.5 was out).
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: webbee
3
- Version: 0.7.6
3
+ Version: 0.7.8
4
4
  Summary: Webbee 🐝 — the Imperal Cloud coding agent in your terminal
5
5
  Project-URL: Homepage, https://imperal.io
6
6
  Project-URL: Documentation, https://docs.imperal.io
@@ -16,7 +16,7 @@ GH="https://github.com/imperalcloud"
16
16
  if command -v git >/dev/null 2>&1; then
17
17
  echo "Installing webbee (newest release, from git)…"
18
18
  uv tool install --force --refresh \
19
- --with "imperal-mcp[code] @ git+$GH/imperal-mcp.git@stable" \
19
+ --with "imperal-mcp @ git+$GH/imperal-mcp.git@stable" \
20
20
  --with "imperal-sdk[universal-index] @ git+$GH/imperal-sdk.git@stable" \
21
21
  "webbee @ git+$GH/webbee-code.git@stable"
22
22
  else
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "webbee"
3
- version = "0.7.6"
3
+ version = "0.7.8"
4
4
  description = "Webbee 🐝 — the Imperal Cloud coding agent in your terminal"
5
5
  readme = "README.md"
6
6
  license = "GPL-3.0-or-later"
@@ -0,0 +1 @@
1
+ __version__ = "0.7.8"
@@ -35,12 +35,18 @@ REPOS = {DIST: "https://github.com/imperalcloud/webbee-code.git",
35
35
  NAMES = {DIST: DIST, MCP: MCP_SPEC, SDK: SDK_SPEC} # each with the extras its install needs
36
36
 
37
37
 
38
- def git_spec(dist: str, ref: str = STABLE) -> str:
38
+ def git_spec(dist: str, ref: str = STABLE, extras: bool = True) -> str:
39
39
  """``dist`` (with its extras) from its repository at ``ref`` -- the ``stable`` branch unless a tag is named."""
40
- return f"{NAMES[dist]} @ git+{REPOS[dist]}@{ref}"
40
+ return f"{NAMES[dist] if extras else dist} @ git+{REPOS[dist]}@{ref}"
41
41
 
42
42
 
43
43
  MCP_GIT_SPEC = git_spec(MCP)
44
+ # imperal-mcp beside webbee goes WITHOUT its code extra: the extra requires webbee, and pipx (uv backend) installed
45
+ # that webbee from PyPI over the git one on a reinstall (live 2026-11-06: stable 0.7.6, `pipx reinstall` gave PyPI's
46
+ # 0.7.5). webbee's own dependencies already hold what the extra brings (tree-sitter, the sdk's index).
47
+ MCP_BESIDE_WEBBEE = git_spec(MCP, extras=False)
48
+ WEBBEE_ENV_SPECS = (git_spec(DIST), MCP_BESIDE_WEBBEE, git_spec(SDK)) # webbee's own environment
49
+ MCP_ENV_SPECS = (MCP_GIT_SPEC, git_spec(SDK), git_spec(DIST)) # the shell's imperal-mcp
44
50
  PYPI = "https://pypi.org/pypi/{}/json"
45
51
  OFF_ENV = "WEBBEE_NO_AUTOUPDATE" # 1: never upgrade at the start (CI, a pinned setup)
46
52
  DONE_ENV = "WEBBEE_UPGRADED" # set on the restart after a self-upgrade: never twice in a row
@@ -124,9 +130,11 @@ def stable_version(dist: str, git, run=subprocess.run):
124
130
  return None
125
131
 
126
132
 
127
- def on_stable(tool: str, prefix: str, dists) -> bool:
133
+ def on_stable(tool: str, prefix: str, specs) -> bool:
128
134
  """What pipx / uv will install again for the environment at ``prefix`` (``pipx reinstall``, ``pipx upgrade``,
129
- ``uv tool upgrade``) names each of ``dists`` by its repository's stable branch. Plain pip keeps no such record."""
135
+ ``uv tool upgrade``) is ``specs``, each by its repository's stable branch. pipx: each spec exactly as given (an
136
+ imperal-mcp recorded beside webbee WITH its code extra pulled webbee from PyPI on a reinstall); uv resolves the
137
+ record as one set, so its repository and branch suffice. Plain pip keeps no such record."""
130
138
  try:
131
139
  if tool == "pipx":
132
140
  import json
@@ -134,11 +142,12 @@ def on_stable(tool: str, prefix: str, dists) -> bool:
134
142
  meta = json.load(f)
135
143
  recorded = [str((meta.get("main_package") or {}).get("package_or_url") or "")]
136
144
  recorded += [str((p or {}).get("package_or_url") or "") for p in (meta.get("injected_packages") or {}).values()]
137
- return all(any(f"git+{REPOS[d]}@{STABLE}" in s for s in recorded) for d in dists)
145
+ return all(s in recorded for s in specs)
138
146
  if tool == "uv":
139
147
  with open(os.path.join(prefix, "uv-receipt.toml"), encoding="utf-8") as f:
140
148
  receipt = f.read()
141
- return all(f'git = "{REPOS[d]}?rev={STABLE}"' in receipt for d in dists)
149
+ urls = [s.split(" @ git+", 1)[-1].rsplit("@", 1)[0] for s in specs]
150
+ return all(f'git = "{u}?rev={STABLE}"' in receipt for u in urls)
142
151
  except Exception:
143
152
  return False
144
153
  return True
@@ -201,7 +210,7 @@ def self_command(version: str, prefix: str = sys.prefix, executable: str = sys.e
201
210
  beside it; ``record``: pipx writes its record anew (see ``_git_install``)."""
202
211
  light = env.get(LIGHT_ENV)
203
212
  if source == "git" and not light:
204
- return _git_install(tool_of(prefix), git_spec(DIST, ref), [git_spec(MCP), git_spec(SDK)], uv, pipx,
213
+ return _git_install(tool_of(prefix), git_spec(DIST, ref), [MCP_BESIDE_WEBBEE, git_spec(SDK)], uv, pipx,
205
214
  executable, DIST, record)
206
215
  spec = _spec(DIST, DIST, version, wheel)
207
216
  if light: # an old Python runs this release through its light install
@@ -234,16 +243,17 @@ def mcp_installed(which=safe_which, run=subprocess.run) -> dict:
234
243
  real = os.path.realpath(exe)
235
244
  py = os.path.join(os.path.dirname(real), "python.exe" if _WINDOWS else "python")
236
245
  out = {"tool": tool_of(real), "python": py, "prefix": os.path.dirname(os.path.dirname(real)), "version": "",
237
- "code": False}
246
+ "code": False, "webbee": ""}
238
247
  # asked twice when it says "no code extra": a reinstall is never made on one odd answer (live 2026-10-25: right after
239
248
  # another install the first answer said no webbee, the next ones yes)
240
249
  for _ in range(2):
241
250
  try:
242
251
  got = run([py, "-c", _NO_CWD + "import importlib.metadata as m, importlib.util as u; "
243
- "print(m.version('imperal-mcp'), u.find_spec('webbee') is not None)"],
252
+ "w = u.find_spec('webbee') is not None; "
253
+ "print(m.version('imperal-mcp'), w, m.version('webbee') if w else '')"],
244
254
  capture_output=True, text=True, timeout=20)
245
- version, code = (str(got.stdout or "").split() + ["", ""])[:2]
246
- out.update(version=version, code=code == "True")
255
+ version, code, webbee = (str(got.stdout or "").split() + ["", "", ""])[:3]
256
+ out.update(version=version, code=code == "True", webbee=webbee)
247
257
  except Exception:
248
258
  pass
249
259
  if out["code"] or not out["version"]:
@@ -251,14 +261,19 @@ def mcp_installed(which=safe_which, run=subprocess.run) -> dict:
251
261
  return out
252
262
 
253
263
 
254
- def mcp_command(inst: dict, newest, uv, pipx, wheel=None, source: str = "pypi"):
264
+ def mcp_command(inst: dict, newest, uv, pipx, wheel=None, source: str = "pypi", webbee_newest=None):
255
265
  """The one command that makes the shell's imperal-mcp the newest with its code extra -- by the tool that installed
256
266
  it (uv, else pipx, for a new install) -- or None when it already is, or nothing can do it. ``source`` "git": from
257
267
  the stable branches, imperal-sdk and webbee beside it, and an install recorded by a version is moved onto the
258
- branch even when its version is the newest (what ``pipx reinstall`` / ``uv tool upgrade`` would put back)."""
268
+ branch even when its version is the newest (what ``pipx reinstall`` / ``uv tool upgrade`` would put back). The
269
+ webbee beside it (its code tools) follows webbee's own releases: one newer than it holds upgrades the environment
270
+ too (live 2026-11-06: webbee 0.7.7 out, imperal-mcp unchanged, its environment kept webbee 0.7.6)."""
259
271
  git = source == "git"
260
- recorded = bool(inst) and on_stable(inst.get("tool") or "", inst.get("prefix") or "", (MCP, SDK, DIST))
261
- if inst and inst.get("version") and inst.get("version") == newest and inst.get("code") and (recorded or not git):
272
+ recorded = bool(inst) and on_stable(inst.get("tool") or "", inst.get("prefix") or "", MCP_ENV_SPECS)
273
+ behind = git and bool(inst) and bool(inst.get("webbee")) and bool(webbee_newest) \
274
+ and _ver(inst.get("webbee")) < _ver(webbee_newest)
275
+ if inst and inst.get("version") and inst.get("version") == newest and inst.get("code") and (recorded or not git) \
276
+ and not behind:
262
277
  return None
263
278
  if inst and not inst.get("version"):
264
279
  return None # it did not answer: a working install is never replaced blind
@@ -471,7 +486,7 @@ def upgrade_before_start(argv, current: str, say=print, fetch=latest, which=safe
471
486
  git = None if light else which("git") # 3.7's pip builds no package from source: the light install stays on PyPI
472
487
  newest, source = _newest(DIST, git, fetch, stable)
473
488
  tool = tool_of(prefix)
474
- recorded = source != "git" or on_stable(tool, prefix, (DIST, MCP, SDK))
489
+ recorded = source != "git" or on_stable(tool, prefix, WEBBEE_ENV_SPECS)
475
490
  # the same release recorded by a version: moved onto the branch (what pipx/uv would put back on a reinstall)
476
491
  if newest and (_ver(newest) > _ver(current) or (not recorded and _ver(newest) == _ver(current))) \
477
492
  and not env.get(DONE_ENV):
@@ -503,9 +518,13 @@ def upgrade_before_start(argv, current: str, say=print, fetch=latest, which=safe
503
518
  say(f"🐝 webbee could not update itself -- run: {_show(cmd)}")
504
519
  mcp_newest, mcp_source = _newest(MCP, git, fetch, stable)
505
520
  inst = mcp_installed(which, run)
506
- cmd = mcp_command(inst, mcp_newest, uv, pipx, wheel, source=mcp_source)
521
+ cmd = mcp_command(inst, mcp_newest, uv, pipx, wheel, source=mcp_source,
522
+ webbee_newest=newest if source == "git" else None)
507
523
  if cmd:
508
- what = (f"imperal-mcp {inst.get('version') or '?'} -> {mcp_newest or 'source'}" if inst
524
+ what = (f"imperal-mcp {inst.get('version') or '?'} -> {mcp_newest or 'source'}"
525
+ + (f", webbee beside it {inst.get('webbee')} -> {newest}"
526
+ if source == "git" and inst.get("webbee") and _ver(inst.get("webbee")) < _ver(newest) else "")
527
+ if inst
509
528
  else f"imperal-mcp {mcp_newest or 'from source'}") + (" (git, stable)" if mcp_source == "git" else "")
510
529
  say(f"🐝 updating {what} ...")
511
530
  if _run(cmd, run):
@@ -304,6 +304,7 @@ def test_an_upgrader_that_dies_during_the_wait_is_taken_over(tmp_path):
304
304
  # ── owner 2026-11-06: always the newest, from git. Live: `pipx reinstall webbee` put back 0.6.7 while 0.7.5 was out --
305
305
  # pipx had recorded 0.6.7's wheel address and reinstalls its record; the record is now the stable branch ──
306
306
  WB, MCP_G, SDK_G = au.git_spec(au.DIST), au.git_spec(au.MCP), au.git_spec(au.SDK)
307
+ MCP_W = au.MCP_BESIDE_WEBBEE # beside webbee: no code extra (it would pull webbee from PyPI)
307
308
  H_OLD, H_NEW, H_TAG = "a" * 40, "b" * 40, "c" * 40
308
309
 
309
310
 
@@ -332,6 +333,7 @@ def test_the_spec_is_the_branch_never_a_version():
332
333
  assert MCP_G == "imperal-mcp[code] @ git+https://github.com/imperalcloud/imperal-mcp.git@stable"
333
334
  assert SDK_G == "imperal-sdk[universal-index] @ git+https://github.com/imperalcloud/imperal-sdk.git@stable"
334
335
  assert au.MCP_GIT_SPEC == MCP_G
336
+ assert MCP_W == "imperal-mcp @ git+https://github.com/imperalcloud/imperal-mcp.git@stable"
335
337
 
336
338
 
337
339
  def _pipx_venv(tmp_path, main, injected=()):
@@ -346,36 +348,38 @@ def _pipx_venv(tmp_path, main, injected=()):
346
348
 
347
349
  def test_what_pipx_and_uv_recorded_is_read(tmp_path):
348
350
  pinned = _pipx_venv(tmp_path / "a", "webbee @ https://files.pythonhosted.org/x/webbee-0.6.7-py3-none-any.whl")
349
- assert not au.on_stable("pipx", pinned, ("webbee",))
350
- good = _pipx_venv(tmp_path / "b", WB, (MCP_G, SDK_G))
351
- assert au.on_stable("pipx", good, ("webbee", "imperal-mcp", "imperal-sdk"))
351
+ assert not au.on_stable("pipx", pinned, (WB,))
352
+ good = _pipx_venv(tmp_path / "b", WB, (MCP_W, SDK_G))
353
+ assert au.on_stable("pipx", good, au.WEBBEE_ENV_SPECS)
354
+ coded = _pipx_venv(tmp_path / "d", WB, (MCP_G, SDK_G)) # mcp WITH its code extra beside webbee
355
+ assert not au.on_stable("pipx", coded, au.WEBBEE_ENV_SPECS)
352
356
  half = _pipx_venv(tmp_path / "c", WB) # its packages still from PyPI
353
- assert not au.on_stable("pipx", half, ("webbee", "imperal-mcp"))
357
+ assert not au.on_stable("pipx", half, au.WEBBEE_ENV_SPECS)
354
358
  uv = tmp_path / "uv" / "tools" / "imperal-mcp"
355
359
  uv.mkdir(parents=True)
356
360
  (uv / "uv-receipt.toml").write_text('[tool]\nrequirements = [{ name = "imperal-mcp", extras = ["code"], '
357
361
  'specifier = "==0.9.5" }]\n')
358
- assert not au.on_stable("uv", str(uv), ("imperal-mcp",))
362
+ assert not au.on_stable("uv", str(uv), (MCP_G,))
359
363
  (uv / "uv-receipt.toml").write_text(
360
364
  '[tool]\nrequirements = [\n'
361
365
  ' { name = "imperal-mcp", extras = ["code"], git = "https://github.com/imperalcloud/imperal-mcp.git?rev=stable" },\n'
362
366
  ' { name = "imperal-sdk", extras = ["universal-index"], git = "https://github.com/imperalcloud/imperal-sdk.git?rev=stable" },\n'
363
367
  ' { name = "webbee", git = "https://github.com/imperalcloud/webbee-code.git?rev=stable" },\n]\n')
364
- assert au.on_stable("uv", str(uv), ("imperal-mcp", "imperal-sdk", "webbee"))
365
- assert not au.on_stable("pipx", str(tmp_path / "missing"), ("webbee",)) # unreadable: moved, never assumed
366
- assert au.on_stable("pip", "/venv", ("webbee",)) # plain pip keeps no record
368
+ assert au.on_stable("uv", str(uv), au.MCP_ENV_SPECS)
369
+ assert not au.on_stable("pipx", str(tmp_path / "missing"), (WB,)) # unreadable: moved, never assumed
370
+ assert au.on_stable("pip", "/venv", (WB,)) # plain pip keeps no record
367
371
 
368
372
 
369
373
  def test_from_git_each_tool_installs_and_records_the_branch():
370
374
  uv = au.self_command("0.7.5", prefix="/u/.local/share/uv/tools/webbee", env={}, uv="/bin/uv", source="git")
371
- assert uv == ["/bin/uv", "tool", "install", "--force", "--refresh", "--with", MCP_G, "--with", SDK_G, WB]
375
+ assert uv == ["/bin/uv", "tool", "install", "--force", "--refresh", "--with", MCP_W, "--with", SDK_G, WB]
372
376
  px = au.self_command("0.7.5", prefix="/h/.local/pipx/venvs/webbee", env={}, pipx="/bin/pipx", source="git")
373
- assert px == [["/bin/pipx", "install", "--force", WB], ["/bin/pipx", "inject", "--force", "webbee", MCP_G, SDK_G]]
377
+ assert px == [["/bin/pipx", "install", "--force", WB], ["/bin/pipx", "inject", "--force", "webbee", MCP_W, SDK_G]]
374
378
  kept = au.self_command("0.7.5", prefix="/h/.local/pipx/venvs/webbee", env={}, pipx="/bin/pipx", source="git",
375
379
  record=False) # already on the branch: its own record
376
380
  assert kept == ["/bin/pipx", "upgrade", "--include-injected", "webbee"]
377
381
  pip = au.self_command("0.7.5", prefix="/v", executable="/v/bin/python", env={}, source="git")
378
- assert pip == ["/v/bin/python", "-m", "pip", "install", "--upgrade", "--no-cache-dir", WB, MCP_G, SDK_G]
382
+ assert pip == ["/v/bin/python", "-m", "pip", "install", "--upgrade", "--no-cache-dir", WB, MCP_W, SDK_G]
379
383
  light = au.self_command("0.7.5", env={au.LIGHT_ENV: "/usr/bin/python3.7"}, source="git", wheel=None)
380
384
  assert light[-1] == "webbee==0.7.5" # 3.7 builds nothing from source: PyPI
381
385
 
@@ -389,12 +393,12 @@ def test_the_same_release_recorded_by_a_version_is_moved_onto_the_branch(tmp_pat
389
393
  execv=lambda p, a: execd.append(a), lock_path=lambda: str(tmp_path / "upgrade.lock"),
390
394
  stable=lambda d, git: stable(d), prefix=venv)
391
395
  assert ["/bin/pipx", "install", "--force", WB] in run.calls
392
- assert ["/bin/pipx", "inject", "--force", "webbee", MCP_G, SDK_G] in run.calls
396
+ assert ["/bin/pipx", "inject", "--force", "webbee", MCP_W, SDK_G] in run.calls
393
397
  assert execd and any("onto its release branch" in s for s in said)
394
398
 
395
399
 
396
400
  def test_on_the_branch_and_newest_nothing_is_installed(tmp_path, monkeypatch):
397
- venv = _pipx_venv(tmp_path, WB, (MCP_G, SDK_G))
401
+ venv = _pipx_venv(tmp_path, WB, (MCP_W, SDK_G))
398
402
  exe = tmp_path / "pipx" / "venvs" / "imperal-mcp" / "bin" / "imperal-mcp"
399
403
  exe.parent.mkdir(parents=True)
400
404
  exe.write_text("")
@@ -411,7 +415,7 @@ def test_on_the_branch_and_newest_nothing_is_installed(tmp_path, monkeypatch):
411
415
 
412
416
 
413
417
  def test_a_newer_release_on_the_branch_is_taken_by_the_install_s_own_record(tmp_path):
414
- venv = _pipx_venv(tmp_path, WB, (MCP_G, SDK_G))
418
+ venv = _pipx_venv(tmp_path, WB, (MCP_W, SDK_G))
415
419
  run, execd = _Run(), []
416
420
  au.upgrade_before_start([], "0.7.4", say=lambda m: None, fetch=lambda d: 1 / 0, wheel=None,
417
421
  which=_which({"pipx": "/bin/pipx", "git": "/bin/git"}), run=run, env={},
@@ -457,7 +461,7 @@ def test_the_steps_of_one_install_stop_at_the_first_that_fails():
457
461
 
458
462
  def test_a_git_release_that_does_not_import_is_put_back_by_its_own_tag(tmp_path, monkeypatch):
459
463
  _checks(monkeypatch, [False, False, True])
460
- venv = _pipx_venv(tmp_path, WB, (MCP_G, SDK_G))
464
+ venv = _pipx_venv(tmp_path, WB, (MCP_W, SDK_G))
461
465
  run, said = _Run(), []
462
466
  au.upgrade_before_start([], "0.7.4", say=said.append, fetch=lambda d: 1 / 0, wheel=None,
463
467
  which=_which({"pipx": "/bin/pipx", "git": "/bin/git"}), run=run, env={},
@@ -465,3 +469,21 @@ def test_a_git_release_that_does_not_import_is_put_back_by_its_own_tag(tmp_path,
465
469
  stable=lambda d, git: "0.7.5", prefix=venv)
466
470
  assert ["/bin/pipx", "install", "--force", au.git_spec("webbee", "v0.7.4")] in run.calls
467
471
  assert any("put 0.7.4 back" in s for s in said)
472
+
473
+
474
+ def test_the_webbee_beside_imperal_mcp_follows_webbee_s_releases(tmp_path):
475
+ uv = tmp_path / "uv" / "tools" / "imperal-mcp"
476
+ uv.mkdir(parents=True)
477
+ (uv / "uv-receipt.toml").write_text("".join(
478
+ f'{{ name = "x", git = "{u}?rev=stable" }}\n' for u in au.REPOS.values()))
479
+ inst = {"tool": "uv", "python": str(uv / "bin" / "python"), "prefix": str(uv), "version": "0.9.5", "code": True,
480
+ "webbee": "0.7.6"}
481
+ assert au.mcp_command(inst, "0.9.5", "/bin/uv", None, source="git", webbee_newest="0.7.6") is None
482
+ assert au.mcp_command(inst, "0.9.5", "/bin/uv", None, source="git", webbee_newest="0.7.7") == [
483
+ "/bin/uv", "tool", "install", "--force", "--refresh", "--with", SDK_G, "--with", WB, MCP_G]
484
+ run = _Run(stdout="0.9.5 True 0.7.6")
485
+ exe = uv / "bin" / "imperal-mcp"
486
+ exe.parent.mkdir()
487
+ exe.write_text("")
488
+ got = au.mcp_installed(which=lambda n: str(exe), run=run)
489
+ assert got["webbee"] == "0.7.6" and got["code"] is True
@@ -35,7 +35,7 @@ def test_install_script_is_posix_and_uses_uv():
35
35
  # must be the plain form.
36
36
  # 2026-11-06: always the newest release, from git -- the three packages by their stable branch (never a version,
37
37
  # so `uv tool upgrade` and a second run bring the newest); without git the plain form from PyPI
38
- for spec in ('"webbee @ git+$GH/webbee-code.git@stable"', '"imperal-mcp[code] @ git+$GH/imperal-mcp.git@stable"',
38
+ for spec in ('"webbee @ git+$GH/webbee-code.git@stable"', '"imperal-mcp @ git+$GH/imperal-mcp.git@stable"',
39
39
  '"imperal-sdk[universal-index] @ git+$GH/imperal-sdk.git@stable"'):
40
40
  assert spec in txt
41
41
  assert 'uv tool install --force --refresh "webbee"' in txt
@@ -1 +0,0 @@
1
- __version__ = "0.7.6"
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
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
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