oscopy 0.1.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.
oscopy-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,122 @@
1
+ Metadata-Version: 2.3
2
+ Name: oscopy
3
+ Version: 0.1.0
4
+ Summary: Python script to copy from STD to your clipboard with OSC 52.
5
+ Author: hammerill
6
+ Author-email: hammerill <kyrylo@hammerill.com>
7
+ Requires-Python: >=3.12
8
+ Description-Content-Type: text/markdown
9
+
10
+ # oscopy
11
+
12
+ Python tool to copy text to your local clipboard over OSC 52, and record shell command transcripts.
13
+
14
+ ## How to Use It
15
+
16
+ ### Piping
17
+
18
+ You simply pipe to `oscopy`, then paste anywhere:
19
+
20
+ ```bash
21
+ echo coucou_yopta | oscopy
22
+ # now you can paste "coucou_yopta" everywhere
23
+ ```
24
+
25
+ > [!IMPORTANT]
26
+ > This works even if you're connected to a remote SSH server. The content travels through SSH and arrives in your local system clipboard.
27
+
28
+ `echo` adds a trailing newline. Strip it with `-s` (or `-x`):
29
+
30
+ ```bash
31
+ echo precision | oscopy -s
32
+ # now you can paste "precision" knowing it won't add a newline
33
+ ```
34
+
35
+ Why not piping file contents?
36
+
37
+ ```bash
38
+ cat ~/.ssh/id_rsa.pub | oscopy
39
+ # then paste your SSH pubkey on the website...
40
+ ```
41
+
42
+ ### Record a Command
43
+
44
+ If you need to execute something and copy both the command you typed and whatever it returned back, e.g.:
45
+
46
+ ```bash
47
+ $ ls -la
48
+ total 112
49
+ drwxr-xr-x@ 11 hammerill staff 352 Mar 10 12:48 .
50
+ drwxr-xr-x 12 hammerill staff 384 Mar 2 11:59 ..
51
+ drwxr-xr-x@ 15 hammerill staff 480 Mar 10 15:36 .git
52
+ -rw-r--r--@ 1 hammerill staff 109 Feb 5 09:31 .gitignore
53
+ -rw-r--r--@ 1 hammerill staff 5 Feb 5 09:31 .python-version
54
+ drwxr-xr-x@ 8 hammerill staff 256 Feb 5 09:31 .venv
55
+ -rw-r--r--@ 1 hammerill staff 35149 Feb 5 09:31 LICENSE
56
+ -rw-r--r--@ 1 hammerill staff 390 Feb 5 09:31 pyproject.toml
57
+ -rw-r--r--@ 1 hammerill staff 1753 Mar 10 13:16 README.md
58
+ drwxr-xr-x@ 3 hammerill staff 96 Feb 5 09:31 src
59
+ -rw-r--r--@ 1 hammerill staff 127 Feb 5 09:31 uv.lock
60
+ ```
61
+
62
+ ...you can use oscopy to do this quickly:
63
+
64
+ ```bash
65
+ oscopy run ls -la
66
+ # now you can paste the thing from above
67
+ ```
68
+
69
+ You can try any other Shell command:
70
+
71
+ ```bash
72
+ oscopy run -- git status --short
73
+ ```
74
+
75
+ `--` is optional, but useful to avoid ambiguity in edge cases with CLI args.
76
+
77
+ ### Record a Shell Session
78
+
79
+ Start a temporary recording shell:
80
+
81
+ ```bash
82
+ oscopy record
83
+ # or `oscopy start`
84
+ ```
85
+
86
+ Session mode currently runs in a temporary `zsh` shell.
87
+
88
+ Then run commands normally. When done:
89
+
90
+ ```bash
91
+ oscopy stop
92
+ ```
93
+
94
+ This copies a transcript like:
95
+
96
+ ```bash
97
+ $ ls *.md
98
+ -rw-r--r--@ 1 hammerill staff 1.1K Mar 10 12:48 README.md
99
+
100
+ $ uname -a
101
+ Darwin pommier 25.3.0 Darwin Kernel Version 25.3.0 ...
102
+ ```
103
+
104
+ The transcript uses `$ ` as the command prefix, includes both stdout and stderr, and inserts clean blank lines between commands.
105
+
106
+ ## Install
107
+
108
+ Install as a [uv tool](https://docs.astral.sh/uv) from this GitHub repo:
109
+
110
+ ```bash
111
+ uv tool install git+https://github.com/hammerill/oscopy
112
+
113
+ # and, if you have OhMyZsh installed, quickly setup recommended aliases orun, orec and ostop:
114
+ oscopy aliases > ~/.oh-my-zsh/custom/oscopy-aliases.zsh
115
+ ```
116
+
117
+ Or local dev install:
118
+
119
+ ```bash
120
+ # in oscopy project folder
121
+ uv tool install -e .
122
+ ```
oscopy-0.1.0/README.md ADDED
@@ -0,0 +1,113 @@
1
+ # oscopy
2
+
3
+ Python tool to copy text to your local clipboard over OSC 52, and record shell command transcripts.
4
+
5
+ ## How to Use It
6
+
7
+ ### Piping
8
+
9
+ You simply pipe to `oscopy`, then paste anywhere:
10
+
11
+ ```bash
12
+ echo coucou_yopta | oscopy
13
+ # now you can paste "coucou_yopta" everywhere
14
+ ```
15
+
16
+ > [!IMPORTANT]
17
+ > This works even if you're connected to a remote SSH server. The content travels through SSH and arrives in your local system clipboard.
18
+
19
+ `echo` adds a trailing newline. Strip it with `-s` (or `-x`):
20
+
21
+ ```bash
22
+ echo precision | oscopy -s
23
+ # now you can paste "precision" knowing it won't add a newline
24
+ ```
25
+
26
+ Why not piping file contents?
27
+
28
+ ```bash
29
+ cat ~/.ssh/id_rsa.pub | oscopy
30
+ # then paste your SSH pubkey on the website...
31
+ ```
32
+
33
+ ### Record a Command
34
+
35
+ If you need to execute something and copy both the command you typed and whatever it returned back, e.g.:
36
+
37
+ ```bash
38
+ $ ls -la
39
+ total 112
40
+ drwxr-xr-x@ 11 hammerill staff 352 Mar 10 12:48 .
41
+ drwxr-xr-x 12 hammerill staff 384 Mar 2 11:59 ..
42
+ drwxr-xr-x@ 15 hammerill staff 480 Mar 10 15:36 .git
43
+ -rw-r--r--@ 1 hammerill staff 109 Feb 5 09:31 .gitignore
44
+ -rw-r--r--@ 1 hammerill staff 5 Feb 5 09:31 .python-version
45
+ drwxr-xr-x@ 8 hammerill staff 256 Feb 5 09:31 .venv
46
+ -rw-r--r--@ 1 hammerill staff 35149 Feb 5 09:31 LICENSE
47
+ -rw-r--r--@ 1 hammerill staff 390 Feb 5 09:31 pyproject.toml
48
+ -rw-r--r--@ 1 hammerill staff 1753 Mar 10 13:16 README.md
49
+ drwxr-xr-x@ 3 hammerill staff 96 Feb 5 09:31 src
50
+ -rw-r--r--@ 1 hammerill staff 127 Feb 5 09:31 uv.lock
51
+ ```
52
+
53
+ ...you can use oscopy to do this quickly:
54
+
55
+ ```bash
56
+ oscopy run ls -la
57
+ # now you can paste the thing from above
58
+ ```
59
+
60
+ You can try any other Shell command:
61
+
62
+ ```bash
63
+ oscopy run -- git status --short
64
+ ```
65
+
66
+ `--` is optional, but useful to avoid ambiguity in edge cases with CLI args.
67
+
68
+ ### Record a Shell Session
69
+
70
+ Start a temporary recording shell:
71
+
72
+ ```bash
73
+ oscopy record
74
+ # or `oscopy start`
75
+ ```
76
+
77
+ Session mode currently runs in a temporary `zsh` shell.
78
+
79
+ Then run commands normally. When done:
80
+
81
+ ```bash
82
+ oscopy stop
83
+ ```
84
+
85
+ This copies a transcript like:
86
+
87
+ ```bash
88
+ $ ls *.md
89
+ -rw-r--r--@ 1 hammerill staff 1.1K Mar 10 12:48 README.md
90
+
91
+ $ uname -a
92
+ Darwin pommier 25.3.0 Darwin Kernel Version 25.3.0 ...
93
+ ```
94
+
95
+ The transcript uses `$ ` as the command prefix, includes both stdout and stderr, and inserts clean blank lines between commands.
96
+
97
+ ## Install
98
+
99
+ Install as a [uv tool](https://docs.astral.sh/uv) from this GitHub repo:
100
+
101
+ ```bash
102
+ uv tool install git+https://github.com/hammerill/oscopy
103
+
104
+ # and, if you have OhMyZsh installed, quickly setup recommended aliases orun, orec and ostop:
105
+ oscopy aliases > ~/.oh-my-zsh/custom/oscopy-aliases.zsh
106
+ ```
107
+
108
+ Or local dev install:
109
+
110
+ ```bash
111
+ # in oscopy project folder
112
+ uv tool install -e .
113
+ ```
@@ -0,0 +1,17 @@
1
+ [project]
2
+ name = "oscopy"
3
+ version = "0.1.0"
4
+ description = "Python script to copy from STD to your clipboard with OSC 52."
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "hammerill", email = "kyrylo@hammerill.com" }
8
+ ]
9
+ requires-python = ">=3.12"
10
+ dependencies = []
11
+
12
+ [project.scripts]
13
+ oscopy = "oscopy.cli:main"
14
+
15
+ [build-system]
16
+ requires = ["uv_build>=0.9.18,<0.10.0"]
17
+ build-backend = "uv_build"
@@ -0,0 +1,2 @@
1
+ def main() -> None:
2
+ print("Hello from oscopy!")
@@ -0,0 +1,365 @@
1
+ import argparse
2
+ import base64
3
+ import os
4
+ from pathlib import Path
5
+ import re
6
+ import shlex
7
+ import shutil
8
+ import subprocess
9
+ import sys
10
+ import tempfile
11
+
12
+
13
+ OSC_SELECTION = "c"
14
+ """The system clipboard target OSC selection."""
15
+
16
+ RECORD_OUTPUT_LOG_ENV = "OSCOPY_RECORD_OUTPUT_LOG"
17
+ RECORD_COMMAND_LOG_ENV = "OSCOPY_RECORD_CMD_LOG"
18
+ RECORD_OFFSET_LOG_ENV = "OSCOPY_RECORD_OFFSET_LOG"
19
+ RECORD_DONE_FILE_ENV = "OSCOPY_RECORD_DONE_FILE"
20
+
21
+ _ANSI_ESCAPE_RE = re.compile(
22
+ r"\x1b(?:\][^\x07]*(?:\x07|\x1b\\)|\[[0-?]*[ -/]*[@-~]|[@-Z\\-_])"
23
+ )
24
+ _CTRL_CHARS_RE = re.compile(r"[\x00-\x08\x0b-\x1f\x7f]")
25
+
26
+
27
+ def _osc52(data: bytes) -> bytes:
28
+ # OSC 52: ESC ] 52 ; <selection> ; <base64> BEL
29
+ payload = base64.b64encode(data)
30
+ return b"\x1b]52;" + OSC_SELECTION.encode("ascii") + b";" + payload + b"\x07"
31
+
32
+
33
+ def _write_clipboard(data: bytes) -> None:
34
+ payload = _osc52(data)
35
+
36
+ # Prefer the controlling TTY so clipboard writes still work even when
37
+ # stdout is redirected (for example in session recording mode).
38
+ try:
39
+ with open("/dev/tty", "wb", buffering=0) as tty:
40
+ tty.write(payload)
41
+ tty.flush()
42
+ return
43
+ except OSError:
44
+ pass
45
+
46
+ sys.stdout.buffer.write(payload)
47
+ sys.stdout.buffer.flush()
48
+
49
+
50
+ def _render_transcript(entries: list[tuple[str, str]]) -> str:
51
+ blocks: list[str] = []
52
+ for command, output in entries:
53
+ cleaned = output.rstrip("\n")
54
+ if cleaned:
55
+ blocks.append(f"$ {command}\n{cleaned}")
56
+ else:
57
+ blocks.append(f"$ {command}")
58
+ return "\n\n".join(blocks)
59
+
60
+
61
+ def _cmd_copy(args: argparse.Namespace) -> int:
62
+ data = sys.stdin.buffer.read()
63
+ if args.strip_trailing_newline and data.endswith(b"\n"):
64
+ data = data[:-1]
65
+
66
+ _write_clipboard(data)
67
+ return 0
68
+
69
+
70
+ def _cmd_run(args: argparse.Namespace) -> int:
71
+ tokens = list(args.cmd)
72
+ if tokens and tokens[0] == "--":
73
+ tokens = tokens[1:]
74
+ if not tokens:
75
+ print("oscopy run: missing command", file=sys.stderr)
76
+ return 2
77
+
78
+ command = shlex.join(tokens)
79
+ shell = os.environ.get("SHELL") or "/bin/sh"
80
+
81
+ process = subprocess.Popen(
82
+ [shell, "-lc", command],
83
+ stdout=subprocess.PIPE,
84
+ stderr=subprocess.STDOUT,
85
+ )
86
+
87
+ captured = bytearray()
88
+ assert process.stdout is not None
89
+ while True:
90
+ chunk = process.stdout.read(8192)
91
+ if not chunk:
92
+ break
93
+ captured.extend(chunk)
94
+ sys.stdout.buffer.write(chunk)
95
+ sys.stdout.buffer.flush()
96
+
97
+ exit_code = process.wait()
98
+
99
+ output_text = _clean_terminal_text(captured.decode("utf-8", errors="replace"))
100
+ transcript = _render_transcript([(command, output_text)])
101
+ _write_clipboard(transcript.encode("utf-8"))
102
+ return exit_code
103
+
104
+
105
+ def _resolve_record_shell() -> str | None:
106
+ shell = os.environ.get("SHELL")
107
+ if shell and Path(shell).name == "zsh" and Path(shell).exists():
108
+ return shell
109
+ return shutil.which("zsh")
110
+
111
+
112
+ def _zsh_record_rc() -> str:
113
+ return """if [[ -f \"${HOME}/.zshrc\" ]]; then
114
+ source \"${HOME}/.zshrc\"
115
+ fi
116
+
117
+ typeset -g OSCOPY_SAVED_STDOUT_FD=-1
118
+ typeset -g OSCOPY_SAVED_STDERR_FD=-1
119
+ typeset -g OSCOPY_CAPTURE_ACTIVE=0
120
+
121
+ _oscopy_preexec() {
122
+ builtin print -r -- \"$1\" >> \"$OSCOPY_RECORD_CMD_LOG\"
123
+ local start_offset
124
+ start_offset=$(command wc -c < \"$OSCOPY_RECORD_OUTPUT_LOG\" 2>/dev/null || builtin echo 0)
125
+ builtin printf '%s\\n' \"$start_offset\" >> \"$OSCOPY_RECORD_OFFSET_LOG\"
126
+
127
+ exec {OSCOPY_SAVED_STDOUT_FD}>&1
128
+ exec {OSCOPY_SAVED_STDERR_FD}>&2
129
+ exec > >(tee -a \"$OSCOPY_RECORD_OUTPUT_LOG\") 2>&1
130
+ OSCOPY_CAPTURE_ACTIVE=1
131
+ }
132
+
133
+ _oscopy_precmd() {
134
+ if [[ \"$OSCOPY_CAPTURE_ACTIVE\" != \"1\" ]]; then
135
+ return
136
+ fi
137
+
138
+ exec 1>&$OSCOPY_SAVED_STDOUT_FD 2>&$OSCOPY_SAVED_STDERR_FD
139
+ exec {OSCOPY_SAVED_STDOUT_FD}>&-
140
+ exec {OSCOPY_SAVED_STDERR_FD}>&-
141
+ OSCOPY_CAPTURE_ACTIVE=0
142
+
143
+ local end_offset
144
+ end_offset=$(command wc -c < \"$OSCOPY_RECORD_OUTPUT_LOG\" 2>/dev/null || builtin echo 0)
145
+ builtin printf '%s\\n' \"$end_offset\" >> \"$OSCOPY_RECORD_OFFSET_LOG\"
146
+ }
147
+
148
+ autoload -Uz add-zsh-hook
149
+ add-zsh-hook preexec _oscopy_preexec
150
+ add-zsh-hook precmd _oscopy_precmd
151
+
152
+ oscopy() {
153
+ command oscopy \"$@\"
154
+ local code=$?
155
+ if [[ \"$1\" == \"stop\" ]]; then
156
+ exit \"$code\"
157
+ fi
158
+ return \"$code\"
159
+ }
160
+ """
161
+
162
+
163
+ def _clean_terminal_text(text: str) -> str:
164
+ # Remove OSC/CSI escapes and non-printable control chars for clean transcripts.
165
+ text = _ANSI_ESCAPE_RE.sub("", text)
166
+ text = text.replace("\r", "")
167
+ text = _CTRL_CHARS_RE.sub("", text)
168
+ return text
169
+
170
+
171
+ def _cmd_record() -> int:
172
+ if os.environ.get(RECORD_OUTPUT_LOG_ENV):
173
+ print("oscopy: recording is already active in this shell", file=sys.stderr)
174
+ return 1
175
+
176
+ shell = _resolve_record_shell()
177
+ if shell is None:
178
+ print("oscopy record currently requires zsh", file=sys.stderr)
179
+ return 1
180
+
181
+ session_dir = Path(tempfile.mkdtemp(prefix="oscopy-record-"))
182
+ output_log = session_dir / "output.log"
183
+ command_log = session_dir / "commands.log"
184
+ offset_log = session_dir / "offsets.log"
185
+ done_file = session_dir / "done"
186
+ zdotdir = session_dir / "zdotdir"
187
+ zdotdir.mkdir(parents=True, exist_ok=True)
188
+
189
+ output_log.touch()
190
+ command_log.touch()
191
+ offset_log.touch()
192
+ (zdotdir / ".zshrc").write_text(_zsh_record_rc(), encoding="utf-8")
193
+
194
+ env = os.environ.copy()
195
+ env[RECORD_OUTPUT_LOG_ENV] = str(output_log)
196
+ env[RECORD_COMMAND_LOG_ENV] = str(command_log)
197
+ env[RECORD_OFFSET_LOG_ENV] = str(offset_log)
198
+ env[RECORD_DONE_FILE_ENV] = str(done_file)
199
+ env["ZDOTDIR"] = str(zdotdir)
200
+
201
+ print("Recording session started. Run `oscopy stop` to finish and copy.", file=sys.stderr)
202
+ shell_code = subprocess.call([shell, "-i"], env=env)
203
+
204
+ finished = done_file.exists()
205
+ if not finished:
206
+ print("Recording ended without `oscopy stop`; nothing copied.", file=sys.stderr)
207
+
208
+ shutil.rmtree(session_dir, ignore_errors=True)
209
+ if finished:
210
+ return 0
211
+ return shell_code
212
+
213
+
214
+ def _parse_record_entries(output_log: Path, command_log: Path, offset_log: Path) -> list[tuple[str, str]]:
215
+ output_bytes = output_log.read_bytes()
216
+ commands = command_log.read_text(encoding="utf-8", errors="replace").splitlines()
217
+ offset_values: list[int] = []
218
+ for raw in offset_log.read_text(encoding="utf-8", errors="replace").splitlines():
219
+ line = raw.strip()
220
+ if not line:
221
+ continue
222
+ try:
223
+ offset = int(line)
224
+ except ValueError:
225
+ continue
226
+ offset_values.append(offset)
227
+
228
+ ranges: list[tuple[int, int]] = []
229
+ size = len(output_bytes)
230
+ for i in range(0, len(offset_values) - 1, 2):
231
+ start = max(0, min(size, offset_values[i]))
232
+ end = max(0, min(size, offset_values[i + 1]))
233
+ if end < start:
234
+ start, end = end, start
235
+ ranges.append((start, end))
236
+
237
+ entries: list[tuple[str, str]] = []
238
+ for idx, (start, end) in enumerate(ranges):
239
+ command = commands[idx] if idx < len(commands) else ""
240
+ stripped = command.strip()
241
+ if not stripped:
242
+ continue
243
+ if stripped.startswith("oscopy stop"):
244
+ continue
245
+ segment = _clean_terminal_text(output_bytes[start:end].decode("utf-8", errors="replace"))
246
+ entries.append((stripped, segment))
247
+
248
+ return entries
249
+
250
+
251
+ def _cmd_stop() -> int:
252
+ output_log = os.environ.get(RECORD_OUTPUT_LOG_ENV)
253
+ command_log = os.environ.get(RECORD_COMMAND_LOG_ENV)
254
+ offset_log = os.environ.get(RECORD_OFFSET_LOG_ENV)
255
+ done_file = os.environ.get(RECORD_DONE_FILE_ENV)
256
+
257
+ if not output_log or not command_log or not offset_log:
258
+ print("oscopy stop: no active recording session", file=sys.stderr)
259
+ return 1
260
+
261
+ output_path = Path(output_log)
262
+ command_path = Path(command_log)
263
+ offset_path = Path(offset_log)
264
+ if not output_path.exists() or not command_path.exists() or not offset_path.exists():
265
+ print("oscopy stop: recording session data is missing", file=sys.stderr)
266
+ return 1
267
+
268
+ entries = _parse_record_entries(output_path, command_path, offset_path)
269
+ if not entries:
270
+ print("oscopy stop: no recorded commands found; clipboard unchanged", file=sys.stderr)
271
+ if done_file:
272
+ Path(done_file).touch()
273
+ return 1
274
+ transcript = _render_transcript(entries)
275
+ _write_clipboard(transcript.encode("utf-8"))
276
+
277
+ if done_file:
278
+ Path(done_file).touch()
279
+
280
+ return 0
281
+
282
+
283
+ def _cmd_aliases() -> int:
284
+ print("alias orun='oscopy run --'")
285
+ print("alias orec='oscopy record'")
286
+ print("alias ostop='oscopy stop'")
287
+ return 0
288
+
289
+
290
+ def _build_parser() -> argparse.ArgumentParser:
291
+ parser = argparse.ArgumentParser(
292
+ prog="oscopy",
293
+ description="Copy text to clipboard via OSC 52, or record shell commands and output.",
294
+ )
295
+ subparsers = parser.add_subparsers(dest="mode")
296
+
297
+ copy_parser = subparsers.add_parser(
298
+ "copy",
299
+ help="copy stdin to clipboard",
300
+ description="Copy stdin to clipboard via OSC 52.",
301
+ )
302
+ copy_parser.add_argument(
303
+ "-x",
304
+ "-s",
305
+ "--strip-trailing-newline",
306
+ action="store_true",
307
+ help="strip the trailing newline (useful with echo)",
308
+ )
309
+
310
+ run_parser = subparsers.add_parser(
311
+ "run",
312
+ help="run one command, copy `$ command + output`",
313
+ description="Run one command and copy its transcript to clipboard.",
314
+ )
315
+ run_parser.add_argument(
316
+ "cmd",
317
+ nargs=argparse.REMAINDER,
318
+ help="command to execute (prefix with -- if needed)",
319
+ )
320
+
321
+ subparsers.add_parser(
322
+ "record",
323
+ help="start a temporary recording shell",
324
+ description="Start a recording shell session. Stop with `oscopy stop`.",
325
+ )
326
+ subparsers.add_parser("start", help="alias for record")
327
+ subparsers.add_parser(
328
+ "stop",
329
+ help="stop recording and copy transcript",
330
+ description="Stop an active recording session and copy its transcript.",
331
+ )
332
+ subparsers.add_parser(
333
+ "aliases",
334
+ help="print shell aliases",
335
+ description="Print convenience aliases for oscopy.",
336
+ )
337
+
338
+ return parser
339
+
340
+
341
+ def main(argv: list[str] | None = None) -> int:
342
+ raw_argv = list(sys.argv[1:] if argv is None else argv)
343
+
344
+ known_modes = {"copy", "run", "record", "start", "stop", "aliases", "-h", "--help"}
345
+ if not raw_argv:
346
+ raw_argv = ["copy"]
347
+ elif raw_argv[0] not in known_modes:
348
+ raw_argv = ["copy", *raw_argv]
349
+
350
+ parser = _build_parser()
351
+ args = parser.parse_args(raw_argv)
352
+
353
+ if args.mode == "copy":
354
+ return _cmd_copy(args)
355
+ if args.mode == "run":
356
+ return _cmd_run(args)
357
+ if args.mode in {"record", "start"}:
358
+ return _cmd_record()
359
+ if args.mode == "stop":
360
+ return _cmd_stop()
361
+ if args.mode == "aliases":
362
+ return _cmd_aliases()
363
+
364
+ parser.print_help()
365
+ return 2