machud 0.0.0 → 0.0.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/DESIGN.md ADDED
@@ -0,0 +1,314 @@
1
+ ---
2
+ # machud DESIGN.md — the visual identity, as a hands-off anchor.
3
+ # Format inspired by google-labs-code/design.md, adapted for a terminal (TUI):
4
+ # machine-readable tokens up top, human rationale below. This file is the thing the
5
+ # autonomous loop optimizes visual choices AGAINST. It is a product decision, not a
6
+ # preference — see CONTRIBUTING.md and .agents/docs/decisions.md (D9).
7
+ #
8
+ # STATUS: most of this is TARGET, not yet shipped. The code does NOT implement it yet
9
+ # (src/theme.ts is still Tokyo Night; the layout is still the flat 3-row grid). Build
10
+ # toward this via the staged RD0–RD5 backlog. Do NOT treat an unbuilt section here as a
11
+ # passing invariant — a "panel renders" grep is not "matches DESIGN.md". Each section is
12
+ # tagged (TARGET) or (SHIPPED).
13
+ meta:
14
+ name: machud
15
+ mood: "cool but refined — a calm, green-forward instrument; striking, never harsh"
16
+ basis: "Everforest — a low-strain, muted, eye-friendly scheme; green-forward, using Everforest's own green"
17
+ appearance: "follows macOS system light/dark automatically (D8); no user theme switch"
18
+
19
+ # --- COLORS (TARGET) — the source of truth. src/theme.ts MUST be rewritten to these
20
+ # tokens (it is still Tokyo Night today — backlog RD1); verify.mjs pins the hex so
21
+ # doc and code can never silently desync again. ---
22
+ colors:
23
+ dark:
24
+ bg: "#2d353b" # soft black, never #000
25
+ bg_lift: "#343f44" # one step up = panel depth
26
+ frame: "#4f5b58" # muted border
27
+ title: "#d3c6aa" # warm off-white, never #fff
28
+ text: "#9da9a0"
29
+ dim: "#7a8478" # labels, tracks (Everforest grey0; was a #5c6a72 typo — Q2, owner-ruled)
30
+ accent: "#a7c080" # PRIMARY accent — Everforest's own green
31
+ silver: "#c4c9cf" # Apple aluminium — the "mac" half of the mac|hud wordmark [VOUCHED @hyf0, D15]
32
+ module:
33
+ cpu: "#a7c080" # green (hero)
34
+ mem: "#d699b6" # purple
35
+ gpu: "#7fbbb3" # blue
36
+ disk: "#dbbc7f" # yellow
37
+ net: "#83c092" # aqua
38
+ battery: "#e69875" # orange
39
+ sensor: "#e67e80" # red
40
+ status:
41
+ good: "#a7c080"
42
+ warn: "#dbbc7f"
43
+ bad: "#e67e80"
44
+ light:
45
+ bg: "#fdf6e3"
46
+ bg_lift: "#f4f0d9"
47
+ frame: "#ddd8be"
48
+ title: "#5c6a72"
49
+ text: "#5c6a72"
50
+ dim: "#939f91"
51
+ accent: "#8da101"
52
+ silver: "#8d939a" # Apple aluminium — "mac" half of the mac|hud wordmark [VOUCHED @hyf0, D15]
53
+ module:
54
+ cpu: "#8da101"
55
+ mem: "#df69ba"
56
+ gpu: "#3a94c5"
57
+ disk: "#dfa000"
58
+ net: "#35a77c"
59
+ battery: "#f57d26"
60
+ sensor: "#f85552"
61
+ status:
62
+ good: "#8da101"
63
+ warn: "#dfa000"
64
+ bad: "#f85552"
65
+
66
+ color_tier: # D11 — truecolor is an ENHANCEMENT, not a guarantee
67
+ truecolor: "full same-hue luminance gradients (24-bit)"
68
+ ansi256: "drop gradients to a single solid accent (degrade like light mode)"
69
+ ansi16: "prefer muted NAMED ansi; never let arbitrary hex snap to saturated basic colors"
70
+ detect: "chalk level at startup; macOS Terminal.app (the OS default) is 256-color, no truecolor"
71
+
72
+ # --- GLYPHS (a terminal's "typography": fixed-width, so the character IS the type) ---
73
+ glyphs:
74
+ border: "round ╭ ╮ ╰ ╯ │ ─" # round = calm; never heavy/double as default
75
+ bar_fill: "█"
76
+ bar_track: "─" # dim, low-contrast
77
+ graph: "braille ⠀-⣿ area chart, 2×4 subpixels per cell"
78
+ big_digit: "5-row block figures for the one hero number per panel"
79
+ spark: "▁▂▃▄▅▆▇█"
80
+ sep: "·"
81
+ charge: "⇡ charging · ⇣ discharging" # width-1, NOT the ⚡ emoji (double-width)
82
+ absent: "—" # honest placeholder for sudo-only / unavailable
83
+
84
+ # --- SPACE & WEIGHT ---
85
+ space:
86
+ layout: "(TARGET) wide curated default = 3-tier hierarchy; + ONE narrow/watch-face fallback (responsive, D4 reopened). NOT the old flat 3-row grid; NOT a 5-breakpoint ladder."
87
+ gutter: 1 # 1 cell padding inside every panel border
88
+ panel_gap: 1
89
+ bar_align: "within a panel, ALL bars share one label-column width + one bar width; left edges, bar ends, and value columns form clean vertical rules. Guaranteed in the WIDE layout where bars render (not at watch-face XS)."
90
+ weight:
91
+ hierarchy: "size > position > weight > color — primary metric is BIG + top-left + accent"
92
+ gradients: "meters & graphs use SAME-HUE luminance ramps (no complementary hue clash)"
93
+
94
+ # --- COMPONENTS ---
95
+ components:
96
+ Panel: { border: frame, title: title, body: bg }
97
+ BigNumber: { color: "module accent, gentle 2-tone same-hue gradient" }
98
+ Meter: { fill: "module accent → warn/bad as value enters danger (levelColor)", track: dim }
99
+ Sparkline: { ramp: "module accent luminance" }
100
+ Graph: { type: braille_area, ramp: "same-hue luminance, dim bottom → accent top" }
101
+ Status: { color: "good/warn/bad", escalation: "intensity + bar bleeds to warn/bad + a text label where it helps" }
102
+ ---
103
+
104
+ # machud — Visual Identity
105
+
106
+ What machud should look and feel like, written down so it can be built and judged
107
+ **without re-litigating taste every time.** Correctness is machine-checked by `pnpm verify`;
108
+ this document is the anchor for the part a gate can't fully check — the *beautiful* in
109
+ "beautiful, zero-config." Changing anything here is a [VOUCHED]-level decision: ship nothing
110
+ that contradicts this file without the owner's say-so.
111
+
112
+ > **This is a TARGET.** The current code has not implemented it (theme.ts is Tokyo Night; the
113
+ > layout is the old flat 3-row grid). The loop builds toward it via the staged RD0–RD5 backlog,
114
+ > safety-net first. Until a section is shipped, do not treat its prose as a passing invariant.
115
+
116
+ ## Overview — the one-line thesis
117
+
118
+ **A calm, green-forward instrument that is cool, not loud.** machud takes over the terminal
119
+ like btop, but its visual language is *refined maximalism*: rich, alive, and striking —
120
+ through gradient meters, high-resolution braille graphs, and big hero numbers — yet **muted,
121
+ low-contrast, and easy on the eyes**. Cool is the goal; harsh is the failure mode.
122
+
123
+ **Drama comes from FORM, never from saturation.** When a frame feels flat, add data density,
124
+ hierarchy, or resolution — *never* raise chroma or contrast. machud's identity is its
125
+ **composition** (hero block number + gradient meter + braille graph + P/E grid), not its
126
+ palette; Everforest is a proven low-strain base, not a brand statement.
127
+
128
+ The seven principles are the whole spec in miniature.
129
+
130
+ 1. **Glanceable.** Every panel answers its core question in under a second — one hero metric,
131
+ BIG, top-left, in a preattentive channel (size + position + accent). *(Few, "5-second rule";
132
+ Ware, preattentive processing.)*
133
+ 2. **Color = identity + state, on DIFFERENT channels.** Hue marks *which module*. **Status is
134
+ carried by colour + intensity** (good/warn/bad green/amber/red; a bar bleeding toward warn-red),
135
+ plus a text label where it helps (e.g. disk "FULL"). **No accessibility / colour-independence
136
+ layer** — machud is a passive full-screen TUI (not screen-reader territory), and the owner ruled
137
+ a11y out of scope (D14). *(Few; Ware, preattentive attributes.)*
138
+ 3. **Cool by default, dramatic on alarm.** Baseline is already handsome (Everforest muted +
139
+ gradient meters + braille graphs + easing on value change). Healthy = quiet. Only an
140
+ **event** (near-full, thermal, on-battery) earns escalation: brightness, the bar bleeding to
141
+ warn/bad, and — only here — motion. **Motion is alarm/transition-only, never
142
+ a routine-state carrier** (and it is invisible to a single-frame gate, so it is not a
143
+ load-bearing accessibility channel). *(Weiser, Calm Technology; Tufte, "smallest effective difference".)*
144
+ 4. **Space by value.** Real estate ∝ worth, not democratic. **CPU and Memory get the most room
145
+ (tier-1 hero); Network leads the medium tier; Disk and Sensors compress into a status strip.**
146
+ *(Few.)*
147
+ 5. **Small multiples + sparklines.** Per-core load is a P/E-grouped grid the eye compares at a
148
+ glance; history is a braille area graph. Maximize data-ink; delete chartjunk. *(Tufte.)*
149
+ 6. **Consistency.** Every panel's hero number lives in the same spot, same weight ramp; panels
150
+ and bars align. Same things look the same. *(Gestalt: similarity, alignment.)*
151
+ 8. **Stable & dense (owner feedback 2026-06-20).** A panel's **row structure is FIXED** — an
152
+ optional value fills its slot with `—`/`on AC`, it never appears/disappears (a value popping in
153
+ or out must NOT change a panel's height or shift the row below it). Numeric columns **right-align
154
+ to a fixed width** so a value's length never moves the column. And a large/hero panel must **earn
155
+ its space with real, dynamic, comparative data** (history graphs, per-core grids, breakdowns) — a
156
+ big box holding three numbers is a failure, not minimalism.
157
+ 7. **Mac-native, honest data.** Show the zero-sudo Apple-Silicon signals others can't (P/E
158
+ cores, adapter PD wattage, memory pressure, thermal pressure). What needs `sudo` is `—`,
159
+ never faked, never prompted. *(Tufte "show the data"; decisions D2/D3.)*
160
+
161
+ ## Colors
162
+
163
+ The palette is **Everforest** — muted, low-contrast, eye-strain-conscious. The **primary accent
164
+ is Everforest's own green (`#a7c080`)**; green-forward is the identity. (A nice fit for a
165
+ vue-tui project, but the color is Everforest's, not bent to match Vue.) The `colors` tokens
166
+ above are the source of truth; **`src/theme.ts` must be rewritten to mirror them (it is still
167
+ Tokyo Night — RD1)**, and verify.mjs pins the hex so they can't silently diverge.
168
+
169
+ Rules (*Refactoring UI*, Few, Tufte's *smallest effective difference*):
170
+ - **Never pure black or pure white.** Base `#2d353b`, text warm off-white.
171
+ - **Per-module hue is confined to small ink** — the panel's title / border / hero number only.
172
+ Panel **bodies** (bars, tracks, secondary text) stay neutral grey + the shared green accent,
173
+ so **green is the only hue across panels** and the 7 module hues never become a rainbow. The
174
+ "loud 10%" of 60-30-10 is this per-panel accent, nothing more.
175
+ - **Low contrast on purpose.** Use the *weakest* distinction that still reads.
176
+ - **No high-saturation complementary pairs on dark** (they vibrate — chromostereopsis). Gradients
177
+ ramp within one hue's luminance.
178
+ - **Truecolor is an ENHANCEMENT, not a guarantee (D11).** The gradient look needs 24-bit color;
179
+ macOS's default Terminal.app is 256-color. Detect chalk level and degrade: 256 → solid accent
180
+ (no gradient), 16 → muted named ANSI. Never let a muted hex snap to a saturated basic color
181
+ (the neon the Don'ts forbid). Judge "cool but refined" through the real chalk path at level 2/1,
182
+ not the raw-truecolor prototype.
183
+ - **Light/dark follows macOS** automatically (D8); no theme switch (D1). Light is a **faithful,
184
+ lower-drama daylight mode**: gradients compress, it cannot glow, and that is acceptable by
185
+ physics — but hero, alignment, and status colours must read as **deliberate** on the cream base.
186
+ Light is not a downgrade; it has the same quality bar minus glow.
187
+
188
+ ## Glyphs & weight (the terminal's typography)
189
+
190
+ A fixed-width grid has no fonts, so the **character set and ANSI weight are the typography.**
191
+ - **Round borders** (`╭─╮`) — calm; heavy/double frames read as loud, not the default.
192
+ - **Hierarchy by size → position → weight → color.** Hero number is a 5-row block figure;
193
+ secondary values are normal weight and `dim`.
194
+ - **Gradients are same-hue luminance ramps** — gentle, never a hue clash.
195
+ - **`⇡ / ⇣`** for charge direction (width-1; **never `⚡`**, a double-width emoji that breaks
196
+ alignment and tofus on glyph-poor terminals). **`—`** = unavailable.
197
+
198
+ ## Layout (SHIPPED — RD4)
199
+
200
+ A **single curated wide layout** (D1: no config, no focus/expand) arranged as a **3-tier
201
+ hierarchy** — important up top, minor compressed below:
202
+
203
+ - **Tier 1 — hero (big, with history graph):** **CPU**, **Memory**.
204
+ - **Tier 2 — medium:** **Network** (lead — owner-ranked above battery), **Battery**, **GPU**.
205
+ Ordered so **GPU** (with its history graph) fills the aligned right-hand column while **Battery**
206
+ stays compact in the middle — a wide-but-empty Battery was the alternative. Battery is here, NOT in
207
+ tier-1: its differentiation is its **data** (live PD wattage), not its real estate.
208
+ - **Tier 3 — status strip (compact, one line):** **Disk**, **Sensors**, uptime, load.
209
+
210
+ - **1-cell gutter** inside every panel; **1-cell gap** between panels.
211
+ - **Cross-tier alignment:** all three tiers share one **~60% vertical divider** — the right-hand panels
212
+ (MEM / GPU / SENSORS) left-align to the same column, and the left panels (CPU / Network / Disk) share
213
+ the left edge. Achieved with `flexBasis:0` + tuned flexGrow ratios so content can't bloat a column;
214
+ **pinned by a verify assertion** (right-edge cols within 2) so it can't silently regress. The lone
215
+ exception is tier-2's inner Network|Battery seam (3 panels vs 2 — unavoidable).
216
+ - **Alignment is non-negotiable** (in the wide layout, where bars render): every bar in a panel
217
+ shares **one** label-column width and **one** bar width, so left edges, bar ends, and value
218
+ columns form clean vertical rules. Pad labels to a fixed column; never let a label's length
219
+ decide where its bar starts. (Machine-checked by verify.mjs RD0b.)
220
+ - **Depth from value, not borders alone** — a lifted panel background (`bg_lift`) + a dim frame
221
+ separate a panel from the field without shouting.
222
+
223
+ ## Responsive (TARGET — last to build, D4 reopened by @hyf0)
224
+
225
+ **Scoped to TWO tiers**, not a 5-breakpoint ladder (that is a scope bomb for an unattended loop):
226
+ - **Wide** (default): the full 3-tier hierarchy above.
227
+ - **Narrow / watch-face:** a single-column fallback; at the smallest size, just the hero numbers
228
+ (CPU% / MEM% / BAT%). The hierarchy IS the degradation order — the least-important tier drops
229
+ first, hero last. *(Marcotte responsive grids; Wroblewski mobile-first; Walton content choreography.)*
230
+ - **Auto-adapt to viewport, like D8 adapts to appearance** — not user config, still zero-config (D1).
231
+ - **Width-seam caveat:** `useWindowSize()` is reactive (reads `stdout.columns`, then a
232
+ terminal-size probe, falling back to 80 only as a last resort), and `App.vue` already binds it as the width. The catch is the
233
+ `--once`/verify path has no TTY width, so the gate drives width via `COLUMNS` →
234
+ `renderToString({columns})` (main.ts already does this). RD5 must make the responsive `v-if` read
235
+ that **same** width (thread `columns` as a prop), so the breakpoint asserted at COLUMNS=40/120 is
236
+ the one the code branches on.
237
+ - **What the gate must assert (RD0b):** widest visible line ≤ COLUMNS at each width (catches the
238
+ existing wide layout already overflowing to ~72 at COLUMNS=60); hero present at wide / absent
239
+ at watch-face (proves the seam works); alignment holds where bars render.
240
+
241
+ ## Per-module specification (TARGET)
242
+
243
+ - **CPU — tier-1 hero (the showpiece — must be DENSE, not boring).** Fill the hero space with: the
244
+ **`BigNumber` overall %**; a **tall braille area history graph** (flowing, the btop look); the
245
+ **per-core grid** — each of the 12 cores its own mini-bar, **P and E clusters grouped + labelled,
246
+ coloured by load** (the small-multiples + the per-core data); **per-cluster averages**; and a
247
+ supporting line (top CPU process / load avg). **Apple-Silicon only** for the P/E split: detect
248
+ cluster count via `hw.nperflevels` (Intel = 1) — `cpu.ts` reads `hw.perflevel0/1.logicalcpu` and
249
+ treats a missing `perflevel1` as `eCount=0`; on a single cluster render ONE unlabelled cluster,
250
+ never `0P+0E` or all-P. Frequency needs `sudo` → `—`. (Two short bars + a number in a big box is
251
+ the failure mode Principle 8 forbids.)
252
+
253
+ **Same lens for the others (owner: 举一反三):** MEM → a wired/compressed/app/cache **breakdown bar**
254
+ + history graph + a fixed-height top-process list; GPU → util + a **history graph** (half-empty
255
+ today); DISK → R/W **I/O history** sparkline; BATTERY → the `power` row **always present** (`—`/`on
256
+ AC` off-discharge — this fixes the height jump) + optional charge history; SENSORS → compress (sudo
257
+ took its content) or add a thermal trend. Every panel: fixed rows, fixed-width numeric columns.
258
+ - **MEM — tier-1 hero.** Used % + swap, **plus the real macOS memory-pressure level** from
259
+ `sysctl kern.memorystatus_vm_pressure_level` (1→Normal / 2→Elevated / 4→High) — the Mac-native
260
+ truth Activity Monitor leads with. (The current usedPct heuristic is only a fallback when the
261
+ sysctl is empty.) Top processes by RSS as support.
262
+ - **NETWORK — tier-2 lead.** Adaptive human units (B/KB/MB/GB·s); ▼ down / ▲ up rates with
263
+ sparklines; interface name. **No IP address** — recorded waiver (D12): a LAN IP is low-value
264
+ *and* leaks in the screenshots machud is built to be; show interface + rates instead.
265
+ - **GPU — tier-2.** Utilization % + VRAM + sparkline.
266
+ - **BATTERY — tier-2 (Mac-exclusive data highlight).** Charge % + `⇡/⇣` charge-state glyph +
267
+ health % + cycles. The differentiator: **adapter max wattage, detected LIVE, never hardcoded**
268
+ (`AdapterDetails.Watts`; varies by cable; show only when `ExternalConnected`, else `—`) **+
269
+ real-time charge power** = `Voltage(mV) × Amperage(mA) / 1e6` W. **Unsigned-int trap:** ioreg
270
+ returns `Amperage` as an **unsigned 64-bit** value, so reinterpret as signed
271
+ (`a = raw >= 2**63 ? raw - 2**64 : raw`) **before** the sign test — only then `a < 0` =
272
+ discharging. (`battery.ts`'s `(-?\d+)` parse does NOT handle this — **RD2** fixes it and injects
273
+ the wraparound value e.g. `18446744073709551179` = −437 mA, via RD0c's injection mechanism, so the
274
+ gate proves the reinterpretation.)
275
+ When `|a| ≈ 0` while charged, show **"charged"**, not "0W". This is battery
276
+ charge wattage, NOT total system draw (that needs `sudo` → omitted). Verify can't see
277
+ on-battery/charging transitions on one host → fixture-tested in **RD2** (via RD0c's mechanism) + manual review of the sign path.
278
+ - **DISK — tier-3 strip.** Used % + free, compact. A **state signifier** that is *earned*:
279
+ neutral when roomy; `levelColor`-driven bar + a `NEAR FULL`/`FULL` text token at ≥85% / ≥95%.
280
+ (Today DiskPanel hardcodes the disk hue and wires no levelColor — RD3 fix.)
281
+ - **SENSORS — tier-3 strip.** `pmset` thermal pressure (coloured good/warn/bad by severity) +
282
+ battery pack temp. Die temps / fan RPM need `sudo` → `—`.
283
+
284
+ ## Do's & Don'ts
285
+
286
+ **Do**
287
+ - Make one number the obvious hero of each panel.
288
+ - Align every bar in a panel to a shared label column + bar width (clean vertical rules).
289
+ - Use gradient meters / braille graphs that *encode data*; degrade gradients to a solid accent
290
+ below truecolor.
291
+ - Detect adapter wattage, units, core counts, terminal color level, and viewport width **live**.
292
+ - Show `—` when data honestly isn't available zero-sudo; record an explicit waiver before
293
+ dropping a previously-live metric (e.g. the LAN IP, D12).
294
+
295
+ **Don't**
296
+ - Don't fix "flat" with saturation or contrast — add form/data/hierarchy instead.
297
+ - Don't use the `⚡` emoji, pure black/white, or complementary hue clashes.
298
+ - Don't spend per-module hue on panel bodies — confine it to title/border/hero number.
299
+ - Don't treat motion as a routine-state channel (alarm/transition only).
300
+ - Don't ask for `sudo`, ever — not even opt-in (D2). Don't give Disk/Sensors hero space.
301
+ - Don't ship light mode as an afterthought — it has the full quality bar minus glow.
302
+
303
+ ## Methodology — what this is built on
304
+
305
+ - **Tufte**, *Visual Display* / *Envisioning Information* — data-ink, sparklines, small multiples,
306
+ **smallest effective difference**.
307
+ - **Few**, *Information Dashboard Design* — glanceability, muted-by-default, color as scarce.
308
+ - **Ware**, *Information Visualization* — preattentive attributes; chromostereopsis; redundant encoding.
309
+ - **Wathan & Schoger**, *Refactoring UI* — no pure black/white, reduce saturation, accent sparingly.
310
+ - **Rams**, *Ten Principles* — "Less, but better"; design is unobtrusive.
311
+ - **Weiser**, *Calm Technology* — inform from the periphery; demand attention only on events.
312
+ - **Marcotte** *Responsive Web Design* / **Wroblewski** *Mobile First* / **Walton** *Content Choreography* — the responsive model.
313
+ - **Everforest / Nord / Solarized** palette methodologies — engineered low-eye-strain color.
314
+ - **60-30-10** — keep the loud 10% actually 10%.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Yunfei He
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.
package/README.md ADDED
@@ -0,0 +1,60 @@
1
+ <p align="center">
2
+ <img alt="machud" src="https://raw.githubusercontent.com/hyf0/machud/main/.github/assets/banner.png" width="640">
3
+ </p>
4
+
5
+ <p align="center">
6
+ A zero-config system monitor for the macOS terminal.
7
+ </p>
8
+
9
+ ---
10
+
11
+ **machud is opinionated — that's the whole point.** Where [btop](https://github.com/aristocratos/btop) wins on deep configurability, machud wins on *beautiful + zero-config*: no settings, no theme switcher, no layout knobs. The single curated wide-screen dashboard — Apple-Silicon-aware, system-following light/dark — **is** the product. Want to tune everything yourself? Use btop. machud is the considered default that looks right out of the box and never asks for your password.
12
+
13
+ <p align="center">
14
+ <img alt="machud dashboard" src="https://raw.githubusercontent.com/hyf0/machud/main/.github/assets/screenshot.png" width="860">
15
+ </p>
16
+
17
+ ## Quick start
18
+
19
+ Requires macOS and Node ≥ 22.18. No install, no config, no password:
20
+
21
+ ```bash
22
+ npx machud@latest
23
+ ```
24
+
25
+ > **Using [Vite+](https://viteplus.dev)?** Try it with:
26
+ >
27
+ > ```bash
28
+ > vpx machud@latest
29
+ > ```
30
+
31
+ ### From source
32
+
33
+ ```bash
34
+ pnpm install
35
+ pnpm build # bundle to dist/machud.mjs
36
+ pnpm start # run the live dashboard (needs a real TTY)
37
+ ```
38
+
39
+ Other entry points:
40
+
41
+ ```bash
42
+ pnpm dev # dev mode with HMR
43
+ node dist/machud.mjs --once # render ONE real-data frame to stdout and exit
44
+ ```
45
+
46
+ `--once` needs no TTY — it prints a single frame with live readings and doubles as a pipe-friendly snapshot.
47
+
48
+ ## Design
49
+
50
+ - **Zero-sudo first.** Every reading comes from unprivileged commands (`sysctl`, `vm_stat`, `ioreg`, `pmset`, `netstat`, `df`, `ps`) plus Node's `os`. Metrics that would need `sudo` — precise per-cluster frequency, GPU watts, fan RPM, die temperatures — render as `—`. They never block startup, and machud never asks for your password.
51
+ - **Never crash, just degrade.** Each collector returns safe nulls on failure, and any throwing collector is swapped for its empty default. A missing metric is a dash, not a stack trace.
52
+ - **Apple-Silicon-aware.** P/E-core clusters, unified-memory pressure, and live battery wattage are first-class — with graceful single-cluster fallback on Intel.
53
+
54
+ ## Contributing
55
+
56
+ machud is opinionated by design. **Bug reports are welcome; feature PRs opened without a prior, agreed-upon issue are closed directly**, and theming / feature / aesthetic changes are generally not accepted. See [CONTRIBUTING.md](./CONTRIBUTING.md).
57
+
58
+ ## Credits
59
+
60
+ machud was inspired by [Stats](https://github.com/exelban/stats) by [@exelban](https://github.com/exelban) — its idea, brought to the terminal.