SLOPSHOPPER

context-alarm

Warns when the context window passes a threshold (80% by default) and suggests /save-context before compaction

newcommandtoaststatusprompttimer
★ 5v0.1.1MITupdated 2026-10-05mutlumehmet/claude-plugins/plugins/context-alarm
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · context-alarm
› fix the failing auth test and add an audit log call ⏺ Read(src/auth.ts) ⎿ Read 6 lines ⏺ Update(src/auth.ts) ⎿ Added 2 lines, removed 1 line ⏺ Bash(bun test) ⎿ 3 pass, 1 fail ● Done. refresh now rejects expired claims and logs an audit event. ✻ Worked for 42s · done 4:20 PM › /context-alarm ⎿ context-alarm: Context: 49% · warns at 80% and 90% · save-context since the warning: not run ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts
README

context-alarm

A Claude Code mod that warns when the context window fills up and suggests saving the session's decisions with /save-context before automatic compaction summarises them away. Part of claude-plugins.

What it does

  • At 80% (the warn_percent setting, 50 to 95): a notice, a status line under the prompt (Context 83% · /save-context suggested) and /save-context suggested in the empty prompt box.
  • 10 points higher: one more notice that automatic compaction is close.
  • Before an automatic compaction without a save: a last notice. The compaction still runs.
  • After /save-context runs: the suggestions stop and the status line says so.
  • /context-alarm: shows how full the context is and the thresholds.

It installs project-workflow from the same marketplace as a dependency, which provides /save-context.

What it reads and does

  • Reads: how full the context window is, whether /save-context ran, and when an automatic compaction starts.
  • Does: notices, a status line under the prompt, and /save-context suggested in the empty prompt box. It never runs save-context itself.
  • Hooks: skill.prompt (save-context only) notes that it ran and passes the skill's prompt on unchanged. session.compact only warns; the compaction still runs. command.run answers its own /context-alarm command and no other.
  • Privacy: see PRIVACY.md.
  • Never: sends anything anywhere (no network calls, no telemetry).

Install

Needs Claude Code 2.1.287 or later (mods). Tested on 2.1.289.

/plugin marketplace add mutlumehmet/claude-plugins
/plugin install context-alarm@mehmetmutlu

A mod runs inside Claude Code with your permissions. Read the code before you install any mod.

Known gaps

  • On the turn that crosses the threshold the prompt suggestion can be missed (the new percent arrives after the turn ends); the notice and status line still show.
  • If one turn jumps past both thresholds, only the second notice shows.
  • Nothing is drawn in the VS Code chat panel.

About

Mehmet Mutlu: work that's shipped, at mehmetmutlu.dev

Made by Mehmet Mutlu, part of claude-plugins. More of his work at mehmetmutlu.dev.

License

MIT

Source 1 files
hooks/register.js 103 lines
1// context-alarm: watches how full the context window is. Past the threshold
2// (80% by default) it pins a status line, shows a toast and puts /save-context
3// in the empty prompt box as a dim suggestion (Tab takes it). Ten points later
4// it warns once more. Before an automatic compaction it warns if save-context
5// has not run since the first warning. It never runs save-context itself and
6// never stops a compaction.
7
8const REARM_BELOW = 10 // points under the threshold that count as "emptied again"
9
10let warnAt = 80
11let level = 0 // 0 quiet, 1 warned at warnAt, 2 warned at warnAt + 10
12let percent = null
13let saved = false // save-context ran since the first warning
14
15export function register(on, options) {
16  const n = Number.parseInt(String(options?.warn_percent ?? '80'), 10)
17  warnAt = Number.isFinite(n) && n >= 50 && n <= 95 ? n : 80
18
19  on('session.start', async ($, e, next) => {
20    await $.command.register({
21      name: 'context-alarm',
22      description: 'Show how full the context is and when context-alarm warns',
23    })
24    return next(e)
25  })
26
27  on('command.run', { command: 'context-alarm' }, async () => {
28    const now = percent === null ? 'not measured yet' : percent + '%'
29    return {
30      text:
31        'Context: ' + now + ' · warns at ' + warnAt + '% and ' + (warnAt + 10) + '%' +
32        ' · save-context since the warning: ' + (saved ? 'ran' : 'not run'),
33    }
34  })
35
36  // Every change in the fill. The main conversation's window only: subagents
37  // have their own and do not fire this.
38  on('session.measure', async ($, e, next) => {
39    if (e.changed.includes('context') && typeof e.context.percent === 'number') {
40      await onPercent($, e.context.percent)
41    }
42    return next(e)
43  })
44
45  // After the turn the prompt box is free, so the suggestion can show
46  on('turn.complete', async ($, e, next) => {
47    const result = await next(e)
48    if (!e.agentId && level > 0 && !saved) {
49      $.clock.after(300, () => suggest($))
50    }
51    return result
52  })
53
54  on('skill.prompt', { skill: 'save-context' }, async ($, e, next) => {
55    saved = true
56    if (percent !== null) $.ui.status(statusText())
57    return next(e)
58  })
59
60  on('session.compact', async ($, e, next) => {
61    if (!e.agentId && e.trigger === 'auto' && level > 0 && !saved) {
62      $.ui.toast('Auto compaction is starting and save-context has not run in this session. The summary may lose some details.', { timeoutMs: 12000 })
63    }
64    return next(e)
65  })
66}
67
68async function onPercent($, p) {
69  percent = p
70
71  if (p < warnAt - REARM_BELOW) {
72    // Emptied, usually by a compaction or /clear: start over
73    if (level > 0) $.ui.status(undefined)
74    level = 0
75    saved = false
76    return
77  }
78
79  if (p >= warnAt + 10 && level < 2) {
80    level = 2
81    $.ui.toast('Context ' + p + '% full. Auto compaction is close: run /save-context now.', { timeoutMs: 12000 })
82  } else if (p >= warnAt && level < 1) {
83    level = 1
84    $.ui.toast('Context ' + p + '% full. Time to save decisions with /save-context.', { timeoutMs: 10000 })
85  }
86
87  if (level > 0) $.ui.status(statusText())
88}
89
90function statusText() {
91  return saved
92    ? 'Context ' + percent + '% · save-context ran'
93    : 'Context ' + percent + '% · /save-context suggested'
94}
95
96async function suggest($) {
97  try {
98    await $.prompt.suggest({ text: '/save-context' })
99  } catch {
100    // Headless, or the box is busy: the status line still says it
101  }
102}
103