claudible 0.1.1__py3-none-any.whl → 0.1.3__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.
claudible/audio.py CHANGED
@@ -33,8 +33,8 @@ class SoundEngine:
33
33
  self._write_pos = 0
34
34
  self._read_pos = 0
35
35
 
36
- # Pre-generate grain variations for efficiency
37
- self._grain_cache = [self._generate_grain() for _ in range(8)]
36
+ # Pre-generate grains spread across registers for tonal variety
37
+ self._grain_cache = self._build_grain_cache()
38
38
  self._cache_index = 0
39
39
 
40
40
  def start(self):
@@ -141,77 +141,147 @@ class SoundEngine:
141
141
 
142
142
  # --- Sound generation ---
143
143
 
144
- def _generate_grain(self) -> np.ndarray:
144
+ def _build_grain_cache(self) -> list:
145
+ """Build grain cache with voices at full-octave intervals, biased low + high."""
146
+ # Each register is a full octave apart.
147
+ # (octave_shift, noise_mult, decay_mult, duration_mult)
148
+ # Multiple character variations per register.
149
+ voices = [
150
+ # --- oct -3: sub rumble (3 voices) ---
151
+ (-3, 3.5, 2.5, 1.8), # sub thump
152
+ (-3, 2.0, 3.0, 2.0), # sub knock
153
+ (-3, 2.8, 2.0, 1.6), # sub punch
154
+
155
+ # --- oct -2: deep (3 voices) ---
156
+ (-2, 2.8, 1.8, 1.5), # deep percussive
157
+ (-2, 1.2, 2.0, 1.4), # deep tonal
158
+ (-2, 2.0, 1.5, 1.3), # deep knock
159
+
160
+ # --- oct -1: low (3 voices) ---
161
+ (-1, 1.8, 1.4, 1.2), # low thump
162
+ (-1, 0.8, 1.2, 1.1), # low warm
163
+ (-1, 1.4, 1.6, 1.2), # low snap
164
+
165
+ # --- oct 0: mid (2 voices) ---
166
+ ( 0, 1.0, 1.0, 1.0), # mid click
167
+ ( 0, 0.6, 0.8, 0.9), # mid soft
168
+
169
+ # --- oct +1: high (3 voices) ---
170
+ ( 1, 0.5, 0.6, 0.7), # high ping
171
+ ( 1, 0.8, 0.5, 0.7), # high tick
172
+ ( 1, 0.3, 0.7, 0.8), # high ring
173
+
174
+ # --- oct +2: bright (3 voices) ---
175
+ ( 2, 0.2, 0.4, 0.55), # bright ting
176
+ ( 2, 0.5, 0.3, 0.6), # bright click
177
+ ( 2, 0.15, 0.5, 0.5), # bright shimmer
178
+
179
+ # --- oct +3: air (3 voices) ---
180
+ ( 3, 0.1, 0.25, 0.4), # air sparkle
181
+ ( 3, 0.3, 0.2, 0.45), # air tick
182
+ ( 3, 0.05, 0.3, 0.35), # air wisp
183
+ ]
184
+ cache = []
185
+ for octave, noise_m, decay_m, dur_m in voices:
186
+ cache.append(self._generate_grain(
187
+ octave_shift=octave,
188
+ noise_mult=noise_m,
189
+ decay_mult=decay_m,
190
+ duration_mult=dur_m,
191
+ ))
192
+ return cache
193
+
194
+ def _generate_grain(self, octave_shift: float = 0.0, noise_mult: float = 1.0,
195
+ decay_mult: float = 1.0, duration_mult: float = 1.0) -> np.ndarray:
145
196
  """Generate a single grain sound from the material's physical model."""
146
197
  m = self.material
147
- # Vary duration per grain
148
- duration = m.grain_duration * np.random.uniform(0.85, 1.15)
198
+ duration = m.grain_duration * duration_mult * 0.5 * np.random.uniform(0.85, 1.15)
149
199
  n = int(duration * self.SAMPLE_RATE)
150
200
  t = np.linspace(0, duration, n, dtype=np.float32)
151
201
 
152
202
  grain = np.zeros(n, dtype=np.float32)
153
203
 
