Lets SI Everywhere reply to this Claude Code session from your phone, the web or your glasses

Small Claude Code mods: TypeScript hook modules that run before, after or instead of what Claude Code does.
| Mod | What it does |
|---|---|
turn-timestamp | Ends every turn with ✻ 2026-10-03 19:05 CDT · 3s · 1 tool call, so scrollback in multi-day sessions shows when each turn finished. In the terminal it replaces Claude Code's own end-of-turn line (✻ Churned for 1s); in the desktop Code tab, which draws no such line, it goes under Claude's last reply block. Display only. |
ci-status | /ci opens a pane listing the repo's latest GitHub Actions runs (via gh), refreshes every 30s, shows CI: N running in the status line and a toast when a run finishes. |
sisomewhere-agents | Classic command hooks (no TypeScript) that record each session's state (working and on which tool, needs you, done) to ~/.config/sisomewhere/agents/, one file per session. The SI Somewhere daemon shows them on the desk display's Agents page. The same script feeds Codex, Grok and Antigravity sessions: python3 mods/sisomewhere-agents/install_other_tools.py adds it to their hook configs (--uninstall removes it). Silent: never blocks a tool call. Superseded on machines that run the SI Somewhere Go agent (sisomewhere install, in that repo's agent/), which hooks every harness itself and switches this plugin off. |
sisomewhere-usage | Saves Claude Code's plan limits (the 5-hour and weekly windows: percent used, when each resets) to ~/.config/sisomewhere/usage/ whenever a window moves, one file per Claude config folder. The SI Somewhere agent sends them to app.sisomewhere.cc and the desk display. Claude Code hands these figures to mods only, so this needs no login, token or API call. No UI. |
Installs every mod in setup/mods.conf (mine and the community ones I use) and restores their saved settings, such as the neon-cyber skin:
git clone https://github.com/shissncg/claude-mods ~/Dev/claude-mods
~/Dev/claude-mods/setup.sh
./setup.sh --dev registers this checkout as the marketplace instead of GitHub, so edits under mods/ load without pushing../setup.sh save copies this machine's mod settings into setup/store/; commit them to carry them to other machines.setup/mods.conf. Running the script again only installs what's missing.Plugins turned on at claude.ai sync by themselves and don't need to be listed.
To install just one of mine:
claude plugin marketplace add shissncg/claude-mods
claude plugin install turn-timestamp@shissncg-mods
Or try one for a session without installing:
claude --plugin-dir mods/turn-timestamp --plugin-dir mods/ci-status
Check one with claude plugin validate mods/<name>. Mods need a recent Claude Code build: older CLIs report "session.start" is not an event.
hooks/register.ts 63 lines1import type { Register } from 'claude-code'
2
3// SI Everywhere replies: what you send a session from your phone, the web or
4// your glasses becomes its next prompt. The SI Everywhere agent (`sisomewhere
5// actions on`) checks the reply came from one of your linked devices,
6// decrypts it and appends it to this session's inbox file; this mod reads the
7// file every 2 seconds and submits each line as a prompt, which Claude Code
8// queues until the session is idle.
9//
10// ~/.config/sisomewhere/inbox/<session id>.ready written at start: replies can come here
11// ~/.config/sisomewhere/inbox/<session id>.jsonl {"text": "...", "at": 1791600000.1} per line
12//
13// Interactive sessions only: a closed session gets its reply through a
14// background `claude -p --resume`, which the agent starts itself.
15
16const POLL_MS = 2000
17
18/** The lines' texts, in order; blank and broken lines are skipped. */
19export function texts(inbox: string): string[] {
20 const out: string[] = []
21 for (const line of inbox.split('\n')) {
22 if (!line.trim()) continue
23 try {
24 const { text } = JSON.parse(line) as { text?: unknown }
25 if (typeof text === 'string' && text.trim()) out.push(text)
26 } catch {
27 // a half-written line: the agent writes whole lines, so skip it
28 }
29 }
30 return out
31}
32
33export const register: Register = on => {
34 on('session.start', async ($, e, next) => {
35 const result = await next(e)
36 if (!e.isInteractive) return result
37 try {
38 const home = await $.env.get('HOME')
39 if (!home) return result
40 const id = await $.session.id()
41 const dir = `${home}/.config/sisomewhere/inbox`
42 await $.fs.write(`${dir}/${id}.ready`, `${Math.floor(Date.now() / 1000)}\n`)
43 const inbox = `${dir}/${id}.jsonl`
44 $.clock.every(POLL_MS, async () => {
45 let body = ''
46 try {
47 body = String(await $.fs.read(inbox))
48 } catch {
49 return // nothing sent yet
50 }
51 if (!body.trim()) return
52 await $.fs.write(inbox, '')
53 for (const text of texts(body)) {
54 void $.prompt.submit({ text })
55 }
56 })
57 } catch {
58 // replies just won't arrive here; the session itself is unaffected
59 }
60 return result
61 })
62}
63