cli-tools-kit 1.1.0__tar.gz → 1.2.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 (60) hide show
  1. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/PKG-INFO +5 -5
  2. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/README.md +4 -4
  3. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/__init__.py +1 -1
  4. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/gui_installer.py +0 -31
  5. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/upgrade.py +13 -2
  6. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit.egg-info/PKG-INFO +5 -5
  7. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_upgrade.py +28 -0
  8. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/LICENSE +0 -0
  9. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/__main__.py +0 -0
  10. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/advertise.py +0 -0
  11. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/autostart.py +0 -0
  12. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/autostart_gate.py +0 -0
  13. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/cli.py +0 -0
  14. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/cron_installer.py +0 -0
  15. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/discovery.py +0 -0
  16. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/host.py +0 -0
  17. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/icons.py +0 -0
  18. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/identity.py +0 -0
  19. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/install.py +0 -0
  20. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/onboarding.py +0 -0
  21. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/settings.py +0 -0
  22. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/skills.py +0 -0
  23. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/sources.py +0 -0
  24. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/state.py +0 -0
  25. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/sweep.py +0 -0
  26. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/taxonomy/__init__.py +0 -0
  27. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/taxonomy/build.py +0 -0
  28. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/taxonomy/capability.py +0 -0
  29. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/taxonomy/cluster.py +0 -0
  30. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/taxonomy/corpus.py +0 -0
  31. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/taxonomy/embedder.py +0 -0
  32. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/taxonomy/groups.py +0 -0
  33. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/taxonomy/llm_groups.py +0 -0
  34. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/testing.py +0 -0
  35. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/tool_installer.py +0 -0
  36. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/tui_installer.py +0 -0
  37. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit.egg-info/SOURCES.txt +0 -0
  38. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit.egg-info/dependency_links.txt +0 -0
  39. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit.egg-info/entry_points.txt +0 -0
  40. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit.egg-info/requires.txt +0 -0
  41. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit.egg-info/top_level.txt +0 -0
  42. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/pyproject.toml +0 -0
  43. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/setup.cfg +0 -0
  44. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_autostart_gate.py +0 -0
  45. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_capability_groups.py +0 -0
  46. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_cron_installer.py +0 -0
  47. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_e2e.py +0 -0
  48. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_engine.py +0 -0
  49. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_gui_smoke.py +0 -0
  50. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_hooks.py +0 -0
  51. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_host.py +0 -0
  52. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_identity.py +0 -0
  53. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_llm_groups.py +0 -0
  54. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_public_api.py +0 -0
  55. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_skills.py +0 -0
  56. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_sources.py +0 -0
  57. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_taxonomy_cluster.py +0 -0
  58. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_testing.py +0 -0
  59. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_tool_installer.py +0 -0
  60. {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_tui_installer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cli-tools-kit
3
- Version: 1.1.0
3
+ Version: 1.2.0
4
4
  Summary: Installer protocol + helpers for self-installing Python CLI/GUI tools (desktop shortcuts, bash aliases, cron entries), plus reusable tkinter and curses installer screens
5
5
  Author: Steffen Probst
6
6
  License: MIT License
@@ -77,10 +77,10 @@ Provides:
77
77
  - **`gui_installer`** — a full, reusable tkinter GUI installer *engine*: it
78
78
  discovers every tool in a project tree that speaks `--advertise`, and offers
79
79
  batch install/remove, per-row skill toggles, themes, orphan cleanup, and an
80
- opt-in login update-check. A thin wrapper points it at its own tree via
81
- `gui_installer.run(root_dir=..., entry_script=...)`; everything else
82
- (discovery layout, repo-cache bootstrap, login-check policy, window/desktop
83
- identities) is configurable. See [§ GUI installer engine](#gui-installer-engine).
80
+ opt-in login update-check (`--enable-autostart-check`). A thin wrapper
81
+ points it at its own tree via `gui_installer.run(root_dir=...,
82
+ entry_script=...)`; everything else (discovery layout, repo-cache
83
+ bootstrap, login-check policy, window/desktop identities) is configurable. See [§ GUI installer engine](#gui-installer-engine).
84
84
 
85
85
  - **`sources`** — one installer offering tools from several repos. A TOML
86
86
  file lists them, the kit clones what is missing over HTTPS and hands the
@@ -17,10 +17,10 @@ Provides:
17
17
  - **`gui_installer`** — a full, reusable tkinter GUI installer *engine*: it
18
18
  discovers every tool in a project tree that speaks `--advertise`, and offers
19
19
  batch install/remove, per-row skill toggles, themes, orphan cleanup, and an
20
- opt-in login update-check. A thin wrapper points it at its own tree via
21
- `gui_installer.run(root_dir=..., entry_script=...)`; everything else
22
- (discovery layout, repo-cache bootstrap, login-check policy, window/desktop
23
- identities) is configurable. See [§ GUI installer engine](#gui-installer-engine).
20
+ opt-in login update-check (`--enable-autostart-check`). A thin wrapper
21
+ points it at its own tree via `gui_installer.run(root_dir=...,
22
+ entry_script=...)`; everything else (discovery layout, repo-cache
23
+ bootstrap, login-check policy, window/desktop identities) is configurable. See [§ GUI installer engine](#gui-installer-engine).
24
24
 
25
25
  - **`sources`** — one installer offering tools from several repos. A TOML
26
26
  file lists them, the kit clones what is missing over HTTPS and hands the
@@ -56,4 +56,4 @@ __all__ = [
56
56
  "read_installed_skill",
57
57
  ]
58
58
 
59
- __version__ = "1.1.0"
59
+ __version__ = "1.2.0"
@@ -1022,27 +1022,6 @@ class InstallerApp:
1022
1022
  # cleanup + .desktop/alias refresh, no pip).
1023
1023
  self._build_update_btn(footer)
1024
1024
 
1025
- # Login auto-check toggle: enable/disable the ~/.config/autostart entry that
1026
- # runs `installer.py --check` once per login — applies the same local-only
1027
- # reconciliations the badge does (no pip) and notifies for new tools. Same
1028
- # safety class as the badge, so it sits in the badge's group.
1029
- ttk.Separator(footer, orient="vertical").pack(side="left", fill="y", padx=15)
1030
- self._autostart_check_var = tk.BooleanVar(value=autostart.autostart_check_enabled())
1031
- self._autostart_check_cb = ttk.Checkbutton(
1032
- footer, text="Check on login", variable=self._autostart_check_var,
1033
- command=self._toggle_autostart_check)
1034
- self._autostart_check_cb.pack(side="left", padx=5)
1035
- self._attach_tooltip(
1036
- self._autostart_check_cb,
1037
- "Check for updates on login (local, no network)\n\n"
1038
- "Installs a startup entry that, once per login, applies "
1039
- "network-free reconciliations (drifted shortcuts, renamed aliases, and "
1040
- "stale installed skills — rewritten from the tool's source on disk, "
1041
- "never pip) and only notifies for updates that need the network (a new, "
1042
- "not-yet-installed tool).\n\n"
1043
- f"Entry: {state.AUTOSTART_CHECK_DESKTOP}\n"
1044
- f"Log: {state.CHECK_LOG}")
1045
-
1046
1025
  # "Reinstall deps" is the deliberate, network-touching pip action — kept in
1047
1026
  # its own group (own separator) so it reads as distinct from the local badge.
1048
1027
  ttk.Separator(footer, orient="vertical").pack(side="left", fill="y", padx=15)
@@ -4064,16 +4043,6 @@ class InstallerApp:
4064
4043
  self.root.destroy()
4065
4044
  host.restart(argv)
4066
4045
 
4067
- def _toggle_autostart_check(self):
4068
- """Enable/disable the login update-check autostart entry."""
4069
- if self._autostart_check_var.get():
4070
- path = autostart.enable_autostart_check()
4071
- self._log(f"Login update check enabled → {path}", "success")
4072
- self._log(" On login: applies local reconciliations (no pip), notifies for new tools.", "info")
4073
- else:
4074
- autostart.disable_autostart_check()
4075
- self._log("Login update check disabled.", "info")
4076
-
4077
4046
  def _reinstall_deps(self):
4078
4047
  """Full reinstall (pip dependencies + shortcuts) for every installed tool.
4079
4048
 
@@ -246,7 +246,8 @@ def upgrade(items: Sequence[Item], tools: Sequence[discovery.ToolEntry],
246
246
  (through ``install.install_tool``, so a wrapper's hooks apply), because its
247
247
  code and its requirements may have changed. The kit is upgraded when it is
248
248
  in ``items`` or the installer's own checkout was pulled, since that can
249
- change the installer's pin.
249
+ change the installer's pin. It is never replaced by an older version, even
250
+ when the pulled installer still pins one.
250
251
 
251
252
  Returns ``{"changed": bool, "errors": int}``. ``changed`` means the running
252
253
  installer is out of date and should restart.
@@ -269,10 +270,20 @@ def upgrade(items: Sequence[Item], tools: Sequence[discovery.ToolEntry],
269
270
  installer_pulled = any(i.kind == "installer" and i.path in pulled for i in items)
270
271
  if any(i.kind == "kit" for i in items) or installer_pulled:
271
272
  blocker = kit_blocker()
273
+ # Ask again after the pull: an installer that still pins an older kit
274
+ # exactly would otherwise make pip downgrade the one running now.
275
+ latest = None if blocker else latest_kit()
276
+ current = kit_version()
272
277
  if blocker:
273
278
  log(f"cli-tools-kit left as it is: {blocker}", "info")
279
+ elif latest is None:
280
+ errors += 1
281
+ log(" ✗ pip could not say which cli-tools-kit the installer asks for", "error")
282
+ elif _version_key(latest) <= _version_key(current):
283
+ log(f"cli-tools-kit stays at {current}: nothing newer within the "
284
+ "installer's pin", "info")
274
285
  else:
275
- log("Upgrading cli-tools-kit", "header")
286
+ log(f"Upgrading cli-tools-kit {current} → {latest}", "header")
276
287
  cmd = [sys.executable, "-m", "pip", "install", "--upgrade",
277
288
  "--disable-pip-version-check", *kit_requirement()]
278
289
  try:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cli-tools-kit
3
- Version: 1.1.0
3
+ Version: 1.2.0
4
4
  Summary: Installer protocol + helpers for self-installing Python CLI/GUI tools (desktop shortcuts, bash aliases, cron entries), plus reusable tkinter and curses installer screens
5
5
  Author: Steffen Probst
6
6
  License: MIT License
@@ -77,10 +77,10 @@ Provides:
77
77
  - **`gui_installer`** — a full, reusable tkinter GUI installer *engine*: it
78
78
  discovers every tool in a project tree that speaks `--advertise`, and offers
79
79
  batch install/remove, per-row skill toggles, themes, orphan cleanup, and an
80
- opt-in login update-check. A thin wrapper points it at its own tree via
81
- `gui_installer.run(root_dir=..., entry_script=...)`; everything else
82
- (discovery layout, repo-cache bootstrap, login-check policy, window/desktop
83
- identities) is configurable. See [§ GUI installer engine](#gui-installer-engine).
80
+ opt-in login update-check (`--enable-autostart-check`). A thin wrapper
81
+ points it at its own tree via `gui_installer.run(root_dir=...,
82
+ entry_script=...)`; everything else (discovery layout, repo-cache
83
+ bootstrap, login-check policy, window/desktop identities) is configurable. See [§ GUI installer engine](#gui-installer-engine).
84
84
 
85
85
  - **`sources`** — one installer offering tools from several repos. A TOML
86
86
  file lists them, the kit clones what is missing over HTTPS and hands the
@@ -119,6 +119,34 @@ def test_the_installer_checkout_is_checked_and_its_tools_reinstalled(
119
119
  assert any("cli-tools-kit left as it is: not under test" in line for line in lines)
120
120
 
121
121
 
122
+ @pytest.mark.parametrize("pinned, pip_runs", [("0.7.1", False), (None, False), ("99.0.0", True)])
123
+ def test_a_pulled_installer_never_downgrades_the_kit(
124
+ remote, monkeypatch: pytest.MonkeyPatch, pinned, pip_runs) -> None:
125
+ clone, push = remote
126
+ state.ENTRY_SCRIPT = str(clone / "installer.py")
127
+ push()
128
+ items = upgrade.check(force=True)
129
+ # pinned None: the installer's pin matches the running kit exactly.
130
+ monkeypatch.setattr(upgrade, "kit_blocker", lambda: None)
131
+ monkeypatch.setattr(upgrade, "latest_kit", lambda: pinned or upgrade.kit_version())
132
+ pip_calls = []
133
+ real_run = upgrade.subprocess.run
134
+
135
+ def run(cmd, **kwargs):
136
+ if "pip" in cmd:
137
+ pip_calls.append(cmd)
138
+ return _Result(0, "")
139
+ return real_run(cmd, **kwargs)
140
+
141
+ monkeypatch.setattr(upgrade.subprocess, "run", run)
142
+ lines = []
143
+ result = upgrade.upgrade(items, [], lambda msg, tag="info": lines.append(msg))
144
+ assert result == {"changed": True, "errors": 0}
145
+ assert bool(pip_calls) == pip_runs
146
+ if not pip_runs:
147
+ assert any("stays at" in line for line in lines)
148
+
149
+
122
150
  def test_a_pull_that_cannot_fast_forward_is_an_error(remote) -> None:
123
151
  clone, push = remote
124
152
  state.UPGRADE_REPOS = [("tools", str(clone))]
File without changes
File without changes