154
- # Randomise base frequency within the material's spread
155
- base_freq = m.base_freq * (2 ** (np.random.uniform(-m.freq_spread, m.freq_spread) / 12))
204
+ # Base frequency shifted by register
205
+ base_freq = m.base_freq * (2 ** octave_shift)
206
+ base_freq *= (2 ** (np.random.uniform(-m.freq_spread, m.freq_spread) / 12))
156
207
 
157
- # Generate each partial with its own decay, detuning, and random phase
158
208
  for i, (ratio, amp) in enumerate(m.partials):
159
209
  detune_cents = np.random.uniform(-m.detune_amount, m.detune_amount)
160
210
  freq = base_freq * ratio * (2 ** (detune_cents / 1200))
161
- # Extra micro-variation per partial
162
211
  freq *= np.random.uniform(0.997, 1.003)
163
212
  phase = np.random.uniform(0, 2 * np.pi)
164
213
 
165
214
  decay_rate = m.decay_rates[i] if i < len(m.decay_rates) else m.decay_rates[-1]
215
+ decay_rate *= decay_mult
166
216
  envelope = np.exp(-decay_rate * t)
167
217
 
168
218
  grain += amp * envelope * np.sin(2 * np.pi * freq * t + phase)
169
219
 
170
- # Noise transient (the "tick" of physical impact)
220
+ # Noise transient louder for low percussive grains
171
221
  if m.attack_noise > 0:
172
222
  noise = np.random.randn(n).astype(np.float32)
173
- noise = self._highpass(noise, m.noise_freq)
174
- # Shape: fast attack, very fast decay with micro-variation
175
- noise_env = np.exp(-150 * t)
223
+ noise_cutoff = max(m.noise_freq * (0.5 if octave_shift < -0.5 else 1.0), 200)
224
+ noise = self._highpass(noise, noise_cutoff)
225
+ noise_env = np.exp(-150 * t / max(decay_mult, 0.5))
176
226
  noise_env *= np.clip(
177
227
  1.0 + 0.3 * np.random.randn(n).astype(np.float32) * np.exp(-200 * t),
178
228
  0, 1,
179
229
  )
180
- grain += m.attack_noise * 0.3 * noise * noise_env
230
+ grain += m.attack_noise * noise_mult * 0.3 * noise * noise_env
181
231
 
182
232
  # Attack shaping
183
233
  attack_samples = max(int(m.attack_ms / 1000 * self.SAMPLE_RATE), 2)
184
234
  if attack_samples < n:
185
235
  grain[:attack_samples] *= np.linspace(0, 1, attack_samples, dtype=np.float32)
186
236
 
187
- # Pitch drop envelope (physical: pitch drops as energy dissipates)
188
- if m.pitch_drop != 1.0:
189
- pitch_env = np.linspace(1.0, m.pitch_drop, n)
237
+ # Pitch drop more dramatic for low grains
238
+ pitch_drop = m.pitch_drop if octave_shift >= 0 else m.pitch_drop ** (1.0 + abs(octave_shift) * 0.3)
239
+ if pitch_drop != 1.0:
240
+ pitch_env = np.linspace(1.0, pitch_drop, n)
190
241
  phase_mod = np.cumsum(pitch_env) / np.sum(pitch_env) * n
191
242
  indices = np.clip(phase_mod.astype(int), 0, n - 1)
192
243
  grain = grain[indices]
193
244
 
194
- # Multi-tap reverb with HF damping
195
- grain = self._apply_reverb(grain, m.reverb_amount, m.room_size, m.reverb_damping)
245
+ # Reverb less for percussive lows (tighter), more for highs
246
+ reverb_amt = m.reverb_amount * (0.6 if octave_shift < -0.5 else 1.0 + max(octave_shift, 0) * 0.2)
247
+ grain = self._apply_reverb(grain, reverb_amt, m.room_size, m.reverb_damping)
196
248
 
197
- # Truncate reverb tail back to grain length
198
- grain = grain[:n]
249
+ # Small pre-delay + extra tail reverb
250
+ delay_ms = 15
251
+ delay_samples = int(delay_ms * self.SAMPLE_RATE / 1000)
252
+ tail_len = n + delay_samples + int(0.08 * self.SAMPLE_RATE)
253
+ out = np.zeros(tail_len, dtype=np.float32)
254
+ out[delay_samples:delay_samples + len(grain)] = grain
255
+ # Light tail reverb on top
256
+ out = self._apply_reverb(out, 0.15, room_size=1.2, damping=0.4)
199
257
 
