SLOPSHOPPER

badges

Badge above the prompt for each active session skill.

newbandprompt
v0.1.0MITupdated 2026-10-25shalom2552/agent-skills/mods/badges
A shopper browsing a rack in a slop shop
README

Agent Skills

Skills and mods for Claude-Code.

Install

curl -fsSL shalom2552.github.io/agent-skills/install | bash

Or clone and run ./install. See ./install --help for options.

Skills

SkillDescription
docs-syncFind and fix drift between docs and code.
dotconfAnswer, change, and remember dotfile and app configs.
read-onlyLock the session to read-only; change nothing.
setup-sh25-skillsInstall, manage and wire up shalom2552's agent-skills assets.
system-auditRead-only health, security and cleanliness audit of this machine.
system-fixDiagnose, fix, and document a system or config fault on this machine.
to-promptWrite a handoff prompt for a fresh agent.
visualizeTurn any subject into a visual HTML page.
walk-with-meLearn languages and idioms by coding with a coach.

Mods

Claude Code plugins.

/plugin install <mod> --marketplace shalom2552/agent-skills
ModDescription
spinner-wordsSwap the spinner verbs for dev ones.
tape/tape pane of tool calls per turn.
frameRounded frame around your prompts.
beatTurn time and usage as pills.
statuslineBottom status line for claude.
badgesBadge above the prompt for active skill.
tdiffLive diff of session changes.

Settings

Small JSON files that ./install merges into the scope's settings.json.

./install settings --scope global -y
SettingDescription
tmux-truecolorTruecolor inside tmux, so mod hex colors render right.
Source 2 files
hooks/register.tsx 90 lines
1import { atom, read, update } from 'claude-code'
2import type { EngineInterface, Register } from 'claude-code'
3
4import type { Active } from '../types'
5
6export const BADGES: Record<string, string> = {
7  dotconf: 'DOTCONF',
8  'system-fix': 'FIX',
9  'system-audit': 'AUDIT',
10}
11
12const SESSIONS = 'sessions'
13const MAX_SESSIONS = 20
14
15const active = atom({ plugin: 'badges', key: 'active' } as const, [] as Active)
16
17export const skillsOf = (raw: unknown): Active =>
18  Array.isArray(raw) ? raw.filter((s): s is string => typeof s === 'string' && BADGES[s] !== undefined) : []
19
20const savedOf = (raw: unknown): Record<string, unknown> =>
21  typeof raw === 'object' && raw !== null && !Array.isArray(raw) ? { ...(raw as Record<string, unknown>) } : {}
22
23const save = async ($: EngineInterface, list: Active) => {
24  const id = await $.session.id()
25  const saved = savedOf(await $.store.get(SESSIONS))
26  delete saved[id]
27  const kept = Object.entries(saved).slice(1 - MAX_SESSIONS)
28  await $.store.set(SESSIONS, Object.fromEntries(list.length === 0 ? kept : [...kept, [id, list]]))
29}
30
31const restore = async ($: EngineInterface) => {
32  const list = skillsOf(savedOf(await $.store.get(SESSIONS))[await $.session.id()])
33  if (list.length > 0) await update($, active, () => list)
34}
35
36const set = async ($: EngineInterface, next: (list: Active) => Active) => {
37  await update($, active, next)
38  await save($, await read($, active)).catch(() => undefined)
39}
40
41export const register: Register = on => {
42  on('session.start', async ($, e, next) => {
43    await restore($).catch(() => undefined)
44
45    return next(e)
46  })
47
48  on('session.end', async ($, e, next) => {
49    if (e.reason === 'clear' || e.reason === 'resume') await update($, active, () => [])
50
51    return next(e)
52  })
53
54  on('skill.prompt', { skill: Object.keys(BADGES) }, async ($, e, next) => {
55    await set($, list => (list.includes(e.skill) ? list : [...list, e.skill]))
56
57    return next(e)
58  })
59
60  on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
61    const list = await read($, active)
62    if (list.length === 0 || e.props.hasSurvey) return next(e)
63
64    const { Box, Button, Text } = $.ui.resolve(e)
65    const rest = await next(e)
66
67    return (
68      <Box flexDirection="column">
69        <Box gap={1}>
70          {list.flatMap(skill => [
71            <Text bold inverse color="permission" key={`badge:${skill}`}>
72              {` ${BADGES[skill]} `}
73            </Text>,
74            ...(e.props.isWorking
75              ? []
76              : [
77                  <Button
78                    key={`clear:${skill}`}
79                    label="x"
80                    onPress={() => set($, l => l.filter(s => s !== skill))}
81                  />,
82                ]),
83          ])}
84        </Box>
85        {rest}
86      </Box>
87    )
88  })
89}
90
types/index.d.ts 8 lines
1export type Active = string[]
2
3declare module 'claude-code' {
4  interface PluginState {
5    badges: { active: Active }
6  }
7}
8