SLOPSHOPPER

helper-desk

Purple progress taskbar above the prompt plus a side panel of sub-agents with colors, progress and token cost.

newpanebandguardcommandagents
v0.1.0no licenseupdated 2026-10-25ikentrock/starwars-claude-mods/helper-desk
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · helper-desk
│ ┃ Helper Desk ✕ › fix the failing auth test and add an audit log call │ ┃ No sub-agents yet. │ ⏺ 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 │ │ › /helper-desk │ ⎿ helper-desk: Helper Desk opened. │ │ ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts

Draws

Pane · Helper Desk
No sub-agents yet.
README

starwars-claude-mods

Nine local Claude Code mods (UI and hook plugins) with a light Star Wars flavor.

ModWhat it does
brain-dumpIntercepts long, unstructured prompts, asks clarifying questions, rewrites them into a structured prompt. !raw prefix skips it.
helper-deskProgress bar from the to-do list, plus a side panel for sub-agents (/helper-desk).
cache-clockPrompt-cache countdown, Keep Warm button, /keep-warm.
handoffGenerates a handoff prompt at 250k tokens, on cold cache, or on /handoff.
reflectOffers to save rules/preferences to memory, CLAUDE.md, or a second brain.
chat-inventoryZero-token "In This Chat" panel (/inventory).
switchboard/mods panel to toggle mods and hide panels.
power-cellsGauges above the prompt for context, the 5-hour limit, and a weekly pacing plan: a daily ration, work-day weighting, over/under-plan warnings. /power plan shows the week.
droid-consoleRestyles the whole UI as a droid console: personality palettes, transmission headers, droid spinner words, status band. /droid switches personality.

Status: experimental. They pass claude plugin validate and tsc, but have not been tested at runtime. Expect rough edges.

Install

Add the marketplace once, then install the mods you want (user scope makes them load in every session):

claude plugin marketplace add ikentrock/starwars-claude-mods
claude plugin install brain-dump@starwars-claude-mods --scope user
claude plugin install cache-clock@starwars-claude-mods --scope user
claude plugin install chat-inventory@starwars-claude-mods --scope user
claude plugin install droid-console@starwars-claude-mods --scope user
claude plugin install handoff@starwars-claude-mods --scope user
claude plugin install helper-desk@starwars-claude-mods --scope user
claude plugin install power-cells@starwars-claude-mods --scope user
claude plugin install reflect@starwars-claude-mods --scope user
claude plugin install switchboard@starwars-claude-mods --scope user

Or one at a time from a session prompt:

/plugin install <mod> --marketplace ikentrock/starwars-claude-mods

Update later with claude plugin marketplace update starwars-claude-mods.