200
- # Normalise with per-material volume
201
- peak = np.max(np.abs(grain))
258
+ peak = np.max(np.abs(out))
202
259
  if peak > 0:
203
- grain = grain / peak * 0.4 * m.volume
204
-
205
- return grain.astype(np.float32)
206
-
207
- def play_grain(self):
208
- """Play a grain/sparkle sound."""
209
- # Pick a random cached grain
210
- idx = np.random.randint(len(self._grain_cache))
260
+ out = out / peak * 0.4 * m.volume
261
+
262
+ return out.astype(np.float32)
263
+
264
+ @staticmethod
265
+ def _token_hash(token: str) -> int:
266
+ """Deterministic hash from token content, stable across sessions."""
267
+ h = 5381
268
+ for c in token:
269
+ h = ((h * 33) ^ ord(c)) & 0xFFFFFFFF
270
+ return h
271
+
272
+ def play_grain(self, token: str = ""):
273
+ """Play a grain/sparkle sound, deterministic for a given token."""
274
+ if token:
275
+ h = self._token_hash(token)
276
+ rng = np.random.RandomState(h)
277
+ else:
278
+ rng = np.random
279
+
280
+ idx = rng.randint(len(self._grain_cache))
211
281
  grain = self._grain_cache[idx].copy()
212
282
 
213
- # Per-play pitch variation via resampling 4 semitones)
214
- pitch_shift = 2 ** (np.random.uniform(-4, 4) / 12)
283
+ # Narrow pitch micro-variation (±1.5 semitones) — stays in its register
284
+ pitch_shift = 2 ** (rng.uniform(-1.5, 1.5) / 12)
215
285
  if abs(pitch_shift - 1.0) > 0.001:
216
286
  new_len = int(len(grain) / pitch_shift)
217
287
  if new_len > 0:
@@ -221,12 +291,7 @@ class SoundEngine:
221
291
  grain,
222
292
  ).astype(np.float32)
223
293
 
224
- # Random amplitude variation
225
- grain *= np.random.uniform(0.6, 1.0)
226
-
227
- # Regenerate cached grains aggressively for variety
228
- if np.random.random() < 0.5:
229
- self._grain_cache[np.random.randint(len(self._grain_cache))] = self._generate_grain()
294
+ grain *= rng.uniform(0.7, 1.0)
230
295
 
231
296
  self._add_to_buffer(grain)
232
297
 
claudible/cli.py CHANGED
@@ -14,6 +14,24 @@ from .materials import get_material, get_random_material, list_materials, MATERI
14
14
  from .monitor import ActivityMonitor
15
15
 
16
16
 
17
+ def run_demo(volume: float):
18
+ """Play a short demo of every sound character."""
19
+ print("Claudible character demo\n", file=sys.stderr)
20
+ for name, mat in MATERIALS.items():
21
+ print(f" {name:10} - {mat.description}", file=sys.stderr)
22
+ engine = SoundEngine(material=mat, volume=volume)
23
+ engine.start()
24
+ # Play a burst of grains then a chime
25
+ for i in range(8):
26
+ engine.play_grain(chr(ord('a') + i))
27
+ time.sleep(0.06)
28
+ time.sleep(0.15)
29
+ engine.play_chime()
30
+ time.sleep(0.6)
31
+ engine.stop()
32
+ print("\nDone.", file=sys.stderr)
33
+
34
+
17
35
  def run_pipe_mode(engine: SoundEngine, monitor: ActivityMonitor):
18
36
  """Run in pipe mode, reading from stdin."""
19
37
  engine.start()
@@ -138,6 +156,11 @@ def main():
138
156
  action='store_true',
139
157
  help='List available sound characters',
140
158
  )
159
+ parser.add_argument(
160
+ '--demo',
161
+ action='store_true',
162
+ help='Play a short demo of each sound character',
163
+ )
141
164
 
142
165
  args = parser.parse_args()
143
166
 
@@ -147,6 +170,11 @@ def main():
147
170
  print(f" {name:10} - {mat.description}")
148
171
  return
149
172
 
173
+ if args.demo:
174
+ volume = max(0.0, min(1.0, args.volume))
175
+ run_demo(volume)
176
+ return
177
+
150
178
  if args.character:
151
179
  material = get_material(args.character)
