cli-tools-kit 0.6.3__tar.gz → 0.6.4__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-0.6.3 → cli_tools_kit-0.6.4}/PKG-INFO +1 -1
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/cli_tools_kit/gui_installer.py +37 -3
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/cli_tools_kit/sources.py +9 -1
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/cli_tools_kit.egg-info/PKG-INFO +1 -1
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/pyproject.toml +1 -1
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/tests/test_sources.py +15 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/LICENSE +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/README.md +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/cli_tools_kit/__init__.py +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/cli_tools_kit/__main__.py +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/cli_tools_kit/advertise.py +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/cli_tools_kit/cron_installer.py +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/cli_tools_kit/host.py +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/cli_tools_kit/identity.py +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/cli_tools_kit/onboarding.py +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/cli_tools_kit/skills.py +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/cli_tools_kit/taxonomy/__init__.py +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/cli_tools_kit/taxonomy/build.py +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/cli_tools_kit/taxonomy/capability.py +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/cli_tools_kit/taxonomy/cluster.py +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/cli_tools_kit/taxonomy/corpus.py +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/cli_tools_kit/taxonomy/embedder.py +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/cli_tools_kit/taxonomy/groups.py +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/cli_tools_kit/taxonomy/llm_groups.py +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/cli_tools_kit/tool_installer.py +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/cli_tools_kit/tui_installer.py +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/cli_tools_kit.egg-info/SOURCES.txt +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/cli_tools_kit.egg-info/dependency_links.txt +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/cli_tools_kit.egg-info/entry_points.txt +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/cli_tools_kit.egg-info/requires.txt +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/cli_tools_kit.egg-info/top_level.txt +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/setup.cfg +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/tests/test_capability_groups.py +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/tests/test_cron_installer.py +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/tests/test_host.py +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/tests/test_identity.py +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/tests/test_llm_groups.py +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/tests/test_skills.py +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/tests/test_taxonomy_cluster.py +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/tests/test_tool_installer.py +0 -0
- {cli_tools_kit-0.6.3 → cli_tools_kit-0.6.4}/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: 0.6.
|
|
3
|
+
Version: 0.6.4
|
|
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
|
|
@@ -5434,13 +5434,37 @@ class InstallerApp:
|
|
|
5434
5434
|
# ================= CLI FUNCTIONS =================
|
|
5435
5435
|
|
|
5436
5436
|
def _self_shortcut_path() -> str:
|
|
5437
|
-
"""The manager's own shortcut: a .desktop file, or a .lnk on Windows.
|
|
5437
|
+
"""The manager's own shortcut: a .desktop file, or a .lnk on Windows.
|
|
5438
|
+
|
|
5439
|
+
Windows shows the file name in the Start Menu, where freedesktop reads the
|
|
5440
|
+
Name= line inside the file, so the .lnk is named after SELF_DESKTOP_NAME.
|
|
5441
|
+
Characters a file name cannot hold become spaces, and a name that is empty
|
|
5442
|
+
or all-punctuation falls back to the desktop file's stem.
|
|
5443
|
+
"""
|
|
5438
5444
|
if host.IS_WINDOWS:
|
|
5439
|
-
|
|
5440
|
-
|
|
5445
|
+
stem = "".join(" " if c in '<>:"/\\|?*' else c
|
|
5446
|
+
for c in SELF_DESKTOP_NAME).strip(" .")
|
|
5447
|
+
stem = " ".join(stem.split()) or os.path.splitext(SELF_DESKTOP_FILE)[0]
|
|
5448
|
+
return os.path.join(APPS_DIR, stem + ".lnk")
|
|
5441
5449
|
return os.path.join(APPS_DIR, SELF_DESKTOP_FILE)
|
|
5442
5450
|
|
|
5443
5451
|
|
|
5452
|
+
def _windows_icon(icon: str) -> Optional[str]:
|
|
5453
|
+
"""The .ico Windows can draw for `icon`, or None.
|
|
5454
|
+
|
|
5455
|
+
A .lnk renders only .ico/.exe/.dll; the shell stores a .png path without
|
|
5456
|
+
complaint and then draws nothing. Consumers configure one icon, normally a
|
|
5457
|
+
.png for freedesktop, so take a sibling .ico of the same stem when there is
|
|
5458
|
+
one and otherwise leave the shortcut on its default picture.
|
|
5459
|
+
"""
|
|
5460
|
+
if not icon:
|
|
5461
|
+
return None
|
|
5462
|
+
for candidate in (icon, os.path.splitext(icon)[0] + ".ico"):
|
|
5463
|
+
if candidate.lower().endswith(".ico") and os.path.isfile(candidate):
|
|
5464
|
+
return candidate
|
|
5465
|
+
return None
|
|
5466
|
+
|
|
5467
|
+
|
|
5444
5468
|
def cli_install_self(quiet: bool = False) -> tuple[bool, str]:
|
|
5445
5469
|
"""Install the manager's own desktop shortcut. Returns (success, message)."""
|
|
5446
5470
|
try:
|
|
@@ -5450,9 +5474,19 @@ def cli_install_self(quiet: bool = False) -> tuple[bool, str]:
|
|
|
5450
5474
|
if host.IS_WINDOWS:
|
|
5451
5475
|
lnk_path = _self_shortcut_path()
|
|
5452
5476
|
ok = host.write_shortcut(lnk_path, python_exec, f'"{script_path}"',
|
|
5477
|
+
icon=_windows_icon(SELF_DESKTOP_ICON),
|
|
5453
5478
|
workdir=ROOT_DIR)
|
|
5454
5479
|
if not ok:
|
|
5455
5480
|
return False, f"Could not write {lnk_path}"
|
|
5481
|
+
# Before 0.6.4 the .lnk was named after the desktop file's stem.
|
|
5482
|
+
# Drop that one, or the Start Menu keeps both.
|
|
5483
|
+
legacy = os.path.join(APPS_DIR,
|
|
5484
|
+
os.path.splitext(SELF_DESKTOP_FILE)[0] + ".lnk")
|
|
5485
|
+
if legacy != lnk_path and os.path.isfile(legacy):
|
|
5486
|
+
try:
|
|
5487
|
+
os.remove(legacy)
|
|
5488
|
+
except OSError:
|
|
5489
|
+
pass
|
|
5456
5490
|
if not quiet:
|
|
5457
5491
|
print(f"Installed: {lnk_path}")
|
|
5458
5492
|
return True, lnk_path
|
|
@@ -157,7 +157,15 @@ def save_local_root(config_path, root, local_path=None, log: Callable = print) -
|
|
|
157
157
|
except OSError as exc:
|
|
158
158
|
log(f"{local.name}: not updated ({exc})")
|
|
159
159
|
return False
|
|
160
|
-
|
|
160
|
+
# A TOML literal string, because a Windows root is full of backslashes and
|
|
161
|
+
# a basic string would read them as escapes: "C:\Users\..." dies on \U and
|
|
162
|
+
# takes the whole file with it, so the question would be asked again on
|
|
163
|
+
# every launch. A path holding a single quote falls back to a basic string
|
|
164
|
+
# with the two characters TOML needs escaped there.
|
|
165
|
+
if "'" in root:
|
|
166
|
+
line = 'root = "{}"\n'.format(root.replace("\\", "\\\\").replace('"', '\\"'))
|
|
167
|
+
else:
|
|
168
|
+
line = f"root = '{root}'\n"
|
|
161
169
|
try:
|
|
162
170
|
local.write_text(line + ("\n" + existing.lstrip("\n") if existing.strip() else ""),
|
|
163
171
|
encoding="utf-8")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cli-tools-kit
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.4
|
|
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
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "cli-tools-kit"
|
|
7
|
-
version = "0.6.
|
|
7
|
+
version = "0.6.4"
|
|
8
8
|
description = "Installer protocol + helpers for self-installing Python CLI/GUI tools (desktop shortcuts, bash aliases, cron entries), plus reusable tkinter and curses installer screens"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -445,6 +445,21 @@ def test_an_existing_root_in_the_local_file_is_never_overwritten(tmp_path: Path)
|
|
|
445
445
|
assert sources.local_root(config) == str(tmp_path / "mine")
|
|
446
446
|
|
|
447
447
|
|
|
448
|
+
def test_a_windows_root_survives_the_round_trip(tmp_path: Path) -> None:
|
|
449
|
+
"""A backslash path must not be written as a TOML basic string.
|
|
450
|
+
|
|
451
|
+
``root = "C:\\Users\\..."`` reads the \\U as a Unicode escape and kills the
|
|
452
|
+
whole file, so the install location was asked for again on every launch.
|
|
453
|
+
"""
|
|
454
|
+
roots = [r"C:\Users\tester\WW3-tools", r"C:\temp\new\table\unicode",
|
|
455
|
+
r"C:\Users\o'brien\tools"]
|
|
456
|
+
for i, root in enumerate(roots):
|
|
457
|
+
config = _write(tmp_path / str(i) / "installer.toml", "")
|
|
458
|
+
assert sources.save_local_root(config, root, log=lambda *a: None)
|
|
459
|
+
local = config.with_name("installer.local.toml")
|
|
460
|
+
assert sources._read_toml(local).get("root") == root
|
|
461
|
+
|
|
462
|
+
|
|
448
463
|
def test_the_default_root_name_names_the_suggested_folder(tmp_path: Path, engine,
|
|
449
464
|
monkeypatch) -> None:
|
|
450
465
|
config = _tree(tmp_path)
|
|
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
|