memmon
Monitor and act on opencode memory health. Run this skill when you notice opencode feeling slow, suspect a memory leak, want a routine memory check, or are asked to check memory usage. Triggers on phr
SKILL.mdopencode/dot-config/opencode/skills/memmon/SKILL.md
GitHubmemmon — opencode memory monitor
Samples and analyzes the opencode process forest for memory leaks, then acts on the verdict. This skill closes the loop: it does not just report — it takes one logged action based on what it finds.
Tool location
~/.config/opencode/tools/memmon/
├── sampler.sh — take one memory snapshot
├── analyzer.py — classify samples as LEAK / SLOW / PLATEAU / TRANSIENT
├── loop.sh — start / stop / status / analyze the background sampler
Runtime data: ~/.local/share/opencode-memmon/ (samples.csv, loop.log, loop.pid)
Step 1 — Check sampler status
bash ~/.config/opencode/tools/memmon/loop.sh status
- If RUNNING: proceed to Step 2 (data exists).
- If not running: take one manual snapshot first, then proceed.
bash ~/.config/opencode/tools/memmon/sampler.sh
Step 2 — Run the analyzer
Always use --json for machine-readable output:
python3 ~/.config/opencode/tools/memmon/analyzer.py \
--samples ~/.local/share/opencode-memmon/samples.csv \
--summary ~/.local/share/opencode-memmon/summary.csv \
--json
Parse the JSON. Apply the decision order in Step 3 strictly — do not infer all-clear from has_leak:false alone.
Before running the analyzer: check whether opencode is currently running:
pgrep -x opencode || pgrep -f "opencode"
If no opencode PIDs exist, go directly to STOP — do not run the analyzer on potentially stale data.
Step 3 — Act / Ask / Stop
Decision order is strict. Evaluate top-to-bottom and stop at the first matching condition.
STOP — opencode not running
If no live opencode PIDs were found in Step 2, skip silently and log:
memmon [<timestamp>]: STOP — no opencode processes found. Skipping analysis of existing samples to avoid stale-data alerts.
Do not run the analyzer. Do not post Slack. Do not alert.
ASK — Ambiguous or degraded data
Escalate to the human when any of these are true:
samples.csvis missing, empty, or has fewer than 3 rows- Analyzer exits with an unexpected error (not exit 0 or exit 2)
- Two consecutive SLOW verdicts on the same role (check
actions.jsonlfor prior SLOW on this role) - LEAK on
opencode-tuioropencode-webwith slope > 200 MiB/hr (abnormally high — may indicate sampler noise rather than a real leak)
Action: Surface the raw analyzer output and ask:
memmon: uncertain result — [reason]. Raw output: <paste>. Should I take any action?
Log to actions.jsonl regardless:
{"ts":"<ISO>","verdict":"ASK","reason":"<reason>","action":"asked human"}
ACT — LEAK detected
Condition: has_leak:true OR any role verdict is "LEAK" in the JSON.
Action — two steps, both required:
- Attempt Slack DM to
@ricardo.santosviamcp_hub_slack__conversations_add_message:
*memmon LEAK detected* — <timestamp>
Role: <role>
Worst PID: <worst_pid>
Slope: <worst_slope> MiB/hr
Recommended action: restart opencode or investigate <role> process.
Loop status: <running/stopped>
Check the Slack tool response. If it returns an error or no
tsfield, do not claim success. Logslack_sent: false, error: <error>and escalate to ASK.Log to
actions.jsonl:
{"ts":"<ISO>","verdict":"LEAK","role":"<role>","slope_mb_hr":<n>,"pid":"<pid>","slack_sent":<true|false>,"action":"dm sent or escalated"}
Undo step: The Slack DM cannot be unsent once delivered, but can be deleted. No process is killed, no file is modified. If sent in error, delete it via Slack and log the correction.
ACT — SLOW drift
Condition: no LEAK, but any role verdict is "SLOW".
Action: Log a watch entry only — no Slack.
{"ts":"<ISO>","verdict":"SLOW","role":"<role>","slope_mb_hr":<n>,"pid":"<pid>","action":"logged watch entry"}
Also write a brief inline session note:
memmon [<timestamp>]: SLOW drift on <role> (<worst_slope> MiB/hr, pid <worst_pid>). Monitoring — no action required yet.
If the prior actions.jsonl entry for this role was also SLOW → escalate to ASK instead.
ACT — PLATEAU / TRANSIENT (all roles)
Condition: every role is "PLATEAU" or "TRANSIENT" and has_leak:false.
Action: Log all-clear only.
{"ts":"<ISO>","verdict":"PLATEAU","roles_checked":<n>,"samples":<n>,"action":"all clear"}
Brief inline note:
memmon [<timestamp>]: all clear — no leak detected. Roles: <list>. Samples: <n>.
Step 4 — Durable audit log
Every run appends one JSONL line to:
~/.local/share/opencode-memmon/actions.jsonl
echo '{"ts":"<ISO>","verdict":"<V>","action":"<a>"}' >> ~/.local/share/opencode-memmon/actions.jsonl
This file is the real audit trail. The inline session note is a convenience mirror only — ephemeral and not the source of truth.
To check for consecutive SLOW on a role before acting:
tail -5 ~/.local/share/opencode-memmon/actions.jsonl | grep '"verdict":"SLOW"' | grep '"role":"<role>"'
If the previous entry for that role was also SLOW → escalate to ASK.
Worked example
$ python3 analyzer.py --json
{
"has_leak": false,
"roles": {
"opencode-tui": {"verdict": "PLATEAU", "worst_slope_mb_hr": 1.2, ...},
"mcphub": {"verdict": "SLOW", "worst_slope_mb_hr": 8.4, ...}
}
}
→ SLOW on mcphub: log watch entry, no Slack. If mcphub shows SLOW next run too → ASK.
Thresholds (env overrides)
| Env var | Default | Meaning |
|---|---|---|
MEMMON_LEAK_MB_HR |
60 | MiB/hr slope → LEAK |
MEMMON_SLOW_MB_HR |
6 | MiB/hr slope → SLOW |
MEMMON_MIN_SAMPLES |
3 | Minimum samples to classify |
MEMMON_MIN_LEAK_SAMPLES |
8 | Minimum samples to call LEAK |
MEMMON_WINDOW |
3 | Window-min smoothing width |