pingthings 0.9.0 → 1.0.0

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/LICENSE CHANGED
@@ -27,4 +27,42 @@ warzone2100-command: Warzone 2100
27
27
  0ad-civilizations: 0 A.D.
28
28
  Copyright Wildfire Games — CC-BY-SA 3.0
29
29
  Distributed as part of this aggregate work alongside GPL v2 code.
30
- See packs/0ad-civilizations/manifest.json for full attribution.
30
+
31
+ xonotic-announcer: Xonotic
32
+ Copyright Xonotic Project — GPL v2+
33
+
34
+ fighting-announcer: Fighting Game Announcer
35
+ Copyright Alba MacKenna — CC-BY 4.0
36
+
37
+ retro-8bit: 8-bit Sound Effects
38
+ Copyright Juhani Junkala / SubspaceAudio — CC0
39
+
40
+ retro-weapons: 8-bit Weapons (from same collection)
41
+ Copyright Juhani Junkala / SubspaceAudio — CC0
42
+
43
+ retro-movement: 8-bit Movement (from same collection)
44
+ Copyright Juhani Junkala / SubspaceAudio — CC0
45
+
46
+ kenney-interface: Interface Sounds
47
+ Copyright Kenney (kenney.nl) — CC0
48
+
49
+ kenney-scifi: Sci-fi Sounds
50
+ Copyright Kenney (kenney.nl) — CC0
51
+
52
+ kenney-voiceover: Voiceover Pack
53
+ Copyright Kenney (kenney.nl) — CC0
54
+
55
+ kenney-fighter: Voiceover Pack: Fighter
56
+ Copyright Kenney (kenney.nl) — CC0
57
+
58
+ kenney-digital: Digital Audio
59
+ Copyright Kenney (kenney.nl) — CC0
60
+
61
+ kenney-rpg: RPG Audio
62
+ Copyright Kenney (kenney.nl) — CC0
63
+
64
+ kenney-impacts: Impact Sounds
65
+ Copyright Kenney (kenney.nl) — CC0
66
+
67
+ droid-announcer: DROID Video Game Announcer
68
+ Copyright VoiceBosch — CC-BY-SA 4.0
package/README.md CHANGED
@@ -140,7 +140,12 @@ For different sounds based on what Claude is doing, set up multiple hooks:
140
140
  | `pingthings random-pack` | Switch to a random pack |
141
141
  | `pingthings install <source>` | Install a pack from GitHub or local path |
142
142
  | `pingthings uninstall <pack>` | Remove a user-installed pack |
143
+ | `pingthings demo` | Play one sound from every pack — showroom tour |
144
+ | `pingthings stats` | Show usage statistics |
145
+ | `pingthings setup <ide>` | Configure hooks for any IDE (cursor, copilot, codex, etc.) |
143
146
  | `pingthings doctor` | Diagnose audio setup and configuration |
147
+ | `pingthings update` | Check for new versions on npm |
148
+ | `pingthings cesp [pack\|--all]` | Generate CESP-compatible manifests |
144
149
  | `pingthings completions <shell>` | Generate shell completions (bash/zsh/fish) |
145
150
 
146
151
  ## Configuration
@@ -153,7 +158,10 @@ Config lives at `~/.config/pingthings/config.json`:
153
158
  "mode": "random",
154
159
  "specificSound": null,
155
160
  "volume": 100,
156
- "eventPacks": {}
161
+ "eventPacks": {},
162
+ "cooldown": true,
163
+ "quietHours": null,
164
+ "notifications": false
157
165
  }
