syswatch-terminal-diagnostics
SysWatch is a single-host system diagnostics TUI for macOS and Linux with twelve tabs, plain-English insights, and session scrubbing.
What this skill does
# SysWatch Terminal Diagnostics
> Skill by [ara.so](https://ara.so) — Daily 2026 Skills collection.
SysWatch is a single-host system diagnostics TUI written in Rust for macOS and Linux. It consolidates what you'd normally get from `htop`, `iostat`, `vm_stat`, `powermetrics`, `launchctl`, and many other tools into twelve navigable tabs, with plain-English anomaly detection and a session-wide scrubber.
---
## Install
```bash
git clone https://github.com/matthart1983/syswatch.git && cd syswatch
cargo build --release
./target/release/syswatch
```
**Requirements:** Rust 1.75+. No extra system dependencies on Linux. macOS links against system frameworks automatically.
> Crates.io, Homebrew, and pre-built binaries are planned for the v0.1 release.
---
## Running SysWatch
```bash
# Default — 1 Hz refresh
./target/release/syswatch
# 2 Hz refresh (500 ms tick)
syswatch --tick 500
# Boot directly into a specific tab
syswatch --tab procs
syswatch --tab cpu
syswatch --tab insights
```
---
## Key Bindings
```text
1 2 3 4 5 6 7 8 9 Overview / CPU / Mem / Disks / FS / Procs / GPU / Power / Services
0 - + Net / Timeline / Insights
Tab / Shift-Tab Cycle tabs forward / backward
↑ / ↓ Select row (Procs, Services tabs)
s Cycle sort column (Procs, Services tabs)
← / → Scrub session backward / forward (Timeline tab)
Home / End Jump to oldest sample / return to live
p Pause collection
q / Ctrl-C Quit
```
---
## Tabs Reference
| Key | Tab | Data Source / Replaces |
|-----|-----|------------------------|
| `1` | Overview | Dashboard of all subsystems |
| `2` | CPU | `htop` CPU panel, `mpstat`, `top -d` |
| `3` | Memory | `free`, `vm_stat`, `htop` mem panel |
| `4` | Disks | `iostat`, `iotop` (aggregate) |
| `5` | Filesystems | `df -h`, `df -i`, `mount` |
| `6` | Procs | `htop`, `ps auxf`, `pstree` |
| `7` | GPU | `ioreg AGXAccelerator` / `/sys/class/drm` |
| `8` | Power | `pmset`, `ioreg AppleSmartBattery` / `/sys/class/power_supply` |
| `9` | Services | `launchctl list` / `systemctl list-units` |
| `0` | Net | `nettop`, `iftop` |
| `-` | Timeline | Session log + scrubber |
| `+` | Insights | Plain-English anomaly cards |
---
## Architecture Overview
```text
src/
├── main.rs CLI entry point + arg parsing
├── app.rs Event loop, tab state, scrub plumbing
├── collect/
│ ├── collector.rs sysinfo-backed CPU/Mem/Procs + dispatch
│ ├── gpu.rs system_profiler / sysfs DRM
│ ├── power.rs ioreg / pmset / sysfs power_supply
│ ├── services.rs launchctl / systemctl
│ └── ring.rs Bounded history ring + nth_back for scrubbing
├── insights/ Pure functions over (History, &Snapshot)
├── tabs/ One file per tab — thin renderers over the model
└── ui/
├── chrome.rs Header, tab bar, footer
├── palette.rs Single color source of truth
└── widgets.rs block_bar, sparkline, panel helpers
```
**Refresh model:**
- **1 Hz** fast loop: CPU, Memory, Procs, Net, IO
- **5 s** slow loop: Power, Services (subprocess-heavy on macOS)
- CPU budget target: < 0.5% at idle
---
## Extending SysWatch: Adding a Collector
Collectors live in `src/collect/`. Each one populates a typed `Snapshot` struct and is called from `collector.rs`.
```rust
// src/collect/my_subsystem.rs
use crate::collect::ring::Ring;
#[derive(Debug, Clone)]
pub struct MySnapshot {
pub value: f64,
pub label: String,
}
pub struct MyCollector {
history: Ring<MySnapshot>,
}
impl MyCollector {
pub fn new(capacity: usize) -> Self {
Self {
history: Ring::new(capacity),
}
}
pub fn collect(&mut self) -> MySnapshot {
// Replace with real data collection
let snap = MySnapshot {
value: 42.0,
label: "example".to_string(),
};
self.history.push(snap.clone());
snap
}
/// Returns the nth most recent snapshot (for scrubbing).
pub fn nth_back(&self, n: usize) -> Option<&MySnapshot> {
self.history.nth_back(n)
}
}
```
Register it in `collector.rs` and call `collect()` in the fast or slow loop as appropriate.
---
## Extending SysWatch: Adding a Tab Renderer
Tab renderers live in `src/tabs/`. They receive the current (or scrubbed) snapshot and render into a `ratatui` `Frame`.
```rust
// src/tabs/my_tab.rs
use ratatui::{
layout::{Constraint, Direction, Layout, Rect},
style::{Color, Style},
widgets::{Block, Borders, Paragraph},
Frame,
};
use crate::collect::my_subsystem::MySnapshot;
pub fn render(f: &mut Frame, area: Rect, snap: &MySnapshot) {
let block = Block::default()
.title(" My Tab ")
.borders(Borders::ALL)
.border_style(Style::default().fg(Color::Cyan));
let text = Paragraph::new(format!(
"Value: {:.2}\nLabel: {}",
snap.value, snap.label
))
.block(block);
f.render_widget(text, area);
}
```
Wire it into `app.rs`'s tab dispatch match arm and add the tab label to `ui/chrome.rs`.
---
## Extending SysWatch: Adding an Insight
Insights are pure functions in `src/insights/`. They take history + the latest snapshot and return zero or more anomaly cards.
```rust
// src/insights/my_insight.rs
use crate::collect::my_subsystem::MySnapshot;
#[derive(Debug, Clone)]
pub struct InsightCard {
pub title: String,
pub body: String,
pub suggested_tab: &'static str,
}
pub fn check(snap: &MySnapshot) -> Vec<InsightCard> {
let mut cards = vec![];
if snap.value > 90.0 {
cards.push(InsightCard {
title: "High value detected".to_string(),
body: format!(
"Current value is {:.1}, which exceeds the 90.0 threshold.",
snap.value
),
suggested_tab: "my_tab",
});
}
cards
}
```
Register the check in `insights/mod.rs` so it's included in the Insights tab and Overview badge.
---
## Using the Ring Buffer (Session Scrubbing)
The `Ring<T>` type in `src/collect/ring.rs` is the backbone of session scrubbing. Any collector that wraps its history in a `Ring` gets scrubbing for free when the tab renderer calls `nth_back`.
```rust
use crate::collect::ring::Ring;
// Create a ring holding 3600 samples (1 hour at 1 Hz)
let mut ring: Ring<f64> = Ring::new(3600);
// Push a new sample each tick
ring.push(42.0);
// In scrub mode, app.rs tracks `scrub_offset: usize`
// 0 = live, N = N ticks in the past
let scrub_offset = 5; // 5 seconds ago
if let Some(val) = ring.nth_back(scrub_offset) {
println!("Value 5s ago: {}", val);
}
```
In `app.rs`, the `←`/`→` keys increment/decrement `scrub_offset`, and every tab renderer receives the offset so they all show the same point in time.
---
## Optional Cargo Features
```toml
# Cargo.toml — enable NVIDIA GPU stats (requires NVML / nvidia-smi)
[features]
gpu-nvidia = ["nvml-wrapper"]
# Enable SMART disk health (requires smartctl in PATH)
smart = []
```
Build with a feature:
```bash
cargo build --release --features gpu-nvidia
cargo build --release --features smart
cargo build --release --features gpu-nvidia,smart
```
---
## Platform Notes
### macOS
- GPU utilization and used memory on Apple Silicon: available **without sudo** via `ioreg AGXAccelerator PerformanceStatistics`.
- Fan speeds, per-component power, GPU temperature: require `sudo powermetrics`. SysWatch shows available data and displays a one-line note where sudo is needed — it never prompts.
- Thermal zone temps require IOReport private FFI (deferred).
### Linux
- Thermal zones: available for free via sysfs (`/sys/class/thermal/`).
- GPU data: read from `/sys/class/drm`.
- Power supply: `/sys/class/power_supply`.
- No elevated privileges required for core functionality.
---
## Common Patterns
### Checking live vs. scrubbed state in a tab
```rust
// In app.rs, scrub_offset == 0 means "live"Related in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.