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.
- headroom_pc-0.1.0/LICENSE +21 -0
- headroom_pc-0.1.0/PKG-INFO +193 -0
- headroom_pc-0.1.0/README.md +159 -0
- headroom_pc-0.1.0/pyproject.toml +62 -0
- headroom_pc-0.1.0/setup.cfg +4 -0
- headroom_pc-0.1.0/src/headroom/__init__.py +3 -0
- headroom_pc-0.1.0/src/headroom/__main__.py +8 -0
- headroom_pc-0.1.0/src/headroom/alerts.py +125 -0
- headroom_pc-0.1.0/src/headroom/analyze.py +555 -0
- headroom_pc-0.1.0/src/headroom/autostart.py +312 -0
- headroom_pc-0.1.0/src/headroom/cleanup.py +773 -0
- headroom_pc-0.1.0/src/headroom/cli.py +1250 -0
- headroom_pc-0.1.0/src/headroom/config.py +512 -0
- headroom_pc-0.1.0/src/headroom/dashboard.py +628 -0
- headroom_pc-0.1.0/src/headroom/downloads.py +227 -0
- headroom_pc-0.1.0/src/headroom/dupes.py +196 -0
- headroom_pc-0.1.0/src/headroom/explain.py +196 -0
- headroom_pc-0.1.0/src/headroom/firstrun.py +98 -0
- headroom_pc-0.1.0/src/headroom/formatutil.py +31 -0
- headroom_pc-0.1.0/src/headroom/history.py +605 -0
- headroom_pc-0.1.0/src/headroom/instance.py +115 -0
- headroom_pc-0.1.0/src/headroom/logsetup.py +47 -0
- headroom_pc-0.1.0/src/headroom/mcp_prompts.py +23 -0
- headroom_pc-0.1.0/src/headroom/mcp_server.py +38 -0
- headroom_pc-0.1.0/src/headroom/mcp_tools.py +175 -0
- headroom_pc-0.1.0/src/headroom/notify.py +129 -0
- headroom_pc-0.1.0/src/headroom/pet.py +1334 -0
- headroom_pc-0.1.0/src/headroom/pets/__init__.py +1 -0
- headroom_pc-0.1.0/src/headroom/pets/draw.py +544 -0
- headroom_pc-0.1.0/src/headroom/pets/midnight.png +0 -0
- headroom_pc-0.1.0/src/headroom/pets/roomy.png +0 -0
- headroom_pc-0.1.0/src/headroom/pets/tower.png +0 -0
- headroom_pc-0.1.0/src/headroom/plans.py +108 -0
- headroom_pc-0.1.0/src/headroom/plugins.py +152 -0
- headroom_pc-0.1.0/src/headroom/probe.py +197 -0
- headroom_pc-0.1.0/src/headroom/processes.py +77 -0
- headroom_pc-0.1.0/src/headroom/profiles/__init__.py +1 -0
- headroom_pc-0.1.0/src/headroom/profiles/developer.rules +64 -0
- headroom_pc-0.1.0/src/headroom/profiles/gamer.rules +36 -0
- headroom_pc-0.1.0/src/headroom/profiles/student.rules +34 -0
- headroom_pc-0.1.0/src/headroom/py.typed +0 -0
- headroom_pc-0.1.0/src/headroom/render.py +457 -0
- headroom_pc-0.1.0/src/headroom/review.py +353 -0
- headroom_pc-0.1.0/src/headroom/rules.py +424 -0
- headroom_pc-0.1.0/src/headroom/safety.py +211 -0
- headroom_pc-0.1.0/src/headroom/scan.py +82 -0
- headroom_pc-0.1.0/src/headroom/schedule.py +104 -0
- headroom_pc-0.1.0/src/headroom/selfcheck.py +78 -0
- headroom_pc-0.1.0/src/headroom/service.py +120 -0
- headroom_pc-0.1.0/src/headroom/startup.py +525 -0
- headroom_pc-0.1.0/src/headroom/tracker.py +317 -0
- headroom_pc-0.1.0/src/headroom/walk.py +230 -0
- headroom_pc-0.1.0/src/headroom/watch.py +324 -0
- headroom_pc-0.1.0/src/headroom/weekly.py +154 -0
- headroom_pc-0.1.0/src/headroom_pc.egg-info/PKG-INFO +193 -0
- headroom_pc-0.1.0/src/headroom_pc.egg-info/SOURCES.txt +71 -0
- headroom_pc-0.1.0/src/headroom_pc.egg-info/dependency_links.txt +1 -0
- headroom_pc-0.1.0/src/headroom_pc.egg-info/entry_points.txt +3 -0
- headroom_pc-0.1.0/src/headroom_pc.egg-info/requires.txt +13 -0
- headroom_pc-0.1.0/src/headroom_pc.egg-info/top_level.txt +1 -0
- headroom_pc-0.1.0/tests/test_agent.py +601 -0
- headroom_pc-0.1.0/tests/test_cleanup.py +312 -0
- headroom_pc-0.1.0/tests/test_cli.py +205 -0
- headroom_pc-0.1.0/tests/test_config.py +156 -0
- headroom_pc-0.1.0/tests/test_formatutil.py +21 -0
- headroom_pc-0.1.0/tests/test_history_commit.py +266 -0
- headroom_pc-0.1.0/tests/test_monitor.py +308 -0
- headroom_pc-0.1.0/tests/test_pet.py +387 -0
- headroom_pc-0.1.0/tests/test_profiles.py +887 -0
- headroom_pc-0.1.0/tests/test_project.py +110 -0
- headroom_pc-0.1.0/tests/test_safety.py +139 -0
- headroom_pc-0.1.0/tests/test_v4.py +880 -0
- 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
|
+
[](https://github.com/Jasdish-Singh/headroom/actions/workflows/test.yml)
|
|
40
|
+
[](https://github.com/Jasdish-Singh/headroom/blob/main/LICENSE)
|
|
41
|
+
[](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
|
+
[](https://github.com/Jasdish-Singh/headroom/actions/workflows/test.yml)
|
|
6
|
+
[](https://github.com/Jasdish-Singh/headroom/blob/main/LICENSE)
|
|
7
|
+
[](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,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
|