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.
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/PKG-INFO +5 -5
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/README.md +4 -4
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/__init__.py +1 -1
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/gui_installer.py +0 -31
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/upgrade.py +13 -2
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit.egg-info/PKG-INFO +5 -5
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_upgrade.py +28 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/LICENSE +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/__main__.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/advertise.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/autostart.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/autostart_gate.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/cli.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/cron_installer.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/discovery.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/host.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/icons.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/identity.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/install.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/onboarding.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/settings.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/skills.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/sources.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/state.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/sweep.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/taxonomy/__init__.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/taxonomy/build.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/taxonomy/capability.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/taxonomy/cluster.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/taxonomy/corpus.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/taxonomy/embedder.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/taxonomy/groups.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/taxonomy/llm_groups.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/testing.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/tool_installer.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit/tui_installer.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit.egg-info/SOURCES.txt +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit.egg-info/dependency_links.txt +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit.egg-info/entry_points.txt +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit.egg-info/requires.txt +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/cli_tools_kit.egg-info/top_level.txt +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/pyproject.toml +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/setup.cfg +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_autostart_gate.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_capability_groups.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_cron_installer.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_e2e.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_engine.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_gui_smoke.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_hooks.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_host.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_identity.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_llm_groups.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_public_api.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_skills.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_sources.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_taxonomy_cluster.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_testing.py +0 -0
- {cli_tools_kit-1.1.0 → cli_tools_kit-1.2.0}/tests/test_tool_installer.py +0 -0
- {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.
|
|
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
|
|
81
|
-
`gui_installer.run(root_dir=...,
|
|
82
|
-
(discovery layout, repo-cache
|
|
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
|
|
21
|
-
`gui_installer.run(root_dir=...,
|
|
22
|
-
(discovery layout, repo-cache
|
|
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
|
|
@@ -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.
|
|
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
|
|
81
|
-
`gui_installer.run(root_dir=...,
|
|
82
|
-
(discovery layout, repo-cache
|
|
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
|
|
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
|