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

Small Claude Code mods.
/plugin marketplace add PhilRoli/claude-mods
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.
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.
hooks/register.ts 56 lines1import 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