pingthings 0.2.0 → 0.2.1

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
@@ -6,6 +6,25 @@
6
6
  Everyone is permitted to copy and distribute verbatim copies
7
7
  of this license document, but changing it is not allowed.
8
8
 
9
- Audio assets are from Seven Kingdoms: Ancient Adversaries
10
- Copyright 1997,1998 Enlight Software Ltd.
11
- Licensed under GPL v2.
9
+ This project is licensed under GPL v2. Audio assets are sourced from
10
+ multiple open source projects under compatible licenses:
11
+
12
+ 7kaa-soldiers: Seven Kingdoms: Ancient Adversaries
13
+ Copyright 1997,1998 Enlight Software Ltd. — GPL v2
14
+
15
+ wesnoth-combat: Battle for Wesnoth
16
+ Copyright Wesnoth contributors — GPL v2+
17
+
18
+ openarena-announcer: OpenArena
19
+ Copyright OpenArena contributors — GPL v2
20
+
21
+ freedoom-arsenal: Freedoom
22
+ Copyright Freedoom contributors — BSD-3-Clause
23
+
24
+ warzone2100-command: Warzone 2100
25
+ Copyright Warzone 2100 Project — GPL v2
26
+
27
+ 0ad-civilizations: 0 A.D.
28
+ Copyright Wildfire Games — CC-BY-SA 3.0
29
+ Distributed as part of this aggregate work alongside GPL v2 code.
30
+ See packs/0ad-civilizations/manifest.json for full attribution.
package/README.md CHANGED
@@ -126,10 +126,15 @@ For different sounds based on what Claude is doing, set up multiple hooks:
126
126
  |---------|-------------|
127
127
  | `pingthings play [sound] [--event type]` | Play a sound (random, specific, or event-based) |
128
128
  | `pingthings list` | Show available sound packs |
129
+ | `pingthings select` | Interactive pack selector |
129
130
  | `pingthings use <pack>` | Set the active sound pack |
130
131
  | `pingthings preview <pack>` | Preview a random sound from a pack |
132
+ | `pingthings test-events [pack]` | Play all event sounds to hear each one |
133
+ | `pingthings theme [name]` | Apply a sound theme |
131
134
  | `pingthings config [key] [val]` | Show or update configuration |
132
- | `pingthings install <pack>` | Install a sound pack (coming soon) |
135
+ | `pingthings init` | Set up Claude Code hooks automatically |
136
+ | `pingthings create <dir>` | Create a pack from audio files |
137
+ | `pingthings install <source>` | Install a pack from GitHub or local path |
133
138
 
134
139
  ## Configuration
135
140
 
@@ -139,13 +144,17 @@ Config lives at `~/.config/pingthings/config.json`:
139
144
  {
140
145
  "activePack": "7kaa-soldiers",
141
146
  "mode": "random",
142
- "specificSound": null
147
+ "specificSound": null,
148
+ "volume": 100,
149
+ "eventPacks": {}
143
150
  }
