oopsx 0.0.1 → 0.0.2
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.
- package/README.md +54 -36
- package/bin/merr.js +54 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,45 +2,66 @@
|
|
|
2
2
|
|
|
3
3
|
A CLI that plays a meme sound when your terminal command fails.
|
|
4
4
|
|
|
5
|
-
Wrap any command with `oopsx`. If it fails (non-zero exit code), you hear a sound. If it passes, nothing happens. Your original output and exit code are preserved.
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
$ oopsx git puhs origin main
|
|
9
|
-
fatal: 'puhs' is not a git command.
|
|
10
|
-
🔊 *plays sound*
|
|
11
|
-
```
|
|
12
|
-
|
|
13
5
|
## Install
|
|
14
6
|
|
|
15
7
|
```bash
|
|
16
8
|
npm install -g oopsx
|
|
17
9
|
```
|
|
18
10
|
|
|
19
|
-
|
|
11
|
+
## Setup (recommended)
|
|
12
|
+
|
|
13
|
+
Add this to your `~/.zshrc`:
|
|
20
14
|
|
|
21
15
|
```bash
|
|
22
|
-
|
|
16
|
+
eval "$(oopsx init zsh)"
|
|
23
17
|
```
|
|
24
18
|
|
|
25
|
-
Or
|
|
19
|
+
Or for bash, add to `~/.bashrc`:
|
|
26
20
|
|
|
27
21
|
```bash
|
|
28
|
-
|
|
22
|
+
eval "$(oopsx init bash)"
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Restart your terminal. Now every failed command plays a sound automatically — no prefix needed.
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
$ git puhs origin main
|
|
29
|
+
fatal: 'puhs' is not a git command.
|
|
30
|
+
🔊 *sound plays*
|
|
31
|
+
|
|
32
|
+
$ ls /does/not/exist
|
|
33
|
+
ls: /does/not/exist: No such file or directory
|
|
34
|
+
🔊 *sound plays*
|
|
35
|
+
|
|
36
|
+
$ echo "hello"
|
|
37
|
+
hello
|
|
38
|
+
# silence — command passed
|
|
29
39
|
```
|
|
30
40
|
|
|
31
|
-
##
|
|
41
|
+
## Wrap a single command
|
|
42
|
+
|
|
43
|
+
If you don't want the shell hook, you can wrap individual commands:
|
|
32
44
|
|
|
33
45
|
```bash
|
|
34
|
-
oopsx
|
|
46
|
+
oopsx ls /does/not/exist
|
|
47
|
+
oopsx npm test
|
|
48
|
+
oopsx --sound ~/bruh.mp3 make build
|
|
35
49
|
```
|
|
36
50
|
|
|
51
|
+
## Custom sounds
|
|
52
|
+
|
|
37
53
|
```bash
|
|
38
|
-
|
|
39
|
-
oopsx --sound ~/bruh.mp3 npm test
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
oopsx --
|
|
43
|
-
|
|
54
|
+
# Use a local file
|
|
55
|
+
oopsx --sound ~/bruh.mp3 npm test
|
|
56
|
+
|
|
57
|
+
# Download from URL (cached automatically)
|
|
58
|
+
oopsx --url https://example.com/fail.mp3 make build
|
|
59
|
+
|
|
60
|
+
# Save a sound as the new default
|
|
61
|
+
oopsx --sound ~/oof.mp3 --set-default echo hi
|
|
62
|
+
|
|
63
|
+
# Set volume (0-100)
|
|
64
|
+
oopsx --volume 50 cargo build
|
|
44
65
|
```
|
|
45
66
|
|
|
46
67
|
## Options
|
|
@@ -53,27 +74,22 @@ oopsx --once cargo build # play only once per session
|
|
|
53
74
|
| `--set-default` | Save the resolved sound as the new default |
|
|
54
75
|
| `--once` | Play sound only once per shell session |
|
|
55
76
|
|
|
56
|
-
##
|
|
57
|
-
|
|
58
|
-
1. Runs your command with inherited stdio — you see all output as normal.
|
|
59
|
-
2. If exit code is `0`, does nothing.
|
|
60
|
-
3. If exit code is non-zero, plays a sound.
|
|
61
|
-
4. Exits with the **same exit code** as your command.
|
|
77
|
+
## Commands
|
|
62
78
|
|
|
63
|
-
|
|
79
|
+
| Command | Description |
|
|
80
|
+
| --------------- | ------------------------------------------------ |
|
|
81
|
+
| `oopsx init zsh` | Print shell hook for zsh |
|
|
82
|
+
| `oopsx init bash` | Print shell hook for bash |
|
|
83
|
+
| `oopsx play` | Play the sound (used by the hook internally) |
|
|
84
|
+
| `oopsx <cmd>` | Wrap a command and play sound on failure |
|
|
64
85
|
|
|
65
|
-
|
|
86
|
+
## How it works
|
|
66
87
|
|
|
67
|
-
|
|
88
|
+
**Shell hook mode**: After each command, the hook checks the exit code. If non-zero, it plays the sound in the background so it doesn't block your prompt.
|
|
68
89
|
|
|
69
|
-
|
|
90
|
+
**Wrapper mode**: Runs your command, checks exit code, plays sound if it failed, then exits with the same code.
|
|
70
91
|
|
|
71
|
-
|
|
72
|
-
~/.merr/
|
|
73
|
-
├── config.json # volume
|
|
74
|
-
├── default.mp3 # default sound
|
|
75
|
-
└── sounds/ # cached URL downloads
|
|
76
|
-
```
|
|
92
|
+
A default sound is bundled. Config and cached sounds live in `~/.merr/`.
|
|
77
93
|
|
|
78
94
|
## Platform support
|
|
79
95
|
|
|
@@ -90,6 +106,8 @@ npm uninstall -g oopsx
|
|
|
90
106
|
rm -rf ~/.merr
|
|
91
107
|
```
|
|
92
108
|
|
|
109
|
+
Remove the `eval "$(oopsx init ...)"` line from your shell rc file.
|
|
110
|
+
|
|
93
111
|
## License
|
|
94
112
|
|
|
95
113
|
MIT
|
package/bin/merr.js
CHANGED
|
@@ -12,8 +12,60 @@ const program = new Command();
|
|
|
12
12
|
program
|
|
13
13
|
.name("oopsx")
|
|
14
14
|
.description("Play a meme sound when your terminal command fails")
|
|
15
|
-
.version("1.0.0")
|
|
16
|
-
|
|
15
|
+
.version("1.0.0");
|
|
16
|
+
|
|
17
|
+
// --- `oopsx init <shell>` — output shell hook snippet ---
|
|
18
|
+
program
|
|
19
|
+
.command("init <shell>")
|
|
20
|
+
.description("Print shell hook to auto-play sound on errors (zsh or bash)")
|
|
21
|
+
.action((shell) => {
|
|
22
|
+
const normalized = shell.toLowerCase().trim();
|
|
23
|
+
|
|
24
|
+
if (normalized === "zsh") {
|
|
25
|
+
console.log(`
|
|
26
|
+
__oopsx_precmd() {
|
|
27
|
+
local exit_code=$?
|
|
28
|
+
[[ $exit_code -ne 0 ]] && oopsx play &>/dev/null &
|
|
29
|
+
return $exit_code
|
|
30
|
+
}
|
|
31
|
+
precmd_functions+=(__oopsx_precmd)
|
|
32
|
+
`.trim());
|
|
33
|
+
} else if (normalized === "bash") {
|
|
34
|
+
console.log(`
|
|
35
|
+
__oopsx_prompt_command() {
|
|
36
|
+
local exit_code=$?
|
|
37
|
+
[[ $exit_code -ne 0 ]] && oopsx play &>/dev/null &
|
|
38
|
+
return $exit_code
|
|
39
|
+
}
|
|
40
|
+
PROMPT_COMMAND="__oopsx_prompt_command;\${PROMPT_COMMAND}"
|
|
41
|
+
`.trim());
|
|
42
|
+
} else {
|
|
43
|
+
console.error(`Unsupported shell: ${shell}. Use "zsh" or "bash".`);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// --- `oopsx play` — just play the sound (used by shell hook) ---
|
|
49
|
+
program
|
|
50
|
+
.command("play")
|
|
51
|
+
.description("Play the error sound (used internally by the shell hook)")
|
|
52
|
+
.action(async () => {
|
|
53
|
+
const config = readConfig();
|
|
54
|
+
const soundFile = await resolveSound({});
|
|
55
|
+
|
|
56
|
+
if (!soundFile) process.exit(0);
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
await playAudio(soundFile, config.volume ?? 80);
|
|
60
|
+
} catch {
|
|
61
|
+
// silent — this runs in background, don't pollute the terminal
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// --- `oopsx run <command...>` — wrap a single command (original behavior) ---
|
|
66
|
+
program
|
|
67
|
+
.command("run <command...>", { isDefault: true })
|
|
68
|
+
.description("Execute a command and play sound on failure")
|
|
17
69
|
.option("--sound <path>", "Use a local sound file (mp3/wav)")
|
|
18
70
|
.option("--url <url>", "Download and cache a sound from a URL")
|
|
19
71
|
.option("--volume <number>", "Playback volume 0–100", parseInt)
|
|
@@ -24,7 +76,6 @@ program
|
|
|
24
76
|
const config = readConfig();
|
|
25
77
|
const volume = options.volume ?? config.volume ?? 80;
|
|
26
78
|
|
|
27
|
-
// Persist volume if explicitly provided
|
|
28
79
|
if (options.volume !== undefined) {
|
|
29
80
|
setVolume(options.volume);
|
|
30
81
|
}
|
|
@@ -35,7 +86,6 @@ program
|
|
|
35
86
|
process.exit(0);
|
|
36
87
|
}
|
|
37
88
|
|
|
38
|
-
// --once: skip if already played in this shell session
|
|
39
89
|
if (options.once && process.env[SESSION_KEY]) {
|
|
40
90
|
process.exit(exitCode);
|
|
41
91
|
}
|
|
@@ -46,7 +96,6 @@ program
|
|
|
46
96
|
process.exit(exitCode);
|
|
47
97
|
}
|
|
48
98
|
|
|
49
|
-
// --set-default: persist the resolved sound
|
|
50
99
|
if (options.setDefault) {
|
|
51
100
|
setDefault(soundFile);
|
|
52
101
|
}
|
|
@@ -57,7 +106,6 @@ program
|
|
|
57
106
|
console.error(`Sound playback failed: ${err.message}`);
|
|
58
107
|
}
|
|
59
108
|
|
|
60
|
-
// Mark as played for --once (only useful if parent reads env, but we set it anyway)
|
|
61
109
|
if (options.once) {
|
|
62
110
|
process.env[SESSION_KEY] = "1";
|
|
63
111
|
}
|