shhhhhh 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.
- shhhhhh-0.1.0/.context/notes.md +0 -0
- shhhhhh-0.1.0/.context/plans/homebrew-distribution-for-shh.md +59 -0
- shhhhhh-0.1.0/.context/todos.md +0 -0
- shhhhhh-0.1.0/.git +1 -0
- shhhhhh-0.1.0/.gitignore +8 -0
- shhhhhh-0.1.0/PKG-INFO +15 -0
- shhhhhh-0.1.0/README.md +44 -0
- shhhhhh-0.1.0/docs/dev.md +54 -0
- shhhhhh-0.1.0/docs/plans/2026-03-07-shh-design.md +137 -0
- shhhhhh-0.1.0/docs/plans/2026-03-07-shh-implementation.md +909 -0
- shhhhhh-0.1.0/docs/plans/2026-03-09-permissions-design.md +34 -0
- shhhhhh-0.1.0/docs/plans/2026-03-09-permissions-implementation.md +282 -0
- shhhhhh-0.1.0/pyproject.toml +30 -0
- shhhhhh-0.1.0/src/shhhhhh/__init__.py +3 -0
- shhhhhh-0.1.0/src/shhhhhh/cli.py +145 -0
- shhhhhh-0.1.0/src/shhhhhh/display.py +84 -0
- shhhhhh-0.1.0/src/shhhhhh/permissions.py +69 -0
- shhhhhh-0.1.0/src/shhhhhh/plist.py +117 -0
- shhhhhh-0.1.0/tests/test_cli.py +114 -0
- shhhhhh-0.1.0/tests/test_permissions.py +51 -0
- shhhhhh-0.1.0/tests/test_plist.py +78 -0
- shhhhhh-0.1.0/tests/test_plist_write.py +74 -0
|
File without changes
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Plan: Homebrew Distribution for `shh`
|
|
2
|
+
|
|
3
|
+
## Context
|
|
4
|
+
`shh` is a macOS CLI tool for managing notification settings. It's built in Python with click + rich, packaged with hatchling. Currently installable only via `pip install -e .` locally — not yet on PyPI or Homebrew. The goal is to make it installable via `brew install alexpriest/tap/shh`.
|
|
5
|
+
|
|
6
|
+
## Steps
|
|
7
|
+
|
|
8
|
+
### 1. Prepare `pyproject.toml` for PyPI
|
|
9
|
+
Add missing metadata fields that PyPI requires/expects:
|
|
10
|
+
- `license` (MIT)
|
|
11
|
+
- `authors`
|
|
12
|
+
- `urls` (homepage, repository)
|
|
13
|
+
- `classifiers` (macOS-only, CLI, Python 3.11+)
|
|
14
|
+
|
|
15
|
+
**File:** `/Users/alex/Code/.conductor/workspaces/shhhhhh/medan/pyproject.toml`
|
|
16
|
+
|
|
17
|
+
### 2. Publish to PyPI
|
|
18
|
+
```bash
|
|
19
|
+
cd /Users/alex/Code/.conductor/workspaces/shhhhhh/medan
|
|
20
|
+
python3.11 -m build
|
|
21
|
+
twine upload dist/shhhhhh-0.1.0*
|
|
22
|
+
```
|
|
23
|
+
This produces the sdist `.tar.gz` that Homebrew needs.
|
|
24
|
+
|
|
25
|
+
### 3. Create `alexpriest/homebrew-tap` GitHub repo
|
|
26
|
+
```bash
|
|
27
|
+
gh repo create alexpriest/homebrew-tap --public --description "Homebrew formulae"
|
|
28
|
+
```
|
|
29
|
+
Structure:
|
|
30
|
+
```
|
|
31
|
+
homebrew-tap/
|
|
32
|
+
Formula/
|
|
33
|
+
shh.rb
|
|
34
|
+
README.md
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### 4. Generate the Homebrew formula
|
|
38
|
+
Use `homebrew-pypi-poet` to auto-generate resource stanzas with correct URLs + SHA256 hashes:
|
|
39
|
+
```bash
|
|
40
|
+
python3.11 -m venv /tmp/poet-env
|
|
41
|
+
source /tmp/poet-env/bin/activate
|
|
42
|
+
pip install shhhhhh homebrew-pypi-poet
|
|
43
|
+
poet -f shhhhhh
|
|
44
|
+
```
|
|
45
|
+
Then edit the output to add: `depends_on :macos`, description, homepage, license, and test block.
|
|
46
|
+
|
|
47
|
+
### 5. Push formula and test
|
|
48
|
+
- Commit `Formula/shh.rb` to the tap repo
|
|
49
|
+
- Test: `brew install alexpriest/tap/shh && shh --help`
|
|
50
|
+
|
|
51
|
+
### 6. Update README with Homebrew install instructions
|
|
52
|
+
Add `brew install alexpriest/tap/shh` to the medan README.
|
|
53
|
+
|
|
54
|
+
**File:** `/Users/alex/Code/.conductor/workspaces/shhhhhh/medan/README.md`
|
|
55
|
+
|
|
56
|
+
## Verification
|
|
57
|
+
1. `brew install alexpriest/tap/shh` succeeds on a clean system
|
|
58
|
+
2. `shh --help` outputs usage info
|
|
59
|
+
3. `shh list` works (with Full Disk Access granted)
|
|
File without changes
|
shhhhhh-0.1.0/.git
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
gitdir: /Users/alex/Code/tools/shhhhhh/.git/worktrees/medan
|
shhhhhh-0.1.0/.gitignore
ADDED
shhhhhh-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: shhhhhh
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Silence your Mac, app by app
|
|
5
|
+
Project-URL: Homepage, https://github.com/alexpriest/shhhhhh
|
|
6
|
+
Project-URL: Repository, https://github.com/alexpriest/shhhhhh
|
|
7
|
+
Author: Alex Priest
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Classifier: Environment :: Console
|
|
10
|
+
Classifier: Operating System :: MacOS
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Topic :: Utilities
|
|
13
|
+
Requires-Python: >=3.11
|
|
14
|
+
Requires-Dist: click>=8.0
|
|
15
|
+
Requires-Dist: rich>=13.0
|
shhhhhh-0.1.0/README.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# shh
|
|
2
|
+
|
|
3
|
+
Silence your Mac, app by app.
|
|
4
|
+
|
|
5
|
+
A CLI tool for batch-managing macOS notification settings — toggle sound and badges for all apps or specific ones in a single command.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install shhhhhh
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Setup
|
|
14
|
+
|
|
15
|
+
`shh` needs **Full Disk Access** to manage notification settings.
|
|
16
|
+
|
|
17
|
+
1. Open **System Settings → Privacy & Security → Full Disk Access**
|
|
18
|
+
2. Enable your terminal app (Terminal, iTerm2, Ghostty, etc.)
|
|
19
|
+
3. Restart your terminal
|
|
20
|
+
|
|
21
|
+
If you skip this step, `shh` will guide you through it on first run.
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
shh list # show all apps + settings
|
|
27
|
+
shh sound off --all # mute everything
|
|
28
|
+
shh sound on Mail Messages # unmute specific apps
|
|
29
|
+
shh badges off --all # remove all badge icons
|
|
30
|
+
shh undo # restore previous settings
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## How it works
|
|
34
|
+
|
|
35
|
+
`shh` reads and writes macOS notification preferences directly from the `usernoted` plist, then restarts the daemon to apply changes. A backup is automatically created before every change.
|
|
36
|
+
|
|
37
|
+
## Requirements
|
|
38
|
+
|
|
39
|
+
- macOS 15+ (Sequoia / Tahoe)
|
|
40
|
+
- Python 3.11+
|
|
41
|
+
|
|
42
|
+
## License
|
|
43
|
+
|
|
44
|
+
MIT
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Development
|
|
2
|
+
|
|
3
|
+
## Setup
|
|
4
|
+
|
|
5
|
+
Requires Python 3.11+. The system Python's pip is too old for editable installs, so use a venv:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
cd ~/Code/tools/shhhhhh
|
|
9
|
+
python3.11 -m venv .venv
|
|
10
|
+
source .venv/bin/activate
|
|
11
|
+
pip install -e .
|
|
12
|
+
pip install pytest
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Running
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
shh list # show all apps + settings
|
|
19
|
+
shh sound off --all # mute everything
|
|
20
|
+
shh sound on Mail Messages # unmute specific apps
|
|
21
|
+
shh badges off --all # remove all badge icons
|
|
22
|
+
shh undo # restore previous settings
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Tests
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
python3 -m pytest tests/ -v
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
18 tests across 3 files: plist reader, plist writer, CLI commands.
|
|
32
|
+
|
|
33
|
+
## Architecture
|
|
34
|
+
|
|
35
|
+
Three modules:
|
|
36
|
+
|
|
37
|
+
- `src/shhhhhh/plist.py` — reads/writes the macOS `usernoted` plist, bitmask manipulation, backup/restore
|
|
38
|
+
- `src/shhhhhh/display.py` — gradient logo, rich tables, result messages
|
|
39
|
+
- `src/shhhhhh/cli.py` — click commands wiring plist + display together
|
|
40
|
+
|
|
41
|
+
## How it works
|
|
42
|
+
|
|
43
|
+
The tool reads `~/Library/Group Containers/group.com.apple.usernoted/Library/Preferences/group.com.apple.usernoted.plist`, which stores per-app notification flags as a bitmask:
|
|
44
|
+
|
|
45
|
+
- Bit 1 (value 2): badges
|
|
46
|
+
- Bit 2 (value 4): sound
|
|
47
|
+
|
|
48
|
+
After modifying flags, it restarts `usernoted` via `killall usernoted` to apply changes. A timestamped backup is saved to `~/.shh/` before every write.
|
|
49
|
+
|
|
50
|
+
## Known gaps (v2)
|
|
51
|
+
|
|
52
|
+
- Ambiguous match warning: if you type `shh sound off Ma` and it matches both Mail and Maps, it silently applies to both. Design doc says it should show matches and ask the user to be specific.
|
|
53
|
+
- No `--version` flag
|
|
54
|
+
- No badges-specific CLI tests (shares code path with sound via factory pattern, low risk)
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# shh (shhhhhh.sh) — Design Document
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
A Python CLI tool for batch-managing macOS notification settings. Reads and writes the `usernoted` plist directly, letting you toggle sound and badges for all apps or specific ones in a single command.
|
|
6
|
+
|
|
7
|
+
- **PyPI package**: `shhhhhh`
|
|
8
|
+
- **CLI command**: `shh`
|
|
9
|
+
- **Domain**: `shhhhhh.sh`
|
|
10
|
+
|
|
11
|
+
## Commands
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
shh list # show all apps + sound/badge status
|
|
15
|
+
shh sound off --all # mute all apps
|
|
16
|
+
shh sound on --all # unmute all apps
|
|
17
|
+
shh sound off Slack Zoom # mute specific apps (fuzzy match)
|
|
18
|
+
shh sound on Mail Messages # unmute specific apps
|
|
19
|
+
shh badges off --all # remove all badge icons
|
|
20
|
+
shh badges on Mail Messages # re-enable badges for specific apps
|
|
21
|
+
shh undo # restore most recent backup
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Branding
|
|
25
|
+
|
|
26
|
+
### Logo
|
|
27
|
+
Box-drawing chunky "SHH" wordmark with top-to-bottom grayscale gradient (bright to dim, like sound fading out). Uses 256-color ANSI codes, one shade per line.
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
███████╗██╗ ██╗██╗ ██╗
|
|
31
|
+
██╔════╝██║ ██║██║ ██║
|
|
32
|
+
███████╗███████║███████║
|
|
33
|
+
╚════██║██╔══██║██╔══██║
|
|
34
|
+
███████║██║ ██║██║ ██║
|
|
35
|
+
╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Tagline
|
|
39
|
+
Dim text below logo: `silence your mac, app by app`
|
|
40
|
+
|
|
41
|
+
### Colors
|
|
42
|
+
Grayscale palette throughout. Green checkmarks, dim crosses for status.
|
|
43
|
+
|
|
44
|
+
## Data Layer
|
|
45
|
+
|
|
46
|
+
### Source file
|
|
47
|
+
```
|
|
48
|
+
~/Library/Group Containers/group.com.apple.usernoted/Library/Preferences/group.com.apple.usernoted.plist
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Flags bitmask
|
|
52
|
+
- Bit 1 (value 2): Badge app icon
|
|
53
|
+
- Bit 2 (value 4): Play sound for notifications
|
|
54
|
+
|
|
55
|
+
### App name resolution
|
|
56
|
+
1. Extract from `path` field (e.g. `/Applications/Slack.app` -> `Slack`)
|
|
57
|
+
2. Fall back to `bundle-id` if no path
|
|
58
|
+
|
|
59
|
+
### Fuzzy matching
|
|
60
|
+
Case-insensitive substring match against friendly app names. If ambiguous, show matches and ask user to be more specific.
|
|
61
|
+
|
|
62
|
+
### System apps
|
|
63
|
+
Skip entries where `bundle-id` starts with `_SYSTEM_CENTER_:`.
|
|
64
|
+
|
|
65
|
+
### Write flow
|
|
66
|
+
1. Read current plist
|
|
67
|
+
2. Modify flags bitmask for targeted apps
|
|
68
|
+
3. Write plist back
|
|
69
|
+
4. Restart `usernoted` daemon via `killall usernoted`
|
|
70
|
+
|
|
71
|
+
## Output Examples
|
|
72
|
+
|
|
73
|
+
### `shh list`
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
[gradient logo]
|
|
77
|
+
|
|
78
|
+
silence your mac, app by app
|
|
79
|
+
|
|
80
|
+
47 apps · 43 with sound · 45 with badges
|
|
81
|
+
|
|
82
|
+
App Sound Badges
|
|
83
|
+
────────────────────────────────────────
|
|
84
|
+
Calendar ✓ ✓
|
|
85
|
+
Chrome ✓ ✓
|
|
86
|
+
FaceTime ✓ ✓
|
|
87
|
+
Linear ✓ ✓
|
|
88
|
+
Mail ✓ ✓
|
|
89
|
+
Messages ✗ ✗
|
|
90
|
+
Slack ✗ ✓
|
|
91
|
+
...
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### `shh sound off --all`
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
[gradient logo]
|
|
98
|
+
|
|
99
|
+
Muted 43 apps
|
|
100
|
+
|
|
101
|
+
To undo: shh sound on --all
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Safety
|
|
105
|
+
|
|
106
|
+
- Backup plist to `~/.shh/backup-<timestamp>.plist` before first write
|
|
107
|
+
- `shh undo` restores most recent backup
|
|
108
|
+
- Confirm before `--all` operations (skip with `--yes` / `-y`)
|
|
109
|
+
|
|
110
|
+
## Tech Stack
|
|
111
|
+
|
|
112
|
+
- Python 3.11+
|
|
113
|
+
- `rich` — colored output, tables, status indicators
|
|
114
|
+
- `plistlib` (stdlib) — read/write plist
|
|
115
|
+
- `click` — CLI argument parsing
|
|
116
|
+
- `subprocess` — restart usernoted after changes
|
|
117
|
+
|
|
118
|
+
## Project Structure
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
~/Code/tools/shhhhhh/
|
|
122
|
+
├── README.md
|
|
123
|
+
├── pyproject.toml
|
|
124
|
+
├── src/
|
|
125
|
+
│ └── shhhhhh/
|
|
126
|
+
│ ├── __init__.py
|
|
127
|
+
│ ├── cli.py # click commands + banner
|
|
128
|
+
│ ├── plist.py # read/write usernoted plist
|
|
129
|
+
│ └── display.py # rich table + formatting
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Future (not v1)
|
|
133
|
+
|
|
134
|
+
- SwiftUI wrapper app (Option C from brainstorming)
|
|
135
|
+
- Additional settings: banners/alert type, lock screen, previews
|
|
136
|
+
- `shh profile save/load` — save/restore named configurations
|
|
137
|
+
- Homebrew formula
|