144
151
  ```
145
152
 
146
153
  - **activePack** — which sound pack to use
147
154
  - **mode** — `"random"` (default), `"specific"`, or `"informational"`
148
155
  - **specificSound** — sound name to always play when mode is `"specific"`
156
+ - **volume** — playback volume, 0-100 (default: 100)
157
+ - **eventPacks** — per-event pack overrides (e.g. `{"error": "freedoom-arsenal"}`)
149
158
 
150
159
  Set values via CLI:
151
160
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pingthings",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Notification sounds for Claude Code and other CLI tools",
5
5
  "type": "module",
6
6
  "license": "GPL-2.0",
package/src/cli/init.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createInterface } from 'node:readline';
2
- import { existsSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { existsSync, readFileSync, writeFileSync, renameSync } from 'node:fs';
3
3
  import { join } from 'node:path';
4
4
  import { homedir } from 'node:os';
5
5
 
@@ -59,7 +59,9 @@ function readSettings() {
59
59
 
60
60
  function writeSettings(settings) {
61
61
  const path = getSettingsPath();
62
- writeFileSync(path, JSON.stringify(settings, null, 2) + '\n', 'utf8');
62
+ const tmpPath = path + '.tmp';
63
+ writeFileSync(tmpPath, JSON.stringify(settings, null, 2) + '\n', 'utf8');
64
+ renameSync(tmpPath, path);
63
65
  }
64
66
 
65
67
  function applyHooks(mode) {
@@ -1,4 +1,4 @@
1
- import { existsSync, mkdirSync, writeFileSync, readdirSync } from 'node:fs';
1
+ import { existsSync, mkdirSync, cpSync } from 'node:fs';
2
2
  import { join, basename } from 'node:path';
3
3
  import { execFileSync } from 'node:child_process';
4
4
  import { getConfigDir } from '../config.js';
@@ -75,6 +75,10 @@ function installFromLocal(source) {
75
75
  }
76
76
 
77
77
  const packName = basename(source);
78
+ if (!packName) {
79
+ console.error('Could not determine pack name from path.');
80
+ process.exit(1);
81
+ }
78
82
  const packsDir = join(getConfigDir(), 'packs');
79
83
  const destDir = join(packsDir, packName);
80
84
 
@@ -87,7 +91,7 @@ function installFromLocal(source) {
87
91
  mkdirSync(packsDir, { recursive: true });
88
92
 
89
93
  try {
90
- execFileSync('cp', ['-r', source, destDir], { stdio: 'inherit' });
94
+ cpSync(source, destDir, { recursive: true });
91
95
  } catch {
92
96
  console.error('Failed to copy pack.');
93
97
  process.exit(1);
package/src/cli/play.js CHANGED
@@ -128,5 +128,10 @@ export default function play(args) {
128
128
  soundFile = pickRandom(sounds);
129
129
  }
130
130
 
131
+ if (!soundFile) {
132
+ console.error(`No sounds available in pack: ${packName}`);
133
+ process.exit(1);
134
+ }
135
+
131
136
  playSound(soundFile, config.volume);
132
137
  }
package/src/cli/select.js CHANGED
@@ -2,6 +2,7 @@ import { createInterface } from 'node:readline';
2
2
  import { readConfig, writeConfig } from '../config.js';
3
3
  import { listPacks, getPackSounds } from '../packs.js';
4
4
  import { playSound } from '../player.js';
5
+ import { basename } from 'node:path';
5
6
 
6
7
  function showHelp() {
7
8
  console.log(`
@@ -68,7 +69,7 @@ export default async function select(args) {
68
69
  const sounds = getPackSounds(chosen.name);
69
70
  if (sounds.length > 0) {
70
71
  const sample = sounds[Math.floor(Math.random() * sounds.length)];
71
- playSound(sample);
72
+ playSound(sample, config.volume);
72
73
  }
73
74
 
74
75
  console.log(`\nActive pack set to: ${chosen.name}`);
@@ -1,6 +1,6 @@
1
1
  import { readConfig, VALID_EVENTS } from '../config.js';
2
2
  import { getEventSounds, getPackSounds, pickRandom, resolvePack } from '../packs.js';
3
- import { playSound } from '../player.js';
3
+ import { playSoundSync } from '../player.js';
4
4
  import { basename } from 'node:path';
5
5
 
6
6
  function showHelp() {
@@ -17,10 +17,6 @@ Events played: done, permission, complete, error, blocked
17
17
  `);
18
18
  }
19
19
 
20
- function sleep(ms) {
21
- return new Promise(resolve => setTimeout(resolve, ms));
22
- }
23
-
24
20
  export default async function testEvents(args) {
25
21
  if (args.includes('--help') || args.includes('-h')) {
26
22
  showHelp();
@@ -48,10 +44,7 @@ export default async function testEvents(args) {
48
44
 
49
45
  const sound = pickRandom(sounds);
50
46
  console.log(` ${event.padEnd(12)} ${basename(sound)}`);
51
- playSound(sound);
52
-
53
- // Wait between sounds so they don't overlap
54
- await sleep(1500);
47
+ playSoundSync(sound, config.volume);
55
48
  }
56
49
 
57
50
  console.log('\nDone.\n');