Source 2 files
hooks/register.tsx 158 lines
1import { atom, read, update } from 'claude-code'
2import type { Register } from 'claude-code'
3
4import type { DeskAgent, DeskTodo } from '../types'
5
6const PANE = 'helper-desk'
7const PURPLE = '#a855f7'
8const PALETTE = ['#38bdf8', '#f59e0b', '#34d399', '#f472b6', '#f87171', '#a3e635', '#22d3ee']
9
10const todos = atom({ plugin: 'helper-desk', key: 'todos' } as const, [], {
11  shape: 'todos-1',
12})
13const agents = atom({ plugin: 'helper-desk', key: 'agents' } as const, [], {
14  shape: 'agents-1',
15})
16
17const bar = (fraction: number, width: number) => {
18  const full = Math.round(Math.max(0, Math.min(1, fraction)) * width)
19
20  return '█'.repeat(full) + '░'.repeat(width - full)
21}
22
23const compact = (n: number) => (n >= 1000 ? `${(n / 1000).toFixed(1)}k` : String(n))
24
25export const register: Register = on => {
26  on('session.start', async ($, e, next) => {
27    await $.command.register({
28      name: 'helper-desk',
29      description: 'Open the Helper Desk panel of sub-agents',
30    })
31
32    return next(e)
33  })
34
35  on('command.run', { command: 'helper-desk' }, async $ => {
36    await $.ui.open({ id: PANE, title: 'Helper Desk' })
37
38    return { text: 'Helper Desk opened.' }
39  })
40
41  on('session.end', async ($, e, next) => {
42    if (e.reason === 'clear') {
43      await update($, todos, () => [])
44      await update($, agents, () => [])
45    }
46
47    return next(e)
48  })
49
50  on('tool.call', { tool: 'TodoWrite' }, async ($, e, next) => {
51    const list: DeskTodo[] = e.todos.map(t => ({
52      content: t.content,
53      status: t.status,
54      activeForm: t.activeForm,
55    }))
56    await update($, todos, () => list)
57
58    return next(e)
59  })
60
61  on('tool.call', async ($, e, next) => {
62    const id = e.agentId
63    if (id !== undefined) {
64      await update($, agents, list =>
65        list.map(a => (a.id === id ? { ...a, tools: a.tools + 1 } : a)),
66      )
67    }
68
69    return next(e)
70  })
71
72  on('agent.spawn', async ($, e, next) => {
73    const started = await next(e)
74    const agent: DeskAgent = {
75      id: started.agentId ?? '',
76      name: e.description ?? e.subagentType ?? 'helper',
77      color: '',
78      tools: 0,
79      tokens: 0,
80      isDone: false,
81    }
82    await update($, agents, list => {
83      const color = PALETTE[list.length % PALETTE.length] ?? PURPLE
84
85      return [...list, { ...agent, color }].slice(-30)
86    })
87    void $.ui.open({ id: PANE, title: 'Helper Desk' })
88
89    return started
90  })
91
92  on('turn.complete', async ($, e, next) => {
93    const id = e.agentId
94    if (id !== undefined) {
95      const used = e.usage
96      const tokens = used ? used.input_tokens + used.output_tokens : 0
97      await update($, agents, list =>
98        list.map(a => (a.id === id ? { ...a, tokens: a.tokens + tokens, isDone: true } : a)),
99      )
100    }
101
102    return next(e)
103  })
104
105  // The purple taskbar right above the prompt box.
106  on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
107    const list = await read($, todos)
108    const helpers = await read($, agents)
109    if (e.props.hasSurvey || (list.length === 0 && helpers.length === 0)) return next(e)
110
111    const { Box, Text } = $.ui.resolve(e)
112    const total = list.length > 0 ? list.length : helpers.length
113    const done =
114      list.length > 0
115        ? list.filter(t => t.status === 'completed').length
116        : helpers.filter(a => a.isDone).length
117    const current = list.find(t => t.status === 'in_progress')
118    const step = current?.activeForm ?? (done === total ? 'All done' : 'Working…')
119    const room = Math.max(10, (e.props.bodyColumns ?? 80) - 30)
120
121    return (
122      <Box>
123        <Text color={PURPLE}>{bar(done / total, Math.min(24, room))} </Text>
124        <Text color={PURPLE} bold>
125          {done}/{total}{' '}
126        </Text>
127        <Text dimColor>{step.slice(0, room)}</Text>
128      </Box>
129    )
130  })
131
132  // The side panel: one colored row per sub-agent.
133  on('ui.render', { component: 'Pane', requestId: PANE }, async ($, e) => {
134    const { Box, Text } = $.ui.resolve(e)
135    const helpers = await read($, agents)
136    const width = Math.max(8, Math.min(20, (e.props.bodyColumns ?? 40) - 14))
137
138    if (helpers.length === 0) return <Text dimColor>No sub-agents yet.</Text>
139
140    return (
141      <Box flexDirection="column">
142        {helpers.map(a => (
143          <Box flexDirection="column" key={a.id}>
144            <Text color={a.color} bold>
145              {a.name.slice(0, width + 10)}
146            </Text>
147            <Text color={a.color}>
148              {bar(a.isDone ? 1 : a.tools / (a.tools + 6), width)}{' '}
149              {a.isDone ? 'done' : `${a.tools} calls`}
150            </Text>
151            <Text dimColor>{compact(a.tokens)} tokens</Text>
152          </Box>
153        ))}
154      </Box>
155    )
156  })
157}
158
types/index.d.ts 20 lines
1export type DeskTodo = {
2  content: string
3  status: 'pending' | 'in_progress' | 'completed'
4  activeForm: string
5}
6export type DeskAgent = {
7  id: string
8  name: string
9  color: string
10  tools: number
11  tokens: number
12  isDone: boolean
13}
14
15declare module 'claude-code' {
16  interface PluginState {
17    'helper-desk': { todos: Shaped<DeskTodo[]>; agents: Shaped<DeskAgent[]> }
18  }
19}
20