A Restart backend button in a sidebar pane, active only in the ULTRON_cBrain folder.

<img width="391" height="379" alt="image" src="https://github.com/user-attachments/assets/6a3cb2d9-57cb-47e2-84c0-6cfc547499c3" />
Small mods for Claude Code: a live sidebar with one-click buttons for the things you do every session, and a repo picker. Works on Windows, macOS and Linux.
| Mod | What you get |
|---|---|
| sidebar | Model badge, context-window gauge, session cost, a 5-hour / weekly usage dial, and buttons for New chat, Commit & Push and Handoff |
| commit-push | One click to stage, commit (with a Claude-written message) and push |
| handoff | One click to save a handoff.md, clear the chat, and continue from it |
| repo | Pick a GitHub repo, clone it if needed, and move the session into it |
| restart-ultron-backend | A Restart backend button in a sidebar pane, active only in the ULTRON_cBrain folder |
In Claude Code:
/plugin marketplace add erte-del/claude-mods
/plugin install sidebar@claude-mods
/plugin install repo@claude-mods
/reload-plugins
Or from a terminal:
claude plugin marketplace add erte-del/claude-mods
claude plugin install sidebar@claude-mods --scope user
claude plugin install repo@claude-mods --scope user
Installing sidebar also installs commit-push and handoff, because its buttons call them. repo is separate, since it opens a picker when a session starts; skip it if you don't want that.
Start a new session or run /reload-plugins and the sidebar appears. It docks beside the transcript on terminals 110 columns or wider, and otherwise shows as a bar above the prompt.
Requirements: Claude Code and git. repo also uses the GitHub CLI (gh, signed in with gh auth login) to list your repos.
Live status, redrawn after every turn:
Buttons, top to bottom: + New chat runs /clear. ⇡ Commit & Push and ⇄ Handoff run the mods below. ↻ Reload plugins runs /reload-plugins.
Click ⇡ Commit & Push, or type /commit-push.
git add -A).fix: handle empty repo list in picker. If that fails it falls back to naming the changed files.git push -u origin <branch>.It never force-pushes. It stops and leaves it to you when the branch is behind its upstream, there are unresolved merge conflicts, a merge or rebase is in progress, or HEAD is detached. Your files are only staged when it gets as far as committing.
It works on whatever folder the session is in, so it follows repo.
Click ⇄ Handoff, or type /handoff. Use it when your context is getting full.
handoff.md in the project root: what you're doing, current state, the exact next step, decisions made, gotchas and open questions. An existing handoff.md is overwritten./clear.handoff.md and carry on from its next step.If you interrupt, the turn errors, or Claude finishes without writing the file, it stops without clearing anything. The prompt is in mods/handoff/hooks/register.tsx if you want to change what goes in the file.
Opens a picker when a session starts with no repo chosen, or type /repo.
list to see them all, then a number to pick.owner/repo, or a git URL.~/repos, ~/Documents, ~/Desktop, ~/code, ~/projects, ~/dev, ~/src, ~/GitHub and a few more) and clones the repo if it can't find one.Change these in /config (each mod's settings are listed under its name).
| Mod | Setting | Default | What it does |
|---|---|---|---|
| sidebar | budgetUsd | 5 | The session cost that fills the cost bar |
| sidebar | ascii | off | Draw bars with # and - and skip the dial, for terminals that can't show block characters |
| repo | owner | your gh login | Whose repos to list: another user or an organization |
| repo | cloneDir | ~/repos | Where missing repos are cloned |
| repo | openOnStart | on | Open the picker when a session starts with no active repo |
claude plugin marketplace update claude-mods
claude plugin uninstall sidebar@claude-mods
Each mod lives in its own folder under mods/ with its tests next to the code (claude plugin test mods/<name>).
hooks/register.tsx 62 lines1import { atom, read, update } from 'claude-code'
2import type { Register } from 'claude-code'
3
4const PANE = 'restart-ultron-backend'
5const FOLDER = 'ULTRON_cBrain'
6const status = atom({ plugin: 'restart-ultron-backend', key: 'status' } as const, 'Idle.')
7
8// Reload through launchd when autostart is on; else stop and start via the project's own scripts.
9const RESTART = `
10ROOT="$PWD"
11AGENT="gui/$(id -u)/com.ultron.backend"
12if launchctl print "$AGENT" >/dev/null 2>&1; then
13 launchctl kickstart -k "$AGENT" || exit 1
14else
15 "$ROOT/scripts/stop_ultron.sh"
16 sleep 1
17 "$ROOT/scripts/start_ultron.sh" --no-open || exit 1
18fi
19for _ in $(seq 1 60); do curl -s -m 1 http://127.0.0.1:8000/health >/dev/null 2>&1 && exit 0; sleep 0.5; done
20echo "backend did not come back on :8000" >&2; exit 1
21`
22
23const inUltron = async ($: { session: { root: () => Promise<string> } }) =>
24 (await $.session.root()).split('/').pop() === FOLDER
25
26export const register: Register = on => {
27 on('session.start', async ($, e, next) => {
28 if (await inUltron($)) {
29 await $.command.register({ name: 'restart-backend', description: 'Restart the Ultron backend' })
30 void $.ui.open({ id: PANE, title: 'Ultron' })
31 }
32 return next(e)
33 })
34
35 on('ui.render', { component: 'Pane', requestId: PANE }, async ($, e) => {
36 const { Box, Text, Button } = $.ui.resolve(e)
37 if (!(await inUltron($))) return <Text dimColor>Only works in {FOLDER}.</Text>
38 const text = await read($, status)
39
40 return (
41 <Box flexDirection="column">
42 <Button
43 key="restart"
44 label="Restart backend"
45 onPress={async () => {
46 if (!(await inUltron($))) return
47 await update($, status, () => 'Restarting...')
48 const r = await $.process.run(['bash', '-c', RESTART], {
49 cwd: await $.session.root(),
50 timeoutMs: 90000,
51 })
52 await update($, status, () =>
53 r.exitCode === 0 ? 'Backend restarted.' : `Failed: ${r.stderr.trim().slice(-200) || r.exitCode}`,
54 )
55 }}
56 />
57 <Text dimColor>{text}</Text>
58 </Box>
59 )
60 })
61}
62types/index.d.ts 8 lines1export type RestartStatus = string
2
3declare module 'claude-code' {
4 interface PluginState {
5 'restart-ultron-backend': { status: RestartStatus }
6 }
7}
8