caproom 0.7.1 → 0.7.2

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/README.md CHANGED
@@ -94,6 +94,24 @@ caproom init claude --limit 6144 --grace 10 >> ~/.zshrc && source ~/.zshrc
94
94
 
95
95
  This appends a shell function that wraps `claude` through the watchdog backend (host-native — no Docker isolation, so the wrapped command keeps its normal filesystem/auth/PATH access) and an alias so plain `claude` picks it up. Per-shell override without editing the rc file: `CAPROOM_LIMIT_MB=8192 claude ...`. Works for any command, not just `claude` — `caproom init npm --limit 2048` wraps `npm` the same way.
96
96
 
97
+ ## setup / bind — shell integration for every terminal
98
+
99
+ `caproom setup` binds headroom management to your shells at the **shell layer**, so it works in any terminal (Terminal.app, iTerm2, Ghostty, Windows Terminal) without touching terminal-specific config:
100
+
101
+ ```bash
102
+ caproom setup # bind zsh/bash + fish (Windows: PowerShell $PROFILE)
103
+ caproom setup --uninstall # remove rc markers; backups and integration files stay
104
+ ```
105
+
106
+ What it does:
107
+
108
+ - Writes one integration file per shell under `~/.caproom/` (`shell.sh`, `shell.fish`, `shell.ps1`) and adds a 3-line marker block (`# >>> caproom >>>`) to your rc files. Idempotent; timestamped `.bak` backups are made before first patch.
109
+ - Adds a **headroom warning on every prompt** — once per minute, only when free memory drops below `CAPROOM_HEADROOM_WARN` percent (default 20). Never blocks, never modifies your prompt text.
110
+ - Optional **auto-wrap**: `export CAPROOM_AUTO_WRAP="claude,codex,opencode"` in your rc creates `<cmd>_capped` twins that run through the watchdog with `CAPROOM_LIMIT_MB`. Bare command names are shadowed **only** if you also set `CAPROOM_AUTO_ALIAS=1` — caproom never hijacks an unwrapped command by default.
111
+ - Optional login daemon: `caproom setup --guard --threshold 15` installs a LaunchAgent (macOS) or systemd user unit (Linux) running `caproom guard` across all terminals.
112
+
113
+ npm install never touches your rc files — postinstall prints a hint, binding is always explicit. Undo any time: `caproom setup --uninstall`.
114
+
97
115
  ### `caproom top` — process-tree inventory for agents
98
116
 
99
117
  Read-only snapshot of every process tree you own, sorted by tree RSS. `--json` output is a **stable contract**: `schema` version field, additive changes only.
