headroom-pc 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.
Files changed (73) hide show
  1. headroom_pc-0.1.0/LICENSE +21 -0
  2. headroom_pc-0.1.0/PKG-INFO +193 -0
  3. headroom_pc-0.1.0/README.md +159 -0
  4. headroom_pc-0.1.0/pyproject.toml +62 -0
  5. headroom_pc-0.1.0/setup.cfg +4 -0
  6. headroom_pc-0.1.0/src/headroom/__init__.py +3 -0
  7. headroom_pc-0.1.0/src/headroom/__main__.py +8 -0
  8. headroom_pc-0.1.0/src/headroom/alerts.py +125 -0
  9. headroom_pc-0.1.0/src/headroom/analyze.py +555 -0
  10. headroom_pc-0.1.0/src/headroom/autostart.py +312 -0
  11. headroom_pc-0.1.0/src/headroom/cleanup.py +773 -0
  12. headroom_pc-0.1.0/src/headroom/cli.py +1250 -0
  13. headroom_pc-0.1.0/src/headroom/config.py +512 -0
  14. headroom_pc-0.1.0/src/headroom/dashboard.py +628 -0
  15. headroom_pc-0.1.0/src/headroom/downloads.py +227 -0
  16. headroom_pc-0.1.0/src/headroom/dupes.py +196 -0
  17. headroom_pc-0.1.0/src/headroom/explain.py +196 -0
  18. headroom_pc-0.1.0/src/headroom/firstrun.py +98 -0
  19. headroom_pc-0.1.0/src/headroom/formatutil.py +31 -0
  20. headroom_pc-0.1.0/src/headroom/history.py +605 -0
  21. headroom_pc-0.1.0/src/headroom/instance.py +115 -0
  22. headroom_pc-0.1.0/src/headroom/logsetup.py +47 -0
  23. headroom_pc-0.1.0/src/headroom/mcp_prompts.py +23 -0
  24. headroom_pc-0.1.0/src/headroom/mcp_server.py +38 -0
  25. headroom_pc-0.1.0/src/headroom/mcp_tools.py +175 -0
  26. headroom_pc-0.1.0/src/headroom/notify.py +129 -0
  27. headroom_pc-0.1.0/src/headroom/pet.py +1334 -0
  28. headroom_pc-0.1.0/src/headroom/pets/__init__.py +1 -0
  29. headroom_pc-0.1.0/src/headroom/pets/draw.py +544 -0
  30. headroom_pc-0.1.0/src/headroom/pets/midnight.png +0 -0
  31. headroom_pc-0.1.0/src/headroom/pets/roomy.png +0 -0
  32. headroom_pc-0.1.0/src/headroom/pets/tower.png +0 -0
  33. headroom_pc-0.1.0/src/headroom/plans.py +108 -0
  34. headroom_pc-0.1.0/src/headroom/plugins.py +152 -0
  35. headroom_pc-0.1.0/src/headroom/probe.py +197 -0
  36. headroom_pc-0.1.0/src/headroom/processes.py +77 -0
  37. headroom_pc-0.1.0/src/headroom/profiles/__init__.py +1 -0
  38. headroom_pc-0.1.0/src/headroom/profiles/developer.rules +64 -0
  39. headroom_pc-0.1.0/src/headroom/profiles/gamer.rules +36 -0
  40. headroom_pc-0.1.0/src/headroom/profiles/student.rules +34 -0
  41. headroom_pc-0.1.0/src/headroom/py.typed +0 -0
  42. headroom_pc-0.1.0/src/headroom/render.py +457 -0
  43. headroom_pc-0.1.0/src/headroom/review.py +353 -0
  44. headroom_pc-0.1.0/src/headroom/rules.py +424 -0
  45. headroom_pc-0.1.0/src/headroom/safety.py +211 -0
  46. headroom_pc-0.1.0/src/headroom/scan.py +82 -0
  47. headroom_pc-0.1.0/src/headroom/schedule.py +104 -0
  48. headroom_pc-0.1.0/src/headroom/selfcheck.py +78 -0
  49. headroom_pc-0.1.0/src/headroom/service.py +120 -0
  50. headroom_pc-0.1.0/src/headroom/startup.py +525 -0
  51. headroom_pc-0.1.0/src/headroom/tracker.py +317 -0
  52. headroom_pc-0.1.0/src/headroom/walk.py +230 -0
  53. headroom_pc-0.1.0/src/headroom/watch.py +324 -0
  54. headroom_pc-0.1.0/src/headroom/weekly.py +154 -0
  55. headroom_pc-0.1.0/src/headroom_pc.egg-info/PKG-INFO +193 -0
  56. headroom_pc-0.1.0/src/headroom_pc.egg-info/SOURCES.txt +71 -0
  57. headroom_pc-0.1.0/src/headroom_pc.egg-info/dependency_links.txt +1 -0
  58. headroom_pc-0.1.0/src/headroom_pc.egg-info/entry_points.txt +3 -0
  59. headroom_pc-0.1.0/src/headroom_pc.egg-info/requires.txt +13 -0
  60. headroom_pc-0.1.0/src/headroom_pc.egg-info/top_level.txt +1 -0
  61. headroom_pc-0.1.0/tests/test_agent.py +601 -0
  62. headroom_pc-0.1.0/tests/test_cleanup.py +312 -0
  63. headroom_pc-0.1.0/tests/test_cli.py +205 -0
  64. headroom_pc-0.1.0/tests/test_config.py +156 -0
  65. headroom_pc-0.1.0/tests/test_formatutil.py +21 -0
  66. headroom_pc-0.1.0/tests/test_history_commit.py +266 -0
  67. headroom_pc-0.1.0/tests/test_monitor.py +308 -0
  68. headroom_pc-0.1.0/tests/test_pet.py +387 -0
  69. headroom_pc-0.1.0/tests/test_profiles.py +887 -0
  70. headroom_pc-0.1.0/tests/test_project.py +110 -0
  71. headroom_pc-0.1.0/tests/test_safety.py +139 -0
  72. headroom_pc-0.1.0/tests/test_v4.py +880 -0
  73. headroom_pc-0.1.0/tests/test_walk_scan.py +167 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jasdish Singh
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,193 @@
1
+ Metadata-Version: 2.4
2
+ Name: headroom-pc
3
+ Version: 0.1.0
4
+ Summary: Always-on RAM and disk watcher for a Windows laptop, with history, file review, and allowlisted cleanup.
5
+ Author: Jasdish Singh
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/Jasdish-Singh/headroom
8
+ Project-URL: Repository, https://github.com/Jasdish-Singh/headroom
9
+ Project-URL: Issues, https://github.com/Jasdish-Singh/headroom/issues
10
+ Keywords: windows,memory,disk,cleanup,monitor
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Win32 (MS Windows)
13
+ Classifier: Intended Audience :: End Users/Desktop
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: Microsoft :: Windows :: Windows 11
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: System :: Monitoring
19
+ Classifier: Topic :: Utilities
20
+ Requires-Python: >=3.11
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: psutil>=5.9.0
24
+ Requires-Dist: PyYAML>=6.0.1
25
+ Provides-Extra: tray
26
+ Requires-Dist: pystray>=0.19.5; extra == "tray"
27
+ Requires-Dist: Pillow>=10.0.0; extra == "tray"
28
+ Provides-Extra: mcp
29
+ Requires-Dist: mcp<3,>=2.0; extra == "mcp"
30
+ Provides-Extra: dev
31
+ Requires-Dist: pytest>=8.0; extra == "dev"
32
+ Requires-Dist: mcp<3,>=2.0; extra == "dev"
33
+ Dynamic: license-file
34
+
35
+ <!-- mcp-name: io.github.Jasdish-Singh/headroom -->
36
+
37
+ # Headroom
38
+
39
+ [![CI](https://github.com/Jasdish-Singh/headroom/actions/workflows/test.yml/badge.svg)](https://github.com/Jasdish-Singh/headroom/actions/workflows/test.yml)
40
+ [![License: MIT](https://img.shields.io/github/license/Jasdish-Singh/headroom)](https://github.com/Jasdish-Singh/headroom/blob/main/LICENSE)
41
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue)](https://github.com/Jasdish-Singh/headroom/blob/main/pyproject.toml)
42
+
43
+ **A quiet watcher for the RAM and disk on your Windows PC.**
44
+
45
+ Headroom sits in the tray, keeps a small history, and shows what is full. Cleanup is limited to an allowlist. Type YES to delete. Type MOVE to send a reviewed file to the Recycle Bin or to an archive folder.
46
+
47
+ [Jasdish Singh](https://github.com/Jasdish-Singh). [MIT](LICENSE), 2026.
48
+
49
+ <p>
50
+ <img alt="Roomy on a Windows desktop" src="https://raw.githubusercontent.com/Jasdish-Singh/headroom/main/docs/images/roomy-desktop.png" width="720">
51
+ </p>
52
+
53
+ ## What it does
54
+
55
+ - Watches RAM, commit charge, page file, and free space.
56
+ - Records history and a short report (disk trend, likely leaks, folders that grew).
57
+ - Cleans only `user_temp`, `windows_temp`, `chrome_cache`, `edge_cache`, `firefox_cache`, `thumbnail_cache`, and `recycle_bin`. Config cannot add a path.
58
+ - Reviews files, duplicate copies, old downloads, and startup programs.
59
+ - Serves a page on `127.0.0.1` and a read-only desktop pet, Roomy.
60
+ - Exposes a local MCP server. `clean`, `move`, and `startup_disable` return a plan. They do not change the PC.
61
+
62
+ ## Install
63
+
64
+ The command is `headroom`. Python 3.11 or newer.
65
+
66
+ **Exe.** `headroom.exe` and `Headroom-Setup` from [GitHub Releases](https://github.com/Jasdish-Singh/headroom/releases) once a release is published. Setup does not clean anything and does not register a logon task.
67
+
68
+ **winget,** after the manifest is published:
69
+
70
+ ```powershell
71
+ winget install JasdishSingh.Headroom
72
+ ```
73
+
74
+ The URL and SHA256 in `packaging/winget` are still placeholders.
75
+
76
+ **pip.** The PyPI name is `headroom-pc`.
77
+
78
+ ```powershell
79
+ py -3.12 -m pip install "headroom-pc[tray,mcp]"
80
+ headroom status
81
+ ```
82
+
83
+ `tray` is the icon. `mcp` is the assistant server.
84
+
85
+ ## Quick start
86
+
87
+ ```powershell
88
+ headroom status
89
+ headroom top
90
+ headroom clean --dry-run
91
+ headroom watch
92
+ headroom dashboard
93
+ ```
94
+
95
+ `headroom watch` takes a sample immediately, then every 30 seconds (slower on battery, faster under pressure). `headroom install` starts it at sign-in. A second watch, dashboard, or pet does not start another copy.
96
+
97
+ `headroom dashboard` prints and opens `http://127.0.0.1:<port>/?token=...`. Before the first stored sample, the page shows the live reading.
98
+
99
+ Commands, defaults, and exit codes: [docs/commands.md](docs/commands.md).
100
+
101
+ ## Roomy
102
+
103
+ Roomy is a small beige monitor. The cord is the tail. It walks a little and changes face with the PC. It cannot clean, move, or delete.
104
+
105
+ <p>
106
+ <img alt="Roomy close up" src="https://raw.githubusercontent.com/Jasdish-Singh/headroom/main/docs/images/roomy-zoom.png" width="360">
107
+ </p>
108
+
109
+ ```powershell
110
+ headroom pet
111
+ headroom widget
112
+ headroom pet --skin midnight
113
+ headroom pet --skin tower --lite
114
+ ```
115
+
116
+ `headroom widget` is the same pet. Click opens the dashboard. Drag remembers the spot. Right-click: dashboard, report, mute, hide, quit.
117
+
118
+ Moods, skins, and the frame budget: [docs/roomy.md](docs/roomy.md).
119
+
120
+ ## MCP
121
+
122
+ `headroom mcp` is a stdio server on this PC. Point a client at the installed exe:
123
+
124
+ ```text
125
+ headroom.exe mcp
126
+ ```
127
+
128
+ **Claude Desktop** (`claude_desktop_config.json`):
129
+
130
+ ```json
131
+ {
132
+ "mcpServers": {
133
+ "headroom": {
134
+ "command": "headroom.exe",
135
+ "args": ["mcp"]
136
+ }
137
+ }
138
+ }
139
+ ```
140
+
141
+ **Cursor** (`.cursor/mcp.json`):
142
+
143
+ ```json
144
+ {
145
+ "mcpServers": {
146
+ "headroom": {
147
+ "command": "headroom.exe",
148
+ "args": ["mcp"]
149
+ }
150
+ }
151
+ }
152
+ ```
153
+
154
+ **Codex and ChatGPT** (`config.toml`):
155
+
156
+ ```toml
157
+ [mcp_servers.headroom]
158
+ command = "headroom.exe"
159
+ args = ["mcp"]
160
+ ```
161
+
162
+ With the pip package on `PATH`, the command is `headroom` and the argument is still `mcp`.
163
+
164
+ mcp-name: io.github.Jasdish-Singh/headroom
165
+
166
+ ### Quick start with AI
167
+
168
+ Paste this into Claude, ChatGPT, or Cursor after connecting the MCP server.
169
+
170
+ ```text
171
+ You have access to Headroom, a local tool that monitors and protects this PC's RAM and disk.
172
+
173
+ Start a full health check:
174
+ 1. Call `status` and tell me current RAM, commit, page file and free disk space in plain English.
175
+ 2. Call `top` and list the 5 biggest RAM users, saying whether each is normal or worth closing.
176
+ 3. Call `analyze` and `scan` to find what's taking up disk space, grouped into important, safe to clean, and needs my review.
177
+ 4. Call `dupes` and `startup_list`, and point out duplicate files and startup apps slowing my PC.
178
+ 5. Call `changes` and `report` to tell me what changed recently and any trends to watch.
179
+
180
+ Finish with a short summary: my overall PC health (good / watch / act now), the top 3 things to do, and how much space or RAM each would free.
181
+
182
+ Rules: only read and explain. If cleanup helps, call `clean`, `move` or `startup_disable` only to show me a plan. Never delete, move or disable anything yourself. I'll approve inside Headroom.
183
+ ```
184
+
185
+ The same text is [docs/ai-starter-prompt.md](docs/ai-starter-prompt.md) and the MCP prompt `health_check`. Tool list: [docs/commands.md](docs/commands.md).
186
+
187
+ ## More
188
+
189
+ - [Commands](docs/commands.md)
190
+ - [Roomy](docs/roomy.md)
191
+ - [Plugins](docs/plugins.md)
192
+ - [Build](docs/develop.md)
193
+ - [Publishing](docs/PUBLISHING.md)
@@ -0,0 +1,159 @@
1
+ <!-- mcp-name: io.github.Jasdish-Singh/headroom -->
2
+
3
+ # Headroom
4
+
5
+ [![CI](https://github.com/Jasdish-Singh/headroom/actions/workflows/test.yml/badge.svg)](https://github.com/Jasdish-Singh/headroom/actions/workflows/test.yml)
6
+ [![License: MIT](https://img.shields.io/github/license/Jasdish-Singh/headroom)](https://github.com/Jasdish-Singh/headroom/blob/main/LICENSE)
7
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue)](https://github.com/Jasdish-Singh/headroom/blob/main/pyproject.toml)
8
+
9
+ **A quiet watcher for the RAM and disk on your Windows PC.**
10
+
11
+ Headroom sits in the tray, keeps a small history, and shows what is full. Cleanup is limited to an allowlist. Type YES to delete. Type MOVE to send a reviewed file to the Recycle Bin or to an archive folder.
12
+
13
+ [Jasdish Singh](https://github.com/Jasdish-Singh). [MIT](LICENSE), 2026.
14
+
15
+ <p>
16
+ <img alt="Roomy on a Windows desktop" src="https://raw.githubusercontent.com/Jasdish-Singh/headroom/main/docs/images/roomy-desktop.png" width="720">
17
+ </p>
18
+
19
+ ## What it does
20
+
21
+ - Watches RAM, commit charge, page file, and free space.
22
+ - Records history and a short report (disk trend, likely leaks, folders that grew).
23
+ - Cleans only `user_temp`, `windows_temp`, `chrome_cache`, `edge_cache`, `firefox_cache`, `thumbnail_cache`, and `recycle_bin`. Config cannot add a path.
24
+ - Reviews files, duplicate copies, old downloads, and startup programs.
25
+ - Serves a page on `127.0.0.1` and a read-only desktop pet, Roomy.
26
+ - Exposes a local MCP server. `clean`, `move`, and `startup_disable` return a plan. They do not change the PC.
27
+
28
+ ## Install
29
+
30
+ The command is `headroom`. Python 3.11 or newer.
31
+
32
+ **Exe.** `headroom.exe` and `Headroom-Setup` from [GitHub Releases](https://github.com/Jasdish-Singh/headroom/releases) once a release is published. Setup does not clean anything and does not register a logon task.
33
+
34
+ **winget,** after the manifest is published:
35
+
36
+ ```powershell
37
+ winget install JasdishSingh.Headroom
38
+ ```
39
+
40
+ The URL and SHA256 in `packaging/winget` are still placeholders.
41
+
42
+ **pip.** The PyPI name is `headroom-pc`.
43
+
44
+ ```powershell
45
+ py -3.12 -m pip install "headroom-pc[tray,mcp]"
46
+ headroom status
47
+ ```
48
+
49
+ `tray` is the icon. `mcp` is the assistant server.
50
+
51
+ ## Quick start
52
+
53
+ ```powershell
54
+ headroom status
55
+ headroom top
56
+ headroom clean --dry-run
57
+ headroom watch
58
+ headroom dashboard
59
+ ```
60
+
61
+ `headroom watch` takes a sample immediately, then every 30 seconds (slower on battery, faster under pressure). `headroom install` starts it at sign-in. A second watch, dashboard, or pet does not start another copy.
62
+
63
+ `headroom dashboard` prints and opens `http://127.0.0.1:<port>/?token=...`. Before the first stored sample, the page shows the live reading.
64
+
65
+ Commands, defaults, and exit codes: [docs/commands.md](docs/commands.md).
66
+
67
+ ## Roomy
68
+
69
+ Roomy is a small beige monitor. The cord is the tail. It walks a little and changes face with the PC. It cannot clean, move, or delete.
70
+
71
+ <p>
72
+ <img alt="Roomy close up" src="https://raw.githubusercontent.com/Jasdish-Singh/headroom/main/docs/images/roomy-zoom.png" width="360">
73
+ </p>
74
+
75
+ ```powershell
76
+ headroom pet
77
+ headroom widget
78
+ headroom pet --skin midnight
79
+ headroom pet --skin tower --lite
80
+ ```
81
+
82
+ `headroom widget` is the same pet. Click opens the dashboard. Drag remembers the spot. Right-click: dashboard, report, mute, hide, quit.
83
+
84
+ Moods, skins, and the frame budget: [docs/roomy.md](docs/roomy.md).
85
+
86
+ ## MCP
87
+
88
+ `headroom mcp` is a stdio server on this PC. Point a client at the installed exe:
89
+
90
+ ```text
91
+ headroom.exe mcp
92
+ ```
93
+
94
+ **Claude Desktop** (`claude_desktop_config.json`):
95
+
96
+ ```json
97
+ {
98
+ "mcpServers": {
99
+ "headroom": {
100
+ "command": "headroom.exe",
101
+ "args": ["mcp"]
102
+ }
103
+ }
104
+ }
105
+ ```
106
+
107
+ **Cursor** (`.cursor/mcp.json`):
108
+
109
+ ```json
110
+ {
111
+ "mcpServers": {
112
+ "headroom": {
113
+ "command": "headroom.exe",
114
+ "args": ["mcp"]
115
+ }
116
+ }
117
+ }
118
+ ```
119
+
120
+ **Codex and ChatGPT** (`config.toml`):
121
+
122
+ ```toml
123
+ [mcp_servers.headroom]
124
+ command = "headroom.exe"
125
+ args = ["mcp"]
126
+ ```
127
+
128
+ With the pip package on `PATH`, the command is `headroom` and the argument is still `mcp`.
129
+
130
+ mcp-name: io.github.Jasdish-Singh/headroom
131
+
132
+ ### Quick start with AI
133
+
134
+ Paste this into Claude, ChatGPT, or Cursor after connecting the MCP server.
135
+
136
+ ```text
137
+ You have access to Headroom, a local tool that monitors and protects this PC's RAM and disk.
138
+
139
+ Start a full health check:
140
+ 1. Call `status` and tell me current RAM, commit, page file and free disk space in plain English.
141
+ 2. Call `top` and list the 5 biggest RAM users, saying whether each is normal or worth closing.
142
+ 3. Call `analyze` and `scan` to find what's taking up disk space, grouped into important, safe to clean, and needs my review.
143
+ 4. Call `dupes` and `startup_list`, and point out duplicate files and startup apps slowing my PC.
144
+ 5. Call `changes` and `report` to tell me what changed recently and any trends to watch.
145
+
146
+ Finish with a short summary: my overall PC health (good / watch / act now), the top 3 things to do, and how much space or RAM each would free.
147
+
148
+ Rules: only read and explain. If cleanup helps, call `clean`, `move` or `startup_disable` only to show me a plan. Never delete, move or disable anything yourself. I'll approve inside Headroom.
149
+ ```
150
+
151
+ The same text is [docs/ai-starter-prompt.md](docs/ai-starter-prompt.md) and the MCP prompt `health_check`. Tool list: [docs/commands.md](docs/commands.md).
152
+
153
+ ## More
154
+
155
+ - [Commands](docs/commands.md)
156
+ - [Roomy](docs/roomy.md)
157
+ - [Plugins](docs/plugins.md)
158
+ - [Build](docs/develop.md)
159
+ - [Publishing](docs/PUBLISHING.md)
@@ -0,0 +1,62 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "headroom-pc"
7
+ version = "0.1.0"
8
+ description = "Always-on RAM and disk watcher for a Windows laptop, with history, file review, and allowlisted cleanup."
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Jasdish Singh" }]
13
+ keywords = ["windows", "memory", "disk", "cleanup", "monitor"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Environment :: Win32 (MS Windows)",
17
+ "Intended Audience :: End Users/Desktop",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Operating System :: Microsoft :: Windows :: Windows 11",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Topic :: System :: Monitoring",
23
+ "Topic :: Utilities",
24
+ ]
25
+ dependencies = [
26
+ "psutil>=5.9.0",
27
+ "PyYAML>=6.0.1",
28
+ ]
29
+
30
+ [project.optional-dependencies]
31
+ tray = [
32
+ "pystray>=0.19.5",
33
+ "Pillow>=10.0.0",
34
+ ]
35
+ mcp = [
36
+ "mcp>=2.0,<3",
37
+ ]
38
+ dev = [
39
+ "pytest>=8.0",
40
+ "mcp>=2.0,<3",
41
+ ]
42
+
43
+ [project.urls]
44
+ Homepage = "https://github.com/Jasdish-Singh/headroom"
45
+ Repository = "https://github.com/Jasdish-Singh/headroom"
46
+ Issues = "https://github.com/Jasdish-Singh/headroom/issues"
47
+
48
+ [project.scripts]
49
+ headroom = "headroom.cli:main"
50
+ headroom-pc = "headroom.cli:main"
51
+
52
+ [tool.setuptools.packages.find]
53
+ where = ["src"]
54
+
55
+ [tool.setuptools.package-data]
56
+ headroom = ["py.typed", "pets/*.png", "profiles/*.rules"]
57
+ "headroom.profiles" = ["*.rules"]
58
+
59
+ [tool.pytest.ini_options]
60
+ testpaths = ["tests"]
61
+ pythonpath = ["src"]
62
+ addopts = "--tb=short"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ """Headroom watches RAM and disk, reviews files, and cleans only known-safe caches."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,8 @@
1
+ from __future__ import annotations
2
+
3
+ import sys
4
+
5
+ from headroom.cli import main
6
+
7
+ if __name__ == "__main__":
8
+ raise SystemExit(main())
@@ -0,0 +1,125 @@
1
+ """Thresholds for sustained RAM pressure and low free space.
2
+
3
+ RAM must stay above the configured percent for the full sustained window.
4
+ A single spike does not notify. Disk alerts fire when a watched drive crosses
5
+ into a worse state, and they do not repeat while it stays there.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from dataclasses import dataclass, field
11
+
12
+ from headroom.config import Config
13
+ from headroom.formatutil import format_bytes, format_duration, format_percent
14
+ from headroom.probe import Sample
15
+
16
+ _RANK = {"ok": 0, "warn": 1, "critical": 2}
17
+
18
+
19
+ @dataclass
20
+ class AlertState:
21
+ ram_high_since: float | None = None
22
+ ram_alerted: bool = False
23
+ disk_level: dict[str, str] = field(default_factory=dict)
24
+
25
+
26
+ @dataclass(frozen=True)
27
+ class Alert:
28
+ level: str
29
+ code: str
30
+ title: str
31
+ message: str
32
+
33
+
34
+ def disk_pressure(free_bytes: int, config: Config) -> str:
35
+ """Return ``ok``, ``warn``, or ``critical``. Equal to a line is still ok."""
36
+ if free_bytes < config.disk_critical_free_bytes:
37
+ return "critical"
38
+ if free_bytes < config.disk_warn_free_bytes:
39
+ return "warn"
40
+ return "ok"
41
+
42
+
43
+ def evaluate(sample: Sample, state: AlertState, config: Config, now: float) -> list[Alert]:
44
+ """Update ``state`` and return alerts that should be delivered now."""
45
+ alerts: list[Alert] = []
46
+ alerts.extend(_ram_alerts(sample, state, config, now))
47
+ alerts.extend(_disk_alerts(sample, state, config))
48
+ return alerts
49
+
50
+
51
+ def _ram_alerts(
52
+ sample: Sample,
53
+ state: AlertState,
54
+ config: Config,
55
+ now: float,
56
+ ) -> list[Alert]:
57
+ percent = sample.memory.ram_percent
58
+ if percent > config.ram_percent:
59
+ if state.ram_high_since is None:
60
+ state.ram_high_since = now
61
+ elapsed = now - state.ram_high_since
62
+ if elapsed >= config.ram_sustained_seconds and not state.ram_alerted:
63
+ state.ram_alerted = True
64
+ window = format_duration(config.ram_sustained_seconds)
65
+ return [
66
+ Alert(
67
+ level="warning",
68
+ code="ram_sustained",
69
+ title="Headroom: RAM is high",
70
+ message=(
71
+ f"RAM has stayed above {format_percent(config.ram_percent)} "
72
+ f"for {window} (now {format_percent(percent)}). "
73
+ "Run `headroom top` to see which processes are using it."
74
+ ),
75
+ )
76
+ ]
77
+ return []
78
+ state.ram_high_since = None
79
+ state.ram_alerted = False
80
+ return []
81
+
82
+
83
+ def _disk_alerts(sample: Sample, state: AlertState, config: Config) -> list[Alert]:
84
+ alerts: list[Alert] = []
85
+ seen: set[str] = set()
86
+ watched = set(config.drives)
87
+ for disk in sample.disks:
88
+ if disk.key not in watched or disk.key in seen:
89
+ continue
90
+ seen.add(disk.key)
91
+ level = disk_pressure(disk.free, config)
92
+ previous = state.disk_level.get(disk.key, "ok")
93
+ state.disk_level[disk.key] = level
94
+ if _RANK[level] <= _RANK[previous]:
95
+ continue
96
+ if level == "critical":
97
+ alerts.append(
98
+ Alert(
99
+ level="critical",
100
+ code="disk_critical",
101
+ title=f"Headroom: {disk.key} is critically low",
102
+ message=(
103
+ f"{disk.key} has {format_bytes(disk.free)} free, under the "
104
+ f"{format_bytes(config.disk_critical_free_bytes)} critical line. "
105
+ "Run `headroom clean --dry-run` to preview safe cleanup."
106
+ ),
107
+ )
108
+ )
109
+ elif level == "warn":
110
+ alerts.append(
111
+ Alert(
112
+ level="warning",
113
+ code="disk_low",
114
+ title=f"Headroom: {disk.key} is low on space",
115
+ message=(
116
+ f"{disk.key} has {format_bytes(disk.free)} free, under the "
117
+ f"{format_bytes(config.disk_warn_free_bytes)} warning line."
118
+ ),
119
+ )
120
+ )
121
+ for key in list(state.disk_level):
122
+ if key not in seen and key in watched:
123
+ # The drive disappeared. Forget it so a later return can alert again.
124
+ del state.disk_level[key]
125
+ return alerts