152
180
  else:
claudible/monitor.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """I/O activity tracking and event detection."""
2
2
 
3
3
  import time
4
- from typing import Callable, Optional
4
+ from typing import Callable, Optional, Union
5
5
  import threading
6
6
 
7
7
 
@@ -17,7 +17,7 @@ class ActivityMonitor:
17
17
 
18
18
  def __init__(
19
19
  self,
20
- on_grain: Callable[[], None],
20
+ on_grain: Callable[[str], None],
21
21
  on_chime: Callable[[], None],
22
22
  on_attention: Callable[[], None],
23
23
  attention_seconds: float = 30.0,
@@ -68,7 +68,7 @@ class ActivityMonitor:
68
68
  if not self._reverse_playing:
69
69
  self._reverse_playing = True
70
70
  self.on_chime()
71
- self.on_grain()
71
+ self.on_grain("")
72
72
  time.sleep(reverse_interval)
73
73
  else:
74
74
  if self._reverse_playing:
@@ -109,11 +109,11 @@ class ActivityMonitor:
109
109
  self._consecutive_newlines = 0
110
110
  else:
111
111
  self._consecutive_newlines = 0
112
- self._maybe_trigger_grain()
112
+ self._maybe_trigger_grain(char)
113
113
 
114
- def _maybe_trigger_grain(self):
114
+ def _maybe_trigger_grain(self, char: str = ""):
115
115
  """Trigger a grain if enough time has passed (throttling)."""
116
116
  now = time.time()
117
117
  if now - self._last_grain_time >= self._min_grain_interval:
118
118
  self._last_grain_time = now
119
- self.on_grain()
119
+ self.on_grain(char)
@@ -0,0 +1,172 @@
1
+ Metadata-Version: 2.4
2
+ Name: claudible
3
+ Version: 0.1.3
4
+ Summary: Ambient audio soundscape feedback for terminal output - an opus for your terminals
5
+ License: MIT
6
+ Project-URL: Homepage, https://github.com/anthropics/claudible
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: Environment :: Console
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.8
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Multimedia :: Sound/Audio
19
+ Classifier: Topic :: Utilities
20
+ Requires-Python: >=3.8
21
+ Description-Content-Type: text/markdown
22
+ Requires-Dist: numpy>=1.20.0
23
+ Requires-Dist: scipy>=1.7.0
24
+ Requires-Dist: sounddevice>=0.4.0
25
+
26
+ ```
27
+ ♪ ♫ ♪
28
+ ┌─────────┐
29
+ │ ╭─────╮ │ ♩
30
+ │ │ ▓▓▓ │ │
31
+ │ │ ▓▓▓ │ │ ♬ ┈┈┈╮
32
+ │ ╰─────╯ │ ♪ │
33
+ │ claude │ ♫ │
34
+ └────┬────┘ ♩ ≋≋≋
35
+ ══════╧══════ ≋≋≋≋≋
36
+ ♪ audible ♫ ≋≋≋
37
+ ```
38
+
39
+ # 🎵 claudible
40
+
41
+ *An opus for your terminals.*
42
+
43
+ Possibly the most annoying Claude utility ever made but here it is anyway. Ambient audio soundscape feedback for terminal output.
44
+
45
+ ## 🎼 The Idea
46
+
47
+ Imagine you are working in a factory. Each Claude Code session is a machine that must be attended to. When it's working, you hear it — crystalline sparkles as text flows, soft chimes when tasks complete. When it goes quiet, you know something needs your attention.
48
+
49
+ ## 🎶 Installation
50
+
51
+ ```bash
52
+ # Recommended (handles PATH automatically)
53
+ brew install pipx && pipx ensurepath # macOS
54
+ # or: pip install --user pipx && pipx ensurepath
55
+
56
+ pipx install claudible
57
+
58
+ # Or with pip
59
+ pip install claudible
60
+ ```
61
+
62
+ > **`claudible` command not found?** If you used `pip install --user`, the script directory may not be on your PATH. Use `python3 -m claudible` instead, or switch to `pipx`.
63
+
64
+ ## 🎹 Usage
65
+
66
+ ```bash
67
+ # Run claude with audio feedback (default)
68
+ claudible
69
+
70
+ # Run a different command
71
+ claudible "python my_script.py"
72
+
73
+ # Pipe mode
74
+ some-command 2>&1 | claudible --pipe
75
+
76
+ # Choose a sound character
77
+ claudible --character crystal
78
+
79
+ # Adjust volume
80
+ claudible --volume 0.3
81
+
82
+ # List available characters
83
+ claudible --list-characters
84
+
85
+ # Demo all sound characters
86
+ claudible --demo
87
+ ```
88
+
89
+ ### 🔇 Reverse Mode
90
+
91
+ In reverse mode, claudible is silent while output is flowing and plays ambient sound during silence. Useful when you want to know a task is *waiting* rather than *working*.
92
+
93
+ ```bash
94
+ # Ambient grains play when Claude is idle/waiting for you
95
+ claudible --reverse
96
+
97
+ # Combine with a character
98
+ claudible --reverse -c shell
99
+ ```
100
+
101
+ ## 🎧 Works With Any CLI
102
+
103
+ Built for Claude Code, but claudible works with anything that produces terminal output.
104
+
105
+ ```bash
106
+ # Aider
107
+ claudible "aider"
108
+
109
+ # Watch a dev server
110
+ npm run dev 2>&1 | claudible --pipe
111
+
112
+ # Monitor logs
113
+ tail -f /var/log/app.log | claudible --pipe -c droplet
114
+ ```
115
+
116
+ ## 🎻 Sound Characters
117
+
118
+ | Character | | Description |
119
+ |-----------|---|-------------|
120
+ | `ice` | 🧊 | Brittle, very high, fast decay with pitch drop |
121
+ | `glass` | 🍷 | Classic wine glass ping |
122
+ | `crystal` | 💎 | Pure lead crystal with beating from close partial pairs |
123
+ | `ceramic` | 🏺 | Duller muted earthenware tap |
124
+ | `bell` | 🔔 | Small metallic bell, classic ratios, long ring |
125
+ | `droplet` | 💧 | Water droplet, pitch bend down, liquid |
126
+ | `click` | ⌨️ | Sharp mechanical click, keyboard-like |
127
+ | `wood` | 🪵 | Hollow wooden tap, warm marimba-like resonance |
128
+ | `stone` | 🪨 | Dense slate tap, heavy and earthy |
129
+ | `bamboo` | 🎋 | Hollow tube resonance, odd harmonics, breathy and airy |
130
+ | `ember` | 🔥 | Warm crackling ember, fire-like with wide pitch scatter |
131
+ | `silk` | 🪶 | Soft breathy whisper, delicate airy texture |
132
+ | `shell` | 🐚 | Swirly ocean interference, dense phase beating |
133
+ | `moss` | 🌿 | Ultra-soft muffled earth, mossy dampness |
134
+
135
+ ## 🎛️ Options
136
+
137
+ | Flag | Description |
138
+ |------|-------------|
139
+ | `--pipe` | 📥 Read from stdin instead of wrapping |
140
+ | `--character`, `-c` | 🎵 Sound character |
141
+ | `--volume`, `-v` | 🔊 Volume 0.0–1.0 (default: 0.5) |
142
+ | `--attention`, `-a` | ⏰ Silence alert seconds (default: 30) |
143
+ | `--reverse`, `-r` | 🔄 Reverse mode: sound during silence, quiet during output |
144
+ | `--list-characters` | 📋 Show presets |
145
+ | `--demo` | 🔊 Play a short demo of each sound character |
146
+
147
+ ## 🛠️ Development
148
+
149
+ Test locally without installing:
150
+
151
+ ```bash
152
+ cd claudible
153
+
154
+ # Run wrapping claude (Ctrl+C to stop)
155
+ PYTHONPATH=src python3 -m claudible
156
+
157
+ # Wrap a different command
158
+ PYTHONPATH=src python3 -m claudible "ls -la"
159
+
160
+ # Pipe mode
161
+ echo "test" | PYTHONPATH=src python3 -m claudible --pipe -c glass
162
+
163
+ # List characters
164
+ PYTHONPATH=src python3 -m claudible --list-characters
165
+
166
+ # Demo all characters
167
+ PYTHONPATH=src python3 -m claudible --demo
168
+ ```
169
+
170
+ ## 📜 License
171
+
172
+ MIT
@@ -0,0 +1,11 @@
1
+ claudible/__init__.py,sha256=xSLL4yfc1p79BWVb65PJBTFMxyJ2PWuu7pCLYcCuOVM,96
2
+ claudible/__main__.py,sha256=5mFnPjtOjk5Hw-mvUhRYtwXVnXFR7ZbXKC27VIXu_dI,106
3
+ claudible/audio.py,sha256=NXINffeLQlH89JJli-QF29WyYtpPl1BjcmfStvJ7geE,14741
4
+ claudible/cli.py,sha256=R7WttU-idl_S4yBx_bXKKRtftkdBS2_ioiNgZQ68a4w,5665
5
+ claudible/materials.py,sha256=6OsobcWOFvLBCuvzkeWi9C7fYHFnW9RED6nEElK-Ki4,9517
6
+ claudible/monitor.py,sha256=KAnb0rmnuTDanD7pgls50Oxd3-TlhXv2tUDjX8RkHIg,4106
7
+ claudible-0.1.3.dist-info/METADATA,sha256=SjWWWyDZIwGt1eQmcbQr_8UMCeCILUlS1fYb0mKrL10,5269
8
+ claudible-0.1.3.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
9
+ claudible-0.1.3.dist-info/entry_points.txt,sha256=4tBIsIGcdtJ-1N9YZ3MrADyNvPBDcUxVgAnt9IuC7TA,49
10
+ claudible-0.1.3.dist-info/top_level.txt,sha256=4ZB16Aa5ZDS12bmxzrQmAJejjsTgQ9Yozwc0Z3qpJp4,10
11
+ claudible-0.1.3.dist-info/RECORD,,
@@ -1,143 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: claudible
3
- Version: 0.1.1
4
- Summary: Ambient audio soundscape feedback for terminal output - an opus for your terminals
5
- License: MIT
6
- Project-URL: Homepage, https://github.com/anthropics/claudible
7
- Classifier: Development Status :: 4 - Beta
8
- Classifier: Environment :: Console
9
- Classifier: Intended Audience :: Developers
10
- Classifier: License :: OSI Approved :: MIT License
11
- Classifier: Operating System :: OS Independent
12
- Classifier: Programming Language :: Python :: 3
13
- Classifier: Programming Language :: Python :: 3.8
14
- Classifier: Programming Language :: Python :: 3.9
15
- Classifier: Programming Language :: Python :: 3.10
16
- Classifier: Programming Language :: Python :: 3.11
17
- Classifier: Programming Language :: Python :: 3.12
18
- Classifier: Topic :: Multimedia :: Sound/Audio
19
- Classifier: Topic :: Utilities
20
- Requires-Python: >=3.8
21
- Description-Content-Type: text/markdown
22
- Requires-Dist: numpy>=1.20.0
23
- Requires-Dist: scipy>=1.7.0
24
- Requires-Dist: sounddevice>=0.4.0
25
-
26
- # claudible
27
-
28
- *An opus for your terminals.*
29
-
30
- Possibly the most annoying Claude utility ever made but here it is anyway. Ambient audio soundscape feedback for terminal output.
31
-
32
- ## The Idea
33
-
34
- Imagine you are working in a factory. Each Claude Code session is a machine that must be attended to. When it's working, you hear it - crystalline sparkles as text flows, soft chimes when tasks complete. When it goes quiet, you know something needs your attention.
35
-
36
- ## Installation
37
-
38
- ```bash
39
- pip install claudible
40
- ```
41
-
42
- ## Usage
43
-
44
- ```bash
45
- # Run claude with audio feedback (default)
46
- claudible
47
-
48
- # Run a different command
49
- claudible "python my_script.py"
50
-
51
- # Pipe mode
52
- some-command 2>&1 | claudible --pipe
53
-
54
- # Choose a sound character
55
- claudible --character crystal
56
-
57
- # Adjust volume
58
- claudible --volume 0.3
59
-
60
- # List available characters
61
- claudible --list-characters
62
- ```
63
-
64
- ### Reverse Mode
65
-
66
- In reverse mode, claudible is silent while output is flowing and plays ambient sound during silence. Useful when you want to know a task is *waiting* rather than *working*.
67
-
68
- ```bash
69
- # Ambient grains play when Claude is idle/waiting for you
70
- claudible --reverse
71
-
72
- # Combine with a character
73
- claudible --reverse -c shell
74
- ```
75
-
76
- ## Works With Any CLI
77
-
78
- Built for Claude Code, but claudible works with anything that produces terminal output.
79
-
80
- ```bash
81
- # Aider
82
- claudible "aider"
83
-
84
- # Watch a dev server
85
- npm run dev 2>&1 | claudible --pipe
86
-
87
- # Monitor logs
88
- tail -f /var/log/app.log | claudible --pipe -c droplet
89
- ```
90
-
91
- ## Sound Characters
92
-
93
- | Character | Description |
94
- |-----------|-------------|
95
- | `ice` | Brittle, very high, fast decay with pitch drop |
96
- | `glass` | Classic wine glass ping |
97
- | `crystal` | Pure lead crystal with beating from close partial pairs |
98
- | `ceramic` | Duller muted earthenware tap |
99
- | `bell` | Small metallic bell, classic ratios, long ring |
100
- | `droplet` | Water droplet, pitch bend down, liquid |
101
- | `click` | Sharp mechanical click, keyboard-like |
102
- | `wood` | Hollow wooden tap, warm marimba-like resonance |
103
- | `stone` | Dense slate tap, heavy and earthy |
104
- | `bamboo` | Hollow tube resonance, odd harmonics, breathy and airy |
105
- | `ember` | Warm crackling ember, fire-like with wide pitch scatter |
106
- | `silk` | Soft breathy whisper, delicate airy texture |
107
- | `shell` | Swirly ocean interference, dense phase beating |
108
- | `moss` | Ultra-soft muffled earth, mossy dampness |
109
-
110
- ## Options
111
-
112
- | Flag | Description |
113
- |------|-------------|
114
- | `--pipe` | Read from stdin instead of wrapping |
115
- | `--character`, `-c` | Sound character |
116
- | `--volume`, `-v` | Volume 0.0-1.0 (default: 0.5) |
117
- | `--attention`, `-a` | Silence alert seconds (default: 30) |
118
- | `--reverse`, `-r` | Reverse mode: sound during silence, quiet during output |
119
- | `--list-characters` | Show presets |
120
-
121
- ## Development
122
-
123
- Test locally without installing:
124
-
125
- ```bash
126
- cd claudible
127
-
128
- # Run wrapping claude (Ctrl+C to stop)
129
- PYTHONPATH=src python3 -m claudible
130
-
131
- # Wrap a different command
132
- PYTHONPATH=src python3 -m claudible "ls -la"
133
-
134
- # Pipe mode
135
- echo "test" | PYTHONPATH=src python3 -m claudible --pipe -c glass
136
-
137
- # List characters
138
- PYTHONPATH=src python3 -m claudible --list-characters
139
- ```
140
-
141
- ## License
142
-
143
- MIT
@@ -1,11 +0,0 @@
1
- claudible/__init__.py,sha256=xSLL4yfc1p79BWVb65PJBTFMxyJ2PWuu7pCLYcCuOVM,96
2
- claudible/__main__.py,sha256=5mFnPjtOjk5Hw-mvUhRYtwXVnXFR7ZbXKC27VIXu_dI,106
3
- claudible/audio.py,sha256=6Do7_g5SUfJkMGSH_c_XGG4YiC2uakhaDTy8xYAp7rE,12043
4
- claudible/cli.py,sha256=j14ZnHT7L_efA67U-3hLSqUsgf2rvlMYgs1b6w3X2pU,4803
5
- claudible/materials.py,sha256=6OsobcWOFvLBCuvzkeWi9C7fYHFnW9RED6nEElK-Ki4,9517
6
- claudible/monitor.py,sha256=iPCaqdO-CDp1CoIie2VtddUUjG1A_xucbFJ1DPk3MKk,4070
7
- claudible-0.1.1.dist-info/METADATA,sha256=yVjyeNYOf6PqhC4FTgvevw0jHd5UDvkO64mmY-6zx4c,4099
8
- claudible-0.1.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
9
- claudible-0.1.1.dist-info/entry_points.txt,sha256=4tBIsIGcdtJ-1N9YZ3MrADyNvPBDcUxVgAnt9IuC7TA,49
10
- claudible-0.1.1.dist-info/top_level.txt,sha256=4ZB16Aa5ZDS12bmxzrQmAJejjsTgQ9Yozwc0Z3qpJp4,10
11
- claudible-0.1.1.dist-info/RECORD,,