SLOPSHOPPER

starter-mod

新 Mod 的起步模板:一个面板 + 一个命令 + 输入框上方的提示 / Starter: one pane, one command, one hint above the prompt

newpanecommand
★ 2v0.1.0MITupdated 2026-10-06AGIHunt/cc-mods/templates/starter-mod
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · starter-mod
│ ┃ Starter ✕ › fix the failing auth test and add an audit log call │ ┃ Hello from a mod │ ┃ ⏺ Read(src/auth.ts) │ ┃ clicked 0 times ⎿ Read 6 lines │ ┃ ⏺ Update(src/auth.ts) │ ┃ [ Click ] ⎿ 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 │ │ › /starter │ │ ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts

Draws

Pane · Starter
Hello from a mod clicked 0 times [ Click ]
README

starter-mod

English · 简体中文

A starter template for a new mod: a /starter command opens a pane with a counter button. Copy it to start your own:

cp -r templates/starter-mod plugins/my-mod

Then rename every starter-mod to my-mod (name in plugin.json, PANE and the state keys in hooks/register.tsx, types/index.d.ts, and plugin in the tests). See CONTRIBUTING.md at the repo root.

Source 2 files
hooks/register.tsx 33 lines
1import type { Register } from 'claude-code'
2
3// 改名时,把这里、plugin.json 的 name、types/index.d.ts 里的键一起改掉
4const PANE = 'starter-mod'
5const CLICKS = { plugin: 'starter-mod', key: 'clicks' } as const
6
7export const register: Register = on => {
8  // 会话开始:注册命令。immediate 让 Claude 干活时输入命令也能马上执行
9  on('session.start', async ($, e, next) => {
10    await $.command.register({ name: 'starter', description: '打开起步模板的面板', immediate: true })
11    return next(e)
12  })
13
14  // 命令:打开面板。不回文字,免得在对话里留一行
15  on('command.run', { command: 'starter' }, async $ => {
16    await $.ui.open({ id: PANE, title: 'Starter', focus: true })
17    return {}
18  })
19
20  // 面板:读 $.state 画界面;按钮里改 $.state,读到它的界面会自动重画
21  on('ui.render', { component: 'Pane', requestId: PANE }, async ($, e) => {
22    const { Box, Text, Button } = $.ui.resolve(e)
23    const { value: clicks = 0 } = await $.state.get(CLICKS)
24    return (
25      <Box flexDirection="column" gap={1}>
26        <Text bold>Hello from a mod</Text>
27        <Text dimColor>{`clicked ${clicks} times`}</Text>
28        <Button key="click" label="Click" onPress={() => void $.state.set(CLICKS, clicks + 1)} />
29      </Box>
30    )
31  })
32}
33
types/index.d.ts 11 lines
1// $.state 里存的值要在这里声明类型(插件名 → 键 → 类型),validate 会逐个核对
2export type Clicks = number
3
4declare module 'claude-code' {
5  interface PluginState {
6    'starter-mod': {
7      clicks: Clicks
8    }
9  }
10}
11