158
166
  ```
159
167
 
@@ -162,6 +170,9 @@ Config lives at `~/.config/pingthings/config.json`:
162
170
  - **specificSound** — sound name to always play when mode is `"specific"`
163
171
  - **volume** — playback volume, 0-100 (default: 100)
164
172
  - **eventPacks** — per-event pack overrides (e.g. `{"error": "freedoom-arsenal"}`)
173
+ - **cooldown** — avoid repeating the same sound twice in a row (default: true)
174
+ - **quietHours** — mute during hours, e.g. `"22-7"` for 10pm-7am (default: null)
175
+ - **notifications** — show desktop notifications alongside sound (default: false)
165
176
 
166
177
  Set values via CLI:
167
178
 
@@ -240,6 +251,30 @@ Arena FPS announcer voice lines from **Xonotic** — 15 sounds including "awesom
240
251
  ### fighting-announcer
241
252
  Fighting game announcer voice lines — 20 sounds including "Fight!", "Victory!", "K.O!", "Game Over!", "Ready?", "You Win!". License: CC-BY 4.0.
242
253
 
254
+ ### kenney-voiceover
255
+ Human voice notifications by **Kenney** — 19 sounds including "mission completed", "objective achieved", "game over", "congratulations". License: CC0.
256
+
257
+ ### droid-announcer
258
+ Robotic AI voice lines — 15 sounds including "objective complete", "action required", "instruction unclear", "mission complete". Perfect for AI coding tools. License: CC-BY-SA 4.0.
259
+
260
+ ### kenney-digital
261
+ Digital and space notification tones by **Kenney** — 18 sounds including power-ups, phasers, zaps, and bleeps. License: CC0.
262
+
263
+ ### kenney-rpg
264
+ Fantasy RPG foley sounds by **Kenney** — 18 sounds including metal latches, book flips, sword draws, door creaks. License: CC0.
265
+
266
+ ### kenney-impacts
267
+ Material impact sounds by **Kenney** — 18 sounds including metal plates, wood, glass, bells, and mining. License: CC0.
268
+
269
+ ### kenney-fighter
270
+ Female fighting game announcer by **Kenney** — 18 sounds including "flawless victory!", "combo breaker!", "prepare yourself!". License: CC0.
271
+
272
+ ### retro-weapons
273
+ 8-bit weapons, explosions, and death screams — 18 sounds from the SubspaceAudio collection. License: CC0.
274
+
275
+ ### retro-movement
276
+ 8-bit portals, doors, jumps, and bleeps — 18 sounds from the SubspaceAudio collection. License: CC0.
277
+
243
278
  ## Custom packs
244
279
 
245
280
  Place packs in `~/.config/pingthings/packs/<pack-name>/`:
@@ -303,6 +338,10 @@ pingthings theme reset # back to defaults
303
338
  | `professional` | Clean and minimal — Kenney UI sounds for everything |
304
339
  | `8bit` | Pure retro — 8-bit chiptune for everything |
305
340
  | `space` | Space station — Warzone 2100 + Kenney sci-fi |
341
+ | `developer` | AI assistant vibes — droid announcer + human voiceover |
342
+ | `arcade` | Full 8-bit arcade experience |
343
+ | `tabletop` | Tavern sounds — RPG foley + material impacts |
344
+ | `tournament` | Fighting game tournament — multiple announcers |
306
345
  | `chaos` | Different pack for every event — maximum variety |
307
346
 
308
347
  ## Tools
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pingthings",
3
- "version": "0.9.0",
3
+ "version": "1.0.0",
4
4
  "description": "Notification sounds for Claude Code and other CLI tools",
5
5
  "type": "module",
6
6
  "license": "GPL-2.0",
@@ -17,12 +17,14 @@ Install:
17
17
  const COMMANDS = [
18
18
  'play', 'list', 'select', 'browse', 'search', 'sounds',
19
19
  'use', 'preview', 'test-events', 'theme', 'config',
20
- 'init', 'create', 'install', 'uninstall', 'random-pack', 'doctor', 'completions',
20
+ 'init', 'setup', 'demo', 'stats', 'create', 'install', 'uninstall',
21
+ 'random-pack', 'doctor', 'update', 'cesp', 'completions',
21
22
  ];
22
23
 
23
24
  const THEMES = [
24
25
  'retro', 'sci-fi', 'arena', 'fantasy', 'ancient',
25
- 'professional', '8bit', 'space', 'chaos', 'reset',
26
+ 'professional', '8bit', 'space', 'developer', 'arcade',
27
+ 'tabletop', 'tournament', 'chaos', 'reset',
26
28
  ];
27
29
 
28
30
  const EVENTS = ['done', 'permission', 'complete', 'error', 'blocked'];
@@ -92,6 +94,11 @@ _pingthings() {
92
94
  'uninstall:Remove a user-installed pack'
93
95
  'random-pack:Switch to a random pack'
94
96
  'doctor:Diagnose audio setup'
97
+ 'setup:Configure hooks for any IDE'
98
+ 'demo:Play one sound from every pack'
99
+ 'stats:Show usage statistics'
100
+ 'update:Check for new versions'
101
+ 'cesp:Generate CESP manifests'
95
102
  'completions:Generate shell completions'
96
103
  )
97
104
  themes=(${THEMES.join(' ')})
package/src/cli/doctor.js CHANGED
@@ -33,7 +33,8 @@ function check(label, fn) {
33
33
 
34
34
  function commandExists(cmd) {
35
35
  try {
36
- execFileSync('which', [cmd], { stdio: 'pipe' });
36
+ const checker = platform() === 'win32' ? 'where' : 'which';
37
+ execFileSync(checker, [cmd], { stdio: 'pipe' });
37
38
  return true;
38
39
  } catch {
39
40
  return false;
package/src/cli/stats.js CHANGED
@@ -1,4 +1,4 @@
1
- import { existsSync, readFileSync, writeFileSync } from 'node:fs';
1
+ import { existsSync, readFileSync, writeFileSync, renameSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { readConfig, getConfigDir } from '../config.js';
4
4
  import { listPacks } from '../packs.js';
@@ -40,7 +40,9 @@ export function recordPlay(packName, event) {
40
40
  stats.dailyPlays[today] = (stats.dailyPlays[today] || 0) + 1;
41
41
 
42
42
  try {
43
- writeFileSync(statsPath, JSON.stringify(stats, null, 2) + '\n', 'utf8');
43
+ const tmpPath = statsPath + '.tmp';
44
+ writeFileSync(tmpPath, JSON.stringify(stats, null, 2) + '\n', 'utf8');
45
+ renameSync(tmpPath, statsPath);
44
46
  } catch {}
45
47
  }
46
48
 
package/src/notify.js CHANGED
@@ -1,19 +1,24 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import { platform } from 'node:os';
3
3
 
4
+ function sanitize(str) {
5
+ return str.replace(/[\\"]/g, '').replace(/[^\x20-\x7E]/g, '');
6
+ }
7
+
4
8
  export function sendNotification(title, message) {
5
9
  const os = platform();
10
+ const safeTitle = sanitize(title);
11
+ const safeMessage = sanitize(message);
6
12
 
7
13
  if (os === 'darwin') {
8
14
  spawn('osascript', [
9
15
  '-e',
10
- `display notification "${message}" with title "${title}"`,
11
- ], { detached: true, stdio: 'ignore' }).unref();
16
+ `display notification "${safeMessage}" with title "${safeTitle}"`,
17
+ ], { detached: true, stdio: 'ignore' }).on('error', () => {}).unref();
12
18
  } else if (os === 'linux') {
13
- spawn('notify-send', [title, message], {
19
+ spawn('notify-send', [safeTitle, safeMessage], {
14
20
  detached: true,
15
21
  stdio: 'ignore',
16
22
  }).on('error', () => {}).unref();
17
23
  }
18
- // Windows: no native CLI notification without dependencies, skip for now
19
24
  }
package/src/player.js CHANGED
@@ -4,7 +4,8 @@ import { platform } from 'node:os';
4
4
 
5
5
  function commandExists(cmd) {
6
6
  try {
7
- execFileSync('which', [cmd], { stdio: 'pipe' });
7
+ const checker = platform() === 'win32' ? 'where' : 'which';
8
+ execFileSync(checker, [cmd], { stdio: 'pipe' });
8
9
  return true;
9
10
  } catch {
10
11
  return false;