shellscripts 0.23.1__py3-none-any.whl
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.
- shell_scripts/__init__.py +5 -0
- shell_scripts/__main__.py +5 -0
- shell_scripts/commands/__init__.py +81 -0
- shell_scripts/commands/_dc_common.py +138 -0
- shell_scripts/commands/ai_install.py +453 -0
- shell_scripts/commands/bin_links.py +78 -0
- shell_scripts/commands/claude.py +56 -0
- shell_scripts/commands/clean.py +98 -0
- shell_scripts/commands/codex.py +107 -0
- shell_scripts/commands/copilot.py +54 -0
- shell_scripts/commands/dicom2jpg.py +98 -0
- shell_scripts/commands/dicomviewer.py +83 -0
- shell_scripts/commands/diff_cmd.py +50 -0
- shell_scripts/commands/doxygen_cmd.py +159 -0
- shell_scripts/commands/edit_cmd.py +50 -0
- shell_scripts/commands/gemini.py +54 -0
- shell_scripts/commands/kiro.py +54 -0
- shell_scripts/commands/opencode.py +54 -0
- shell_scripts/commands/pdf_crop.py +520 -0
- shell_scripts/commands/pdf_merge.py +180 -0
- shell_scripts/commands/pdf_split_by_format.py +243 -0
- shell_scripts/commands/pdf_split_by_toc.py +197 -0
- shell_scripts/commands/pdf_tiler_090.py +61 -0
- shell_scripts/commands/pdf_tiler_100.py +61 -0
- shell_scripts/commands/pdf_toc_clean.py +175 -0
- shell_scripts/commands/pi.py +61 -0
- shell_scripts/commands/req_cmd.py +362 -0
- shell_scripts/commands/tests_cmd.py +87 -0
- shell_scripts/commands/ubuntu_dark_theme.py +64 -0
- shell_scripts/commands/venv_cmd.py +82 -0
- shell_scripts/commands/video2h264.py +89 -0
- shell_scripts/commands/video2h265.py +87 -0
- shell_scripts/commands/view_cmd.py +50 -0
- shell_scripts/commands/vscode_cmd.py +42 -0
- shell_scripts/commands/vsinsider_cmd.py +42 -0
- shell_scripts/config.py +328 -0
- shell_scripts/core.py +324 -0
- shell_scripts/utils.py +455 -0
- shell_scripts/version_check.py +323 -0
- shellscripts-0.23.1.dist-info/METADATA +310 -0
- shellscripts-0.23.1.dist-info/RECORD +45 -0
- shellscripts-0.23.1.dist-info/WHEEL +5 -0
- shellscripts-0.23.1.dist-info/entry_points.txt +3 -0
- shellscripts-0.23.1.dist-info/licenses/LICENSE +674 -0
- shellscripts-0.23.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""@brief Update-check orchestration for shellscripts startup.
|
|
3
|
+
|
|
4
|
+
@details Performs cooldown-gated PyPI registry checks for the startup update
|
|
5
|
+
notification, resolves GitHub release tags for develop upgrades and version
|
|
6
|
+
display, persists cache metadata for every request outcome, and prints
|
|
7
|
+
terminal-visible update or error status lines. Successful requests apply the
|
|
8
|
+
default cooldown. Request errors apply the enlarged cooldown without aborting
|
|
9
|
+
CLI startup continuity.
|
|
10
|
+
@satisfies PRJ-004, DES-002, DES-003, DES-004, DES-005, DES-006, DES-018, DES-019, DES-020, REQ-003, REQ-075, REQ-059, REQ-060, REQ-061
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
import sys
|
|
15
|
+
import time
|
|
16
|
+
import urllib.error
|
|
17
|
+
import urllib.request
|
|
18
|
+
from datetime import datetime, timezone
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Any
|
|
21
|
+
|
|
22
|
+
PROGRAM = "shellscripts"
|
|
23
|
+
OWNER = "Ogekuri"
|
|
24
|
+
REPOSITORY = "shellScripts"
|
|
25
|
+
IDLE_DELAY = 3600
|
|
26
|
+
HTTP_ERROR_IDLE_DELAY = 86400
|
|
27
|
+
HTTP_TIMEOUT = 2
|
|
28
|
+
GITHUB_API_URL = f"https://api.github.com/repos/{OWNER}/{REPOSITORY}/releases/latest"
|
|
29
|
+
PYPI_API_URL = f"https://pypi.org/pypi/{PROGRAM}/json"
|
|
30
|
+
CACHE_DIR = Path.home() / ".cache" / PROGRAM
|
|
31
|
+
IDLE_TIME_FILE = CACHE_DIR / "check_version_idle-time.json"
|
|
32
|
+
|
|
33
|
+
BRIGHT_GREEN = "\033[92m"
|
|
34
|
+
BRIGHT_RED = "\033[91m"
|
|
35
|
+
RESET = "\033[0m"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _read_idle_config() -> dict[str, object] | None:
|
|
39
|
+
"""@brief Load cached cooldown metadata for the version check.
|
|
40
|
+
|
|
41
|
+
@details Reads the cooldown JSON payload from the user cache directory.
|
|
42
|
+
Absent files and unreadable JSON payloads resolve to `None`. Complexity:
|
|
43
|
+
O(1) for fixed-size payload parsing.
|
|
44
|
+
@return {dict[str, object] | None} Cached cooldown payload or `None`.
|
|
45
|
+
@satisfies DES-003, REQ-059
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
if not IDLE_TIME_FILE.exists():
|
|
49
|
+
return None
|
|
50
|
+
try:
|
|
51
|
+
with open(IDLE_TIME_FILE) as f:
|
|
52
|
+
return json.load(f)
|
|
53
|
+
except (json.JSONDecodeError, OSError):
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _write_idle_config(last_check_ts: float, idle_delay_seconds: int) -> None:
|
|
58
|
+
"""@brief Persist cooldown timestamps for the next version-check gate.
|
|
59
|
+
|
|
60
|
+
@details Derives the idle-until timestamp from the supplied delay, writes
|
|
61
|
+
both machine-readable timestamps and UTC-rendered strings, and stores the
|
|
62
|
+
applied delay for downstream inspection. Complexity: O(1).
|
|
63
|
+
@param last_check_ts {float} UNIX timestamp recorded for the current check.
|
|
64
|
+
@param idle_delay_seconds {int} Cooldown duration applied after the check.
|
|
65
|
+
@return {None} No return value.
|
|
66
|
+
@throws {OSError} Propagated when cache directory creation or file write fails.
|
|
67
|
+
@satisfies DES-003, DES-004, DES-005, REQ-061
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
CACHE_DIR.mkdir(parents=True, exist_ok=True)
|
|
71
|
+
idle_until_ts = last_check_ts + idle_delay_seconds
|
|
72
|
+
data = {
|
|
73
|
+
"last_check_timestamp": last_check_ts,
|
|
74
|
+
"last_check_human": datetime.fromtimestamp(
|
|
75
|
+
last_check_ts, tz=timezone.utc
|
|
76
|
+
).strftime("%Y-%m-%d %H:%M:%S UTC"),
|
|
77
|
+
"idle_delay_seconds": idle_delay_seconds,
|
|
78
|
+
"idle_until_timestamp": idle_until_ts,
|
|
79
|
+
"idle_until_human": datetime.fromtimestamp(
|
|
80
|
+
idle_until_ts, tz=timezone.utc
|
|
81
|
+
).strftime("%Y-%m-%d %H:%M:%S UTC"),
|
|
82
|
+
}
|
|
83
|
+
with open(IDLE_TIME_FILE, "w") as f:
|
|
84
|
+
json.dump(data, f, indent=2)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _is_forced_version_check() -> bool:
|
|
88
|
+
"""@brief Detect CLI flags that force the version-check HTTP request.
|
|
89
|
+
|
|
90
|
+
@details Evaluates the live process argument vector and returns `True` when
|
|
91
|
+
the current invocation requested `--version` or `--ver`. Complexity: O(N)
|
|
92
|
+
where N is the number of CLI arguments.
|
|
93
|
+
@return {bool} `True` when the request must bypass cooldown gating.
|
|
94
|
+
@satisfies REQ-003, REQ-059
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
return any(arg in ("--version", "--ver") for arg in sys.argv[1:])
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _format_request_error(error: Exception) -> str:
|
|
101
|
+
"""@brief Convert a request exception into terminal output detail text.
|
|
102
|
+
|
|
103
|
+
@details Returns a stable, parser-friendly error descriptor for HTTP and
|
|
104
|
+
non-HTTP request failures. HTTP errors preserve the status code. Non-HTTP
|
|
105
|
+
errors expose the exception type and optional message. Complexity: O(1).
|
|
106
|
+
@param error {Exception} Request failure captured during update checking.
|
|
107
|
+
@return {str} Error detail suffix without terminal color codes.
|
|
108
|
+
@satisfies DES-005, DES-006, REQ-061
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
if isinstance(error, urllib.error.HTTPError):
|
|
112
|
+
if error.code in (403, 429):
|
|
113
|
+
return f"rate limit exceeded (HTTP {error.code})"
|
|
114
|
+
return f"HTTP {error.code}"
|
|
115
|
+
|
|
116
|
+
error_type = type(error).__name__
|
|
117
|
+
error_message = str(error).strip()
|
|
118
|
+
if error_message:
|
|
119
|
+
return f"{error_type}: {error_message}"
|
|
120
|
+
return error_type
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _handle_request_error(last_check_ts: float, error: Exception) -> None:
|
|
124
|
+
"""@brief Persist cooldown metadata and print the request error line.
|
|
125
|
+
|
|
126
|
+
@details Applies the fixed HTTP-error cooldown, updates the cooldown JSON,
|
|
127
|
+
and emits a bright-red terminal line for the supplied request failure.
|
|
128
|
+
Complexity: O(1) excluding filesystem latency.
|
|
129
|
+
@param last_check_ts {float} UNIX timestamp recorded for the failed check.
|
|
130
|
+
@param error {Exception} Request failure captured during update checking.
|
|
131
|
+
@return {None} No return value.
|
|
132
|
+
@throws {OSError} Propagated when cache directory creation or file write fails.
|
|
133
|
+
@satisfies DES-003, DES-005, DES-006, REQ-061
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
_write_idle_config(last_check_ts, HTTP_ERROR_IDLE_DELAY)
|
|
137
|
+
print(f"{BRIGHT_RED}Update check failed: {_format_request_error(error)}.{RESET}")
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _should_check(force_check: bool = False) -> bool:
|
|
141
|
+
"""@brief Evaluate whether the GitHub version-check request should run.
|
|
142
|
+
|
|
143
|
+
@details Forces execution when `force_check` is `True`; otherwise reads the
|
|
144
|
+
cached idle-until timestamp and compares it against the current wall-clock
|
|
145
|
+
time. Complexity: O(1).
|
|
146
|
+
@param force_check {bool} Cooldown-bypass flag derived from CLI arguments.
|
|
147
|
+
@return {bool} `True` when the HTTP request is allowed or forced.
|
|
148
|
+
@satisfies REQ-003, REQ-059
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
if force_check:
|
|
152
|
+
return True
|
|
153
|
+
config = _read_idle_config()
|
|
154
|
+
if config is None:
|
|
155
|
+
return True
|
|
156
|
+
idle_until = config.get("idle_until_timestamp", 0)
|
|
157
|
+
if not isinstance(idle_until, (int, float)):
|
|
158
|
+
return True
|
|
159
|
+
return time.time() >= idle_until
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _parse_version(version_value: str) -> tuple[int, ...]:
|
|
163
|
+
"""@brief Convert a semantic-version string into an integer tuple.
|
|
164
|
+
|
|
165
|
+
@details Strips a leading `v` prefix, splits by `.`, and converts each
|
|
166
|
+
segment to `int`. Complexity: O(N) where N is the number of segments.
|
|
167
|
+
@param version_value {str} Raw semantic version token.
|
|
168
|
+
@return {tuple[int, ...]} Parsed numeric version segments.
|
|
169
|
+
@throws {ValueError} Propagated when a segment is not numeric.
|
|
170
|
+
@satisfies PRJ-004
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
return tuple(int(part) for part in version_value.strip().lstrip("v").split("."))
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _compare_versions(current: str, latest: str) -> bool:
|
|
177
|
+
"""@brief Compare installed and latest semantic versions.
|
|
178
|
+
|
|
179
|
+
@details Parses both version strings into integer tuples and returns
|
|
180
|
+
`True` only when `latest` is newer than `current`. Invalid inputs collapse
|
|
181
|
+
to `False`. Complexity: O(N).
|
|
182
|
+
@param current {str} Installed package version.
|
|
183
|
+
@param latest {str} Latest GitHub release version.
|
|
184
|
+
@return {bool} `True` when the remote version is newer.
|
|
185
|
+
@satisfies PRJ-004, REQ-060
|
|
186
|
+
"""
|
|
187
|
+
|
|
188
|
+
try:
|
|
189
|
+
return _parse_version(latest) > _parse_version(current)
|
|
190
|
+
except (ValueError, AttributeError):
|
|
191
|
+
return False
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _fetch_json_payload(
|
|
195
|
+
url: str, current_version: str, accept_header: str
|
|
196
|
+
) -> dict[str, Any]:
|
|
197
|
+
"""@brief Fetch and decode one JSON payload over HTTP.
|
|
198
|
+
|
|
199
|
+
@details Issues a GET request with caller-supplied `Accept` header and a
|
|
200
|
+
program/version `User-Agent`, then decodes the response body as a JSON
|
|
201
|
+
object. Time complexity O(n) over payload size.
|
|
202
|
+
@param url {str} Remote JSON endpoint URL.
|
|
203
|
+
@param current_version {str} Installed package version used in User-Agent.
|
|
204
|
+
@param accept_header {str} Value of the HTTP `Accept` header.
|
|
205
|
+
@return {dict[str, Any]} Parsed JSON response object.
|
|
206
|
+
@throws {urllib.error.HTTPError} On HTTP error responses.
|
|
207
|
+
@throws {urllib.error.URLError} On connection failures.
|
|
208
|
+
@throws {json.JSONDecodeError} On malformed payload bodies.
|
|
209
|
+
@satisfies DES-018, DES-019
|
|
210
|
+
"""
|
|
211
|
+
|
|
212
|
+
request = urllib.request.Request(url)
|
|
213
|
+
request.add_header("Accept", accept_header)
|
|
214
|
+
request.add_header("User-Agent", f"{PROGRAM}/{current_version}")
|
|
215
|
+
with urllib.request.urlopen(request, timeout=HTTP_TIMEOUT) as response:
|
|
216
|
+
return json.loads(response.read().decode())
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def fetch_pypi_latest_version(current_version: str) -> str:
|
|
220
|
+
"""@brief Resolve the latest published version from the PyPI registry.
|
|
221
|
+
|
|
222
|
+
@details Queries the PyPI JSON API for this program and extracts field
|
|
223
|
+
`info.version`. Complexity O(1) over response object size.
|
|
224
|
+
@param current_version {str} Installed package version used in User-Agent.
|
|
225
|
+
@return {str} Latest published version token (for example `1.2.3`).
|
|
226
|
+
@throws {urllib.error.HTTPError} On HTTP error responses.
|
|
227
|
+
@throws {urllib.error.URLError} On connection failures.
|
|
228
|
+
@throws {json.JSONDecodeError} On malformed payload bodies.
|
|
229
|
+
@throws {KeyError} When `info.version` fields are absent.
|
|
230
|
+
@satisfies DES-018, PRJ-004
|
|
231
|
+
"""
|
|
232
|
+
|
|
233
|
+
payload = _fetch_json_payload(
|
|
234
|
+
PYPI_API_URL, current_version, "application/vnd.pypi.simple.v1+json"
|
|
235
|
+
)
|
|
236
|
+
info = payload["info"]
|
|
237
|
+
return str(info["version"])
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def fetch_github_latest_release(current_version: str) -> str:
|
|
241
|
+
"""@brief Resolve the latest GitHub release tag regardless of its branch.
|
|
242
|
+
|
|
243
|
+
@details Queries the GitHub Releases API `latest` endpoint and extracts
|
|
244
|
+
field `tag_name`, which tracks the newest published tagged release across
|
|
245
|
+
any branch. Complexity O(1) over response object size.
|
|
246
|
+
@param current_version {str} Installed package version used in User-Agent.
|
|
247
|
+
@return {str} Latest release tag token preserving its `v` prefix.
|
|
248
|
+
@throws {urllib.error.HTTPError} On HTTP error responses.
|
|
249
|
+
@throws {urllib.error.URLError} On connection failures.
|
|
250
|
+
@throws {json.JSONDecodeError} On malformed payload bodies.
|
|
251
|
+
@throws {KeyError} When `tag_name` field is absent.
|
|
252
|
+
@satisfies DES-019, REQ-076
|
|
253
|
+
"""
|
|
254
|
+
|
|
255
|
+
payload = _fetch_json_payload(
|
|
256
|
+
GITHUB_API_URL, current_version, "application/vnd.github.v3+json"
|
|
257
|
+
)
|
|
258
|
+
return str(payload["tag_name"])
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def print_remote_latest_versions(current_version: str) -> None:
|
|
262
|
+
"""@brief Print PyPI and GitHub latest-version report lines.
|
|
263
|
+
|
|
264
|
+
@details Emits one `<source> latest: <version>` line per remote source in
|
|
265
|
+
fixed order (PyPI, GitHub). A failed source lookup prints one bright-red
|
|
266
|
+
error line instead of its version line, and the remaining source is still
|
|
267
|
+
processed. Complexity O(1) excluding network latency.
|
|
268
|
+
@param current_version {str} Installed package version used in User-Agent.
|
|
269
|
+
@return {None} Writes two report or error lines to stdout.
|
|
270
|
+
@satisfies REQ-003, REQ-075, DES-020
|
|
271
|
+
"""
|
|
272
|
+
|
|
273
|
+
for source_label, fetch_version in (
|
|
274
|
+
("PyPI", fetch_pypi_latest_version),
|
|
275
|
+
("GitHub", fetch_github_latest_release),
|
|
276
|
+
):
|
|
277
|
+
try:
|
|
278
|
+
latest_version = fetch_version(current_version).lstrip("v")
|
|
279
|
+
except Exception as error:
|
|
280
|
+
print(
|
|
281
|
+
f"{BRIGHT_RED}{source_label} latest check failed: "
|
|
282
|
+
f"{_format_request_error(error)}.{RESET}"
|
|
283
|
+
)
|
|
284
|
+
continue
|
|
285
|
+
print(f"{source_label} latest: {latest_version}")
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def check_for_updates(current_version: str) -> None:
|
|
289
|
+
"""@brief Execute the startup PyPI registry version check.
|
|
290
|
+
|
|
291
|
+
@details Applies cooldown gating unless the current CLI invocation requests
|
|
292
|
+
`--version` or `--ver`, performs the PyPI latest-version HTTP request,
|
|
293
|
+
prints a bright-green update line for newer releases, persists a
|
|
294
|
+
3600-second success cooldown or an 86400-second request-error cooldown for
|
|
295
|
+
every request outcome, prints bright-red request errors, and suppresses
|
|
296
|
+
propagation of non-HTTP request exceptions. Complexity O(1) excluding
|
|
297
|
+
network latency and JSON parsing.
|
|
298
|
+
@param current_version {str} Installed package version string.
|
|
299
|
+
@return {None} No return value.
|
|
300
|
+
@throws {urllib.error.HTTPError} Internally handled and converted to output.
|
|
301
|
+
@satisfies PRJ-004, DES-004, DES-005, DES-006, DES-018, REQ-003, REQ-059, REQ-060, REQ-061
|
|
302
|
+
"""
|
|
303
|
+
|
|
304
|
+
if not _should_check(force_check=_is_forced_version_check()):
|
|
305
|
+
return
|
|
306
|
+
|
|
307
|
+
now = time.time()
|
|
308
|
+
|
|
309
|
+
try:
|
|
310
|
+
latest = fetch_pypi_latest_version(current_version)
|
|
311
|
+
|
|
312
|
+
if latest and _compare_versions(current_version, latest):
|
|
313
|
+
print(
|
|
314
|
+
f"{BRIGHT_GREEN}Versione Disponibile: {latest} | "
|
|
315
|
+
f"Versione Installata: {current_version}{RESET}"
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
_write_idle_config(now, IDLE_DELAY)
|
|
319
|
+
|
|
320
|
+
except urllib.error.HTTPError as error:
|
|
321
|
+
_handle_request_error(now, error)
|
|
322
|
+
except Exception as error:
|
|
323
|
+
_handle_request_error(now, error)
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: shellscripts
|
|
3
|
+
Version: 0.23.1
|
|
4
|
+
Summary: Execute useful shell scripts via an uvx-compatible CLI interface.
|
|
5
|
+
Author: Francesco Rolando
|
|
6
|
+
License-Expression: GPL-3.0-only
|
|
7
|
+
Project-URL: Homepage, https://github.com/Ogekuri/shellScripts
|
|
8
|
+
Project-URL: Repository, https://github.com/Ogekuri/shellScripts
|
|
9
|
+
Keywords: shell,scripts,uv,astral,uvx,cli
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Software Development
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
|
|
22
|
+
> [!CAUTION]
|
|
23
|
+
> ### 🚨 STRICTLY EXPERIMENTAL — PROOF OF CONCEPT (PRE-ALPHA)
|
|
24
|
+
>
|
|
25
|
+
>    
|
|
26
|
+
>
|
|
27
|
+
> **PLEASE READ CAREFULLY BEFORE USING THIS SOFTWARE:**
|
|
28
|
+
>
|
|
29
|
+
> * **Very Early Stage:** This project is currently a raw **Proof of Concept (PoC)** in a **pre-alpha** state. It is nowhere near production-ready and may contain severe bugs, breaking changes, or incomplete logic.
|
|
30
|
+
> * **Personal Use Only:** This repository is developed, maintained, and shared strictly for **personal use**.
|
|
31
|
+
> * **No Support or Feature Requests:** Please **do not** open issues, bug reports, or feature requests. They will not be addressed or accepted at this stage of development.
|
|
32
|
+
> * **No Warranty:** This software is provided **"AS IS"**, without warranty of any kind, express or implied. You may use it strictly for **testing and evaluation purposes** at your own sole risk.
|
|
33
|
+
|
|
34
|
+
# s/shellScripts (0.23.1)
|
|
35
|
+
|
|
36
|
+
<p align="center">
|
|
37
|
+
<img src="https://img.shields.io/badge/python-3.11%2B-3776AB?style=flat-square&logo=python&logoColor=white" alt="Python 3.11+">
|
|
38
|
+
<img src="https://img.shields.io/badge/license-GPL--3.0-491?style=flat-square" alt="License: GPL-3.0">
|
|
39
|
+
<img src="https://img.shields.io/badge/platform-Linux-6A7EC2?style=flat-square&logo=terminal&logoColor=white" alt="Platforms">
|
|
40
|
+
<img src="https://img.shields.io/badge/docs-live-b31b1b" alt="Docs">
|
|
41
|
+
<img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json" alt="uv">
|
|
42
|
+
</p>
|
|
43
|
+
<p align="center">
|
|
44
|
+
<strong>Execute useful shell scripts via an uvx-compatible CLI interface.</strong><br>
|
|
45
|
+
<code>shellscripts</code> is a Linux-first command collection for project automation, AI CLI launchers,
|
|
46
|
+
PDF/DICOM utilities, development helpers, and editor integrations, exposed through a single command.
|
|
47
|
+
<br>
|
|
48
|
+
</p>
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
<p align="center">
|
|
52
|
+
<a href="#quick-start">Quick Start</a> |
|
|
53
|
+
<a href="#feature-highlights">Feature Highlights</a> |
|
|
54
|
+
<a href="#management-commands">Upgrading or Removing</a> |
|
|
55
|
+
<a href="#shell-scripts">Shell Scripts</a>
|
|
56
|
+
</p>
|
|
57
|
+
|
|
58
|
+
<p align="center">
|
|
59
|
+
<br>
|
|
60
|
+
🚧 <strong>DRAFT:</strong> Proof-of-Concept (PoC) Version 📝 - Work in Progress 🏗️ 🚧<br>
|
|
61
|
+
⚠️ <strong>IMPORTANT NOTICE</strong>: Created with <a href="https://www.npmjs.com/package/pi-usereq"><strong>PI-useReq/pi-usereq</strong></a> 🤖✨ ⚠️<br>
|
|
62
|
+
<br>
|
|
63
|
+
</p>
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
## Feature Highlights
|
|
67
|
+
- Single CLI entrypoint with command dispatch and grouped built-in help (`shellscripts` and alias `s`).
|
|
68
|
+
- AI tooling bootstrap command (`ai-install`) with per-tool selectors for Codex, Copilot, Gemini, OpenCode, Claude, and Kiro.
|
|
69
|
+
- Project-context launchers for AI CLIs (`claude`, `codex`, `copilot`, `gemini`, `kiro`, `opencode`, `pi`) and IDEs (`vscode`, `vsinsider`).
|
|
70
|
+
- PDF workflow commands for crop, merge, split, TOC cleanup, and tiling.
|
|
71
|
+
- DICOM helpers (`dicom2jpg`, `dicomviewer`) and video transcoders (`video2h264`, `video2h265`).
|
|
72
|
+
- Project maintenance and automation commands (`req`, `clean`, `venv`, `tests`, `doxygen`, `ubuntu-dark-theme`).
|
|
73
|
+
- Startup update notification with rate-limited checks against the PyPI registry.
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
## Requirements
|
|
77
|
+
|
|
78
|
+
- Python `>=3.11`.
|
|
79
|
+
- Astral `uv` is required for install and runtime flows (`uv tool install`, `uvx`, `uv run`).
|
|
80
|
+
- Git is required for commands that enforce repository-root execution (`claude`, `codex`, `copilot`, `gemini`, `kiro`, `opencode`, `pi`, `clean`, `venv`, `tests`, `doxygen`, `vscode`, `vsinsider`).
|
|
81
|
+
- Command-specific external tools are required only for related commands:
|
|
82
|
+
- `npm` (and `sudo` on non-Windows) for npm-based `ai-install` targets.
|
|
83
|
+
- `req` for the `req` command.
|
|
84
|
+
- `doxygen` (plus optional `make` and `pdflatex`) for `doxygen`.
|
|
85
|
+
- `gs`, `pdfinfo`, `qpdf`, `pdftk`, `plakativ` for PDF commands.
|
|
86
|
+
- `ffmpeg` for `video2h264` and `video2h265`.
|
|
87
|
+
- Java runtime + PixelMed jars (+ `java-wrappers`) for DICOM commands.
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
## Quick Start
|
|
91
|
+
|
|
92
|
+
### Prerequisites
|
|
93
|
+
|
|
94
|
+
- Install `uv`: [https://docs.astral.sh/uv/getting-started/installation/](https://docs.astral.sh/uv/getting-started/installation/)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
### Install / Upgrade
|
|
98
|
+
|
|
99
|
+
Install from PyPI (primary distribution channel):
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
uv tool install shellscripts
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Upgrade to the latest published PyPI release:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
s --upgrade
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
### Run Without Installing (uvx)
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
uvx shellscripts <command> [args...]
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
### Run From a Local Clone
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
./scripts/s.sh <command> [args...]
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
or
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
uv run --project . python -m shell_scripts <command> [args...]
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
## Install from GitHub (Alternative)
|
|
133
|
+
|
|
134
|
+
PyPI is the primary distribution channel. As an alternative, the package can be installed from GitHub Releases, which also hosts intermediate and test releases that are not published on PyPI:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
uv tool install shellscripts --force --from git+https://github.com/Ogekuri/shellScripts.git@vX.Y.Z
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Intermediate and test releases are available at: [https://github.com/Ogekuri/shellScripts/releases](https://github.com/Ogekuri/shellScripts/releases).
|
|
141
|
+
|
|
142
|
+
To upgrade to the newest tagged GitHub release, regardless of the branch that carries the tag, use the `--dev` (alias `--develop`) flag together with `--upgrade`:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
s --upgrade --dev
|
|
146
|
+
# or
|
|
147
|
+
s --upgrade --develop
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
## Management Commands
|
|
152
|
+
|
|
153
|
+
Global syntax:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
shellscripts [command] [options]
|
|
157
|
+
# or
|
|
158
|
+
s [command] [options]
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Built-in management flags:
|
|
162
|
+
|
|
163
|
+
- `--help` : show full help or command help (`shellscripts --help <command>`).
|
|
164
|
+
- `--version` / `--ver` : print the installed version plus the latest versions available on PyPI and GitHub.
|
|
165
|
+
- `--upgrade` : run the configured upgrade command automatically on Linux (installs the latest PyPI release by default), otherwise print the manual command.
|
|
166
|
+
- `--upgrade --dev` / `--upgrade --develop` : upgrade from the latest tagged GitHub release instead of PyPI, regardless of its source branch.
|
|
167
|
+
- `--uninstall` : run configured uninstall command automatically on Linux, otherwise print the manual command.
|
|
168
|
+
- `--write-config` : write the default runtime configuration file.
|
|
169
|
+
|
|
170
|
+
### Runtime Configuration
|
|
171
|
+
|
|
172
|
+
Generate a default config file:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
s --write-config
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Default location:
|
|
179
|
+
|
|
180
|
+
```text
|
|
181
|
+
~/.config/shellScripts/config.json
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Top-level configuration keys:
|
|
185
|
+
|
|
186
|
+
- `management`
|
|
187
|
+
- `upgrade`: command string executed by `--upgrade` (PyPI install by default).
|
|
188
|
+
- `upgrade_develop`: command template executed by `--upgrade --dev`; the `{tag}` placeholder is replaced with the latest GitHub release tag.
|
|
189
|
+
- `uninstall`: command string executed by `--uninstall`.
|
|
190
|
+
- `dispatch`
|
|
191
|
+
- `diff`, `edit`, `view`: each command supports `categories` and `fallback` command vectors.
|
|
192
|
+
- `req`
|
|
193
|
+
- `providers`: provider list forwarded to external `req`.
|
|
194
|
+
- `static_checks`: static-check entries forwarded to external `req`.
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
## Shell Scripts
|
|
198
|
+
|
|
199
|
+
### Command List
|
|
200
|
+
|
|
201
|
+
| Command | Purpose |
|
|
202
|
+
|---|---|
|
|
203
|
+
| `ai-install` | Install AI CLIs (all or selected tools). |
|
|
204
|
+
| `claude` | Launch Claude CLI in project context. |
|
|
205
|
+
| `codex` | Launch Codex CLI in project context with auth sync and `CODEX_HOME`. |
|
|
206
|
+
| `copilot` | Launch Copilot CLI in project context. |
|
|
207
|
+
| `gemini` | Launch Gemini CLI in project context. |
|
|
208
|
+
| `kiro` | Launch Kiro CLI in project context. |
|
|
209
|
+
| `opencode` | Launch OpenCode CLI in project context. |
|
|
210
|
+
| `pi` | Launch pi.dev CLI in project context (adds default `--tools` when missing). |
|
|
211
|
+
| `clean` | Find and delete cache directories under the project root (or a passed directory). |
|
|
212
|
+
| `req` | Run useReq bootstrap on current directory, first-level dirs, or recursively. |
|
|
213
|
+
| `venv` | Recreate `.venv` and install `requirements.txt` if present. |
|
|
214
|
+
| `tests` | Ensure/create `.venv` and run pytest (arguments passed through). |
|
|
215
|
+
| `doxygen` | Generate HTML/XML/Markdown docs and optional PDF output. |
|
|
216
|
+
| `vscode` | Open project in VS Code with `CODEX_HOME` set. |
|
|
217
|
+
| `vsinsider` | Open project in VS Code Insiders with `CODEX_HOME` set. |
|
|
218
|
+
| `diff` | Dispatch file differ by MIME/category. |
|
|
219
|
+
| `edit` | Dispatch file editor by MIME/category. |
|
|
220
|
+
| `view` | Dispatch file viewer by MIME/category. |
|
|
221
|
+
| `pdf-crop` | Crop PDF pages with auto or manual bounding boxes. |
|
|
222
|
+
| `pdf-merge` | Merge multiple PDFs preserving/rebuilding bookmarks. |
|
|
223
|
+
| `pdf-split-by-format` | Split PDFs into parts when page format changes. |
|
|
224
|
+
| `pdf-split-by-toc` | Split PDF into chapter files from level-1 TOC entries. |
|
|
225
|
+
| `pdf-tiler-090` | Tile PDF to A4 at 90% scale. |
|
|
226
|
+
| `pdf-tiler-100` | Tile PDF to A4 at original A1 size. |
|
|
227
|
+
| `pdf-toc-clean` | Remove out-of-range TOC entries and write cleaned outputs. |
|
|
228
|
+
| `dicom2jpg` | Convert DICOM images to JPEG (PixelMed). |
|
|
229
|
+
| `dicomviewer` | Launch PixelMed DICOM viewer. |
|
|
230
|
+
| `video2h264` | Convert one video to H.264 MP4 via ffmpeg. |
|
|
231
|
+
| `video2h265` | Convert one video to H.265 MP4 via ffmpeg. |
|
|
232
|
+
| `ubuntu-dark-theme` | Apply GNOME and Qt dark-theme settings. |
|
|
233
|
+
|
|
234
|
+
### Selected Usage Examples
|
|
235
|
+
|
|
236
|
+
Install all AI CLIs (default behavior):
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
s ai-install
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Install only selected AI tools:
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
s ai-install --codex --gemini --claude
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Run pi.dev with default tools injected automatically:
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
s pi
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Override pi tools explicitly:
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
s pi --tools read,bash,grep,find
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Run useReq bootstrap on first-level child directories:
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
s req --dirs
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Clean caches without interactive confirmation:
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
s clean --yes
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Crop PDF with automatic bbox over a page window:
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
s pdf-crop --in input.pdf --out cropped.pdf --analyze-pages 1-10 --pages 1-
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Merge PDFs and choose output file:
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
s pdf-merge -o merged.pdf a.pdf b.pdf c.pdf
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Split PDF by TOC chapters:
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
s pdf-split-by-toc document.pdf
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Convert a video to H.264 MP4:
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
s video2h264 input.mov
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
Run tests and pass pytest options:
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
s tests -q -k "pdf"
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Open current git project in VS Code / Insiders:
|
|
303
|
+
|
|
304
|
+
```bash
|
|
305
|
+
s vscode
|
|
306
|
+
s vsinsider
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
## Acknowledgments
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
shell_scripts/__init__.py,sha256=tcta0Vrn-w5n4wRkcb6xWB5jcyslWz3oKUyNDDU4ago,96
|
|
2
|
+
shell_scripts/__main__.py,sha256=-wEmwKTfeY5TphAH82E38XRPoB4KT5QDLDgRZz84PTs,83
|
|
3
|
+
shell_scripts/config.py,sha256=YUs8QWsZ31lJPrMpFqvn7YLQdseQnlaiu7OXcXsKdEw,12640
|
|
4
|
+
shell_scripts/core.py,sha256=rhgy9xduzkujsjD4NRL2AYvtNI3yTVnPThzEAzyMWUM,11204
|
|
5
|
+
shell_scripts/utils.py,sha256=bXSFRszGmkSKkxOUYJwqqVldveI9rrizkEu-nzstoJs,14133
|
|
6
|
+
shell_scripts/version_check.py,sha256=GDE7IwCzt8-UJ0pFAOQzv0dIYUsynYRN1Z1Gz6Mx7Yo,13090
|
|
7
|
+
shell_scripts/commands/__init__.py,sha256=tFiG-epwWLjfwnZ3LEXXULNk8bEquE5_x9uK59uamQo,3696
|
|
8
|
+
shell_scripts/commands/_dc_common.py,sha256=kfUn92r6n8F1w27jy5QDJE_6Kvpvzhq6dUGNs0_GO0U,4695
|
|
9
|
+
shell_scripts/commands/ai_install.py,sha256=BH38g3OXWQ4F60-pzTbt-fY67eFmRSOEzryhpxVSgQw,17011
|
|
10
|
+
shell_scripts/commands/bin_links.py,sha256=7chuWbk1FdnxH9ak_fATdmUSOyzIbg4WmfRPCOw10-U,2425
|
|
11
|
+
shell_scripts/commands/claude.py,sha256=_X76ygt0jnxZyyNoZ8qVhn6r2C0B9mcNIaKd_hLthl4,2049
|
|
12
|
+
shell_scripts/commands/clean.py,sha256=LOK7bVzqpWDvFBMInlu9COtODVmjwOtwCHyMriq7wIk,2552
|
|
13
|
+
shell_scripts/commands/codex.py,sha256=iHg5S_E8QpsGr2qQwucNDw-24-lcPH_FHsaGCxH9hEE,4331
|
|
14
|
+
shell_scripts/commands/copilot.py,sha256=zYGbB33S8B8zWPi8jV-EBWSoxGv0-11K_zGgBkfi7-Y,1954
|
|
15
|
+
shell_scripts/commands/dicom2jpg.py,sha256=YQQHdhwP8yi10UU3qWn4UzbCjSCK6C33bORcNWtXaek,2614
|
|
16
|
+
shell_scripts/commands/dicomviewer.py,sha256=sMnwQGeHf7r8Qo0n8Cf_ZeTmcask3z5bqQZ44mvSMG8,2283
|
|
17
|
+
shell_scripts/commands/diff_cmd.py,sha256=C6R4GiNhgvvlU6drCVCG9-TusLotNNcb9v5wBq10VJw,1828
|
|
18
|
+
shell_scripts/commands/doxygen_cmd.py,sha256=wd4qecljYZ0lVpdu3D1qKf6cD9xNBv36TetfLlvdFJw,5111
|
|
19
|
+
shell_scripts/commands/edit_cmd.py,sha256=WnWVs4Ojq0qN4fw9iqooUqQHK0XIfDUbJBRmI-AMrUE,1827
|
|
20
|
+
shell_scripts/commands/gemini.py,sha256=efvOhfrDu6WkeJZ0WuRu_vp2fbqPkPrp9r6WECQJDxg,1902
|
|
21
|
+
shell_scripts/commands/kiro.py,sha256=NuD2Zx76azDQAWbJ8YcewshEogLmrd-Avu9feisqHys,1871
|
|
22
|
+
shell_scripts/commands/opencode.py,sha256=64I3IHrliAZ7C4jTU4WLxQcJHJohJcKex09yhegWUoc,1900
|
|
23
|
+
shell_scripts/commands/pdf_crop.py,sha256=VliRAMHMjnSQK9mZhNoghftZAVGZeJG6xhrihcuuawQ,15135
|
|
24
|
+
shell_scripts/commands/pdf_merge.py,sha256=vTTlUv8oVYUo9U45b4yUpK4x0unIwctgXV7g5exvihc,5996
|
|
25
|
+
shell_scripts/commands/pdf_split_by_format.py,sha256=kWID9sadX9cf0jMWFqTyCFMRHwAFNZVmnp24IlcN1H8,7573
|
|
26
|
+
shell_scripts/commands/pdf_split_by_toc.py,sha256=I027ldWXat8ODafn3KRD1X52yeUP2xez1PX-kFNptAg,6674
|
|
27
|
+
shell_scripts/commands/pdf_tiler_090.py,sha256=1eLCw9rqGN8LELB5lBvx6xfaTYc4Kci5YDK2EvnHG6E,1781
|
|
28
|
+
shell_scripts/commands/pdf_tiler_100.py,sha256=U-DlX_PJPHL2P2p0wvWMzb-XkxVYn5-Jb1vqlBzIAqA,1805
|
|
29
|
+
shell_scripts/commands/pdf_toc_clean.py,sha256=7GroNjVbOoh184AbYoqXSVdK_RHqA3y5urjkZ4lcJbw,5907
|
|
30
|
+
shell_scripts/commands/pi.py,sha256=Ozo-VKaieQYp1OFiyo2LFsxvpD-XYdO5JpcvTdqDEhQ,2381
|
|
31
|
+
shell_scripts/commands/req_cmd.py,sha256=3zR4FRbZWebQa-csna7qWttVcZsLqAIqiJSjQ4Idtlk,13212
|
|
32
|
+
shell_scripts/commands/tests_cmd.py,sha256=nKfD2sDb06Hy5J93yRH6dP4kSfV1sxLkpHHdTgqLvvw,2672
|
|
33
|
+
shell_scripts/commands/ubuntu_dark_theme.py,sha256=O-Ku_U3ZwFNVBpSHYvsFZSK36SoDhrlFBeWlqD5_GYc,1987
|
|
34
|
+
shell_scripts/commands/venv_cmd.py,sha256=X8phS-K4b0oOGuuwn2u84D1bcQlfo1QpJi1RI3VVe3s,2381
|
|
35
|
+
shell_scripts/commands/video2h264.py,sha256=5Cj4OpDrl1Bm24wGBbzni2So1ELP3Wual3RaB98DQrs,2717
|
|
36
|
+
shell_scripts/commands/video2h265.py,sha256=Sr_UWGz_V77x4anmuy8kiWZv5I_AOFlV0WDnJEToAhk,2675
|
|
37
|
+
shell_scripts/commands/view_cmd.py,sha256=qHEivQHZpZCTE3VSFP1ay2SuqgancQlEKxJcKtyeqNA,1827
|
|
38
|
+
shell_scripts/commands/vscode_cmd.py,sha256=Rkcjt5A1HMzqJoh2Yz9VRGoLP-L3qpRO5hkcSrzOmvA,1461
|
|
39
|
+
shell_scripts/commands/vsinsider_cmd.py,sha256=KTTv1cVpJO-u1ktxOE0W_o4AAmz20pAvkDtyhpU9XBg,1548
|
|
40
|
+
shellscripts-0.23.1.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
41
|
+
shellscripts-0.23.1.dist-info/METADATA,sha256=BihFSRKVrmcCCAiiJLu0B5seR0Unkn0W2-WCrrgWo0A,10985
|
|
42
|
+
shellscripts-0.23.1.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
43
|
+
shellscripts-0.23.1.dist-info/entry_points.txt,sha256=9ICAgH87fxHBzi3f6JXIVcsPWzAKsCh4_1xS8JThQ0M,85
|
|
44
|
+
shellscripts-0.23.1.dist-info/top_level.txt,sha256=7bDVqjcR5CfCCArCP4X4e9gYJ-ua8z-GfQeHbAo8rCY,14
|
|
45
|
+
shellscripts-0.23.1.dist-info/RECORD,,
|