package/bin/caproom CHANGED
@@ -743,6 +743,21 @@ collect_tree() {
743
743
  run_watchdog() {
744
744
  echo "caproom: watchdog backend (host-native), limit=${LIMIT_MB}m poll=${INTERVAL}s (process-tree RSS)" >&2
745
745
  local limit_kb=$(( LIMIT_MB * 1024 ))
746
+ # Terminal hygiene: a TUI child (opencode, claude, ...) puts the tty in
747
+ # raw + mouse-tracking mode. If WE kill it, it never restores, and the
748
+ # user's shell then prints mouse reports like [[<35;25;15M as garbage.
749
+ # Snapshot termios before spawn so we can put the tty back after a kill.
750
+ local saved_stty=""
751
+ if [[ -t 0 ]]; then
752
+ saved_stty=$(stty -g </dev/tty 2>/dev/null || true)
753
+ fi
754
+ restore_tty() {
755
+ # Only on OUR kill — a clean exit already restored its own state, and
756
+ # re-emitting resets there could clobber whatever the NEXT program drew.
757
+ [[ -n "$saved_stty" ]] && stty "$saved_stty" </dev/tty 2>/dev/null \
758
+ || { [[ -t 0 ]] && stty sane </dev/tty 2>/dev/null || true; }
759
+ printf '\033[?1049l\033[?1000l\033[?1002l\033[?1003l\033[?1006l\033[?2004l\033[?25h' >/dev/tty 2>/dev/null || true
760
+ }
746
761
  "$@" &
747
762
  local pid=$!
748
763
  local exit_code=0
@@ -776,10 +791,12 @@ run_watchdog() {
776
791
  if [[ "$sweep" -gt 0 ]]; then
777
792
  echo "caproom: SIGKILLed ${sweep} survivor(s) after grace — exit 137" >&2
778
793
  wait "$pid" 2>/dev/null || true
794
+ restore_tty
779
795
  exit 137
780
796
  fi
781
797
  wait "$pid" 2>/dev/null || exit_code=$?
782
798
  echo "caproom: pid $pid exited cleanly (code $exit_code) during grace period" >&2
799
+ restore_tty
783
800
  exit "$exit_code"
784
801
  fi
785
802
  sleep "$INTERVAL"
package/bin/caproom.ps1 CHANGED
@@ -504,18 +504,23 @@ function Invoke-Watch {
504
504
  # Same NDJSON contract as the POSIX watcher (schema:1 events on stdout
505
505
  # under --json). Explicit pids only; naming the pid IS the opt-in for
506
506
  # --auto-park, same rule as POSIX.
507
+ # NOTE: args arrive via the named -Rest parameter, NOT $args — splatting
508
+ # into $args proved unreliable here (every element read back as null,
509
+ # yielding pids [0,0,0,0] and a busy-spinning interval-0 loop).
510
+ param([string[]]$Rest)
507
511
  $thresholdMb = 2048; $intervalSec = 5.0; $auto = $false; $wake = -1.0; $json = $false
508
512
  $targets = New-Object System.Collections.Generic.List[int]
509
- for ($i = 0; $i -lt $args.Count; $i++) {
510
- switch ($args[$i]) {
511
- '--threshold-mb' { $thresholdMb = [int]$args[$i + 1]; $i++ }
512
- '--interval' { $intervalSec = [double]$args[$i + 1]; $i++ }
513
+ for ($i = 0; $i -lt $Rest.Count; $i++) {
514
+ switch ($Rest[$i]) {
515
+ '--threshold-mb' { $thresholdMb = [int]$Rest[$i + 1]; $i++ }
516
+ '--interval' { $intervalSec = [double]$Rest[$i + 1]; $i++ }
513
517
  '--auto-park' { $auto = $true }
514
- '--auto-wake-free-pct' { $wake = [double]$args[$i + 1]; $i++ }
518
+ '--auto-wake-free-pct' { $wake = [double]$Rest[$i + 1]; $i++ }
515
519
  '--json' { $json = $true }
520
+ '--' { }
516
521
  default {
517
- try { $targets.Add([int]$args[$i]) }
518
- catch { [Console]::Error.WriteLine("caproom: unknown watch arg $($args[$i])"); exit 1 }
522
+ try { $targets.Add([int]$Rest[$i]) }
523
+ catch { [Console]::Error.WriteLine("caproom: unknown watch arg $($Rest[$i])"); exit 1 }
519
524
  }
520
525
  }
521
526
  }
@@ -523,6 +528,7 @@ function Invoke-Watch {
523
528
  [Console]::Error.WriteLine('usage: caproom watch [--threshold-mb <mb>] [--interval <sec>] [--auto-park] [--auto-wake-free-pct <pct>] [--json] <pid...>')
524
529
  exit 1
525
530
  }
531
+ if ($intervalSec -lt 0.5) { $intervalSec = 0.5 }
526
532
 
527
533
  function Emit([object]$Ev) {
528
534
  [Console]::Out.WriteLine((ConvertTo-Json -Compress -Depth 6 -InputObject $Ev))
@@ -683,7 +689,7 @@ switch ($args[0]) {
683
689
  exit 0
684
690
  }
685
691
  'watch' {
686
- Invoke-Watch @($args | Select-Object -Skip 1)
692
+ Invoke-Watch -Rest @($args | Select-Object -Skip 1)
687
693
  exit 0
688
694
  }
689
695
  'park' {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "caproom",
3
- "version": "0.7.1",
3
+ "version": "0.7.2",
4
4
  "description": "Memory-cap any command (AI coding agents, builds, background jobs) on macOS, Linux, and Windows — real enforcement via Docker cgroups, Windows Job Objects, or a polling watchdog, plus park/wake to reclaim idle process memory without killing.",
5
5
  "bin": {
6
6
  "caproom": "bin/caproom.js",