SLOPSHOPPER

train-pause

Tells every Claude Code session to finish up and pause shortly before your TrainTracker train arrives.

newstatusprompttimer
v0.1.0MITupdated 2026-10-12PhilRoli/claude-mods/plugins/train-pause
A shopper browsing a rack in a slop shop
README

claude-mods

Small Claude Code mods.

/plugin marketplace add PhilRoli/claude-mods

train-pause

For TrainTracker. Shortly before your tracked train arrives (default: 10 minutes), every session is told to stop starting new work, leave the code in a consistent state and report what's done and what remains, so you can switch locations or turn off wifi without cutting a session off mid-task.

/plugin install train-pause@claude-mods

Requires TrainTracker (≥ 1.3.0), which writes the arrival time to ~/.traintracker/arrival.json ({"arrival": <epoch seconds, delay included>}) while a train is tracked. Idle sessions pick up the instruction on their next prompt; the status line shows a countdown. Change the window with WRAP_UP_MINUTES in plugins/train-pause/hooks/register.ts.

session-limit-pause

Same idea for usage limits: once the 5-hour or weekly window is ≥ 95% used, the session is told to wrap up. A window that resets within 5 minutes is ignored. Needs a subscription (the engine reports no limits otherwise).

/plugin install session-limit-pause@claude-mods

Tune with WRAP_UP_PERCENT and IGNORE_IF_RESET_WITHIN_MIN in plugins/session-limit-pause/hooks/register.ts.

Source 1 files
hooks/register.ts 56 lines
1import type { EngineInterface, Register } from 'claude-code'
2
3// TrainTracker writes ~/.traintracker/arrival.json: { "arrival": <epoch seconds, delay included> }
4const WRAP_UP_MINUTES = 10
5const POLL_MS = 30_000
6
7export const minutesLeft = (raw: string, nowMs: number): number | undefined => {
8  try {
9    const arrival = JSON.parse(raw).arrival
10    return typeof arrival === 'number' ? (arrival * 1000 - nowMs) / 60_000 : undefined
11  } catch {
12    return undefined
13  }
14}
15
16async function check($: EngineInterface) {
17  try {
18    const home = await $.env.get('HOME')
19    const raw = await $.fs.read(`${home}/.traintracker/arrival.json`)
20    const left = minutesLeft(raw, await $.clock.now())
21    return left !== undefined && left > -5 && left <= WRAP_UP_MINUTES ? Math.max(0, Math.ceil(left)) : undefined
22  } catch {
23    return undefined
24  }
25}
26
27export const register: Register = on => {
28  on('session.start', async ($, e, next) => {
29    $.clock.every(POLL_MS, async () => {
30      const left = await check($)
31      $.ui.status(left === undefined ? undefined : `🚆 arrives in ${left} min — wrap up`)
32    })
33    return next(e)
34  })
35
36  on('prompt.compose', async ($, e, next) => {
37    const res = await next(e)
38    const left = await check($)
39    if (left === undefined) return res
40    return {
41      sections: [
42        ...res.sections,
43        {
44          id: `${$.plugin.name}:wrap-up`,
45          scope: 'session',
46          text:
47            `The user's train arrives in about ${left} minute(s) and they will lose their connection and must stop. ` +
48            `Do not start new work. Finish the current step only if it can be done in a minute or two, leave the code in a ` +
49            `consistent, working state (no half-applied edits), commit or note progress if appropriate, then stop and tell ` +
50            `the user briefly what is done and what remains so they can resume later.`,
51        },
52      ],
53    }
54  })
55}
56