SLOPSHOPPER

team

Your digital team inside Claude Code: who works on what (programmer, reviewer, security, legal, design, QA), a Watch link to what they build, and what each…

newpanebandguardcommandtoast
v0.2.0no licenseupdated 2026-10-12yverse-studio/claude-setup/mods/team
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · team
│ ┃ Team ✕ › fix the failing auth test and add an audit log call │ ┃ ○ Project Manager │ ┃ Waiting for you ⏺ Read(src/auth.ts) │ ┃ ⎿ Read 6 lines │ ┃ ○ Researcher ⏺ Update(src/auth.ts) │ ┃ idle ⎿ Added 2 lines, removed 1 line │ ┃ ⏺ Bash(bun test) │ ┃ ○ Designer ⎿ 3 pass, 1 fail │ ┃ idle │ ┃ ● Done. refresh now rejects expired claims and logs an audit event. │ ┃ ○ Programmer │ ┃ idle ✻ Worked for 42s · done 4:20 PM │ ┃ │ ┃ ○ Tester › /team │ ┃ idle ⎿ team: Team pane opened. │ ┃ │ ┃ ○ Reviewer │ ┃ idle │ ┃ │ ┃ ○ Security │ ┃ idle │ ┃ │ ┃ ○ Legal │ ┃ idle │ ┃ │ ┃ ○ Marketer /team for details Last turn: 99k tokens, 91k of them re-read context ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts

Draws

Band
/team for details Last turn: 99k tokens, 91k of them re-read context
Pane · Team
○ Project Manager Waiting for you ○ Researcher idle ○ Designer idle ○ Programmer idle ○ Tester idle ○ Reviewer idle ○ Security idle ○ Legal idle ○ Marketer idle Watch: nothing running yet. A dev server or the test browser shows up here.
README

claude-setup

yskills' global Claude Code setup: one command makes every project on a machine use the same rules, skills, agents, hooks and plugins. Built on Everything Claude Code (the Anthropic hackathon winner's setup), curated to this stack so it costs about a fifth of the context the full version does.

Lost? docs/MAP.md says where every part lives, what loads when, and which file is the one home of each rule.

Install

Needs Node 20+ (24 recommended), Git and Claude Code.

Windows (PowerShell; use $HOME, never ~, which PowerShell passes through as a folder named "~"):

git clone https://github.com/yverse-studio/claude-setup $HOME/claude-setup
node $HOME/claude-setup/install.mjs

Mac or Linux:

git clone https://github.com/yverse-studio/claude-setup ~/claude-setup && node ~/claude-setup/install.mjs

Update later (Windows: run the two commands one after the other, with $HOME instead of ~):

git -C ~/claude-setup pull && node ~/claude-setup/install.mjs

Re-running is safe. Your old ~/.claude/CLAUDE.md and settings.json are backed up to ~/.claude/backups/ each time; your own notes in CLAUDE.md outside the managed block, your own skills and agents, and settings keys this setup doesn't touch are kept. Flags: --dry-run, --skip-plugins, --skip-hooks. At the end it lists useful command-line tools that are missing (gh, ffmpeg, docker, python, uv, fnm) with the install command for your OS.

What you get

PieceWhatWhere it comes from
CLAUDE.mdHow Claude works for you: owns tasks end to end, verifies before pushing, screenshot-reviews UI, your default stackCLAUDE.md
SettingsAuto permission mode, safe allow list, asks before force-push and deploys, denies reading .env and key files (a deny rule is not a sandbox), high effort, status line with context barglobal/settings.json
13 plugins onsuperpowers, frontend-design, security-guidance, TypeScript and Python LSP, Context7, Playwright, commit-commands, code-simplifier, feature-dev, claude-md-management, claude-code-setup, skill-creator (/code-review and the claude-api skill are built into Claude Code now)Anthropic's official marketplace, config/plugins.json
7 plugins per projectCloudflare, Firebase, Stripe, Sentry, PostHog, Supabase, Vercel: installed but off; a repo turns one on in its .claude/settings.jsonsame
17 ECC skillsVue, Nuxt 4, Vite, API design, security review, e2e, SEO, i18n....claude/skills, list in config/ecc.json
15 ECC agentsplanner, architect, code/TS/Vue/security/database reviewers, build-error-resolver, e2e-runner, a11y, performance, SEO....claude/agents
ECC rulescommon + TypeScript, Vue, Nuxt, web (language rules load only for matching files).claude/rules/ecc
ECC hookssession memory across restarts, quality gate after edits, design-quality warning, console.log and secret checks before commits, compaction hints (PC only)npx ecc-universal@2.2.3
ToolboxKnows which plugins, APIs and skills any kind of app needs (video, AI, payments, ads, auth, email, analytics, mobile, scraping...), searches all ~315 plugins, 293 ECC skills and skills.sh live (and points cloud threads at the Anthropic Directory), and adds them to that one project.claude/skills/toolbox
Own skillsoperator (idea to live app: sizes the job, staffs research/legal/design/build/QA/red team/launch/marketing, acceptance criteria before building, a 5/5 gate on every PR, three ok/no briefs, the gate merges every 5/5 PR), legal (German law for any app, privacy scan of a preview, license and trademark checks), onboard-project (set a repo up for Claude in one PR), scaffold (the exact file list of a new project's skeleton, in order), publish (deploys through Cloudflare Workers Builds with a preview per branch, no keys in GitHub), ship-check (run CI locally, then fresh code-reviewer passes until clean), ui-review (phone/desktop screenshots + critique), sell (checkout, shop and shipping from Germany: keys, tested Stripe-on-Workers code, German shop law, go-live), market (marketing plan in the PRD, launch, channels, German advertising law, weekly report), store (App Store and Google Play: Capacitor, RevenueCat, paywall defaults, review rules, keyless native builds), watch (watch a video or reel: frames plus transcript; vendored from claude-video, MIT), studio (Roblox Studio and Blender through their MCP servers on the PC: setup check, build loop, live frames yskills watches in HQ), roblox (a Roblox game: Rojo and Lune, Robux receipts, Open Cloud publish, Creator Hub steps), game-3d (a 3D game with three.js and Electron for Steam: folder layout, VRM characters and moves, asset licences checked, cloud screenshots and perf budget, Steam checklist).claude/skills
Own agentsdesign-critic (reviews screenshots for the "looks AI" problem), evaluator (clicks through a preview and grades a slice against its criteria), legal-reviewer (German must-haves on a preview), red-team (attacks your own preview like an outsider).claude/agents
Design tasteimpeccable (design skill plus a local detect linter for AI-look patterns) and web-interface-guidelines (Vercel's UI checklist); ui-review runs both.claude/skills (Apache-2.0 and MIT, see THIRD_PARTY.md)
Context guardA hook after every tool call: at 150k tokens of context it tells Claude to work leaner, at 200k to wrap up and hand off, then again every further 50k (re-reading long contexts is most of the bill). Runs on the PC and in cloud threadsglobal/context-guard.mjs, cloud/setup.sh
team modYour digital team inside Claude Code (terminal or desktop app, not cloud threads): a band above the prompt shows which roles are working (the nine roles: Project Manager, Researcher, Designer, Programmer, Tester, Reviewer, Security, Legal, Marketer) and what the last turn cost; /team opens a pane with each role's task and a Watch link to the dev server or page being tested. Claude's test browser (Playwright) opens as a visible Chrome window on the PCmods/team
HQ dashboardOne live page for all projects (Company XY, https://claude.ai/artifact/TmQ7UpL6EPjXKkpR9S4kJT): level, money, what needs you, who works on what with whom, progress per part, Watch links. Each project's Project Manager writes its rows into it; no project has a page of its owndocs/hq-rows.md (the page lives in yverse-studio/company-xy)
PonytailWrite the least code that works: reuse, standard library, platform, installed dependency, one line, then new code.claude/skills/ponytail (MIT, see THIRD_PARTY.md)

Project threads (cloud)

Threads in a Claude project run in the cloud, not on your machine. Add this repo to the project (Project settings, Repositories) and every thread loads CLAUDE.md and all skills, agents and rules from .claude/, the same files the installer copies.

Per project, once: install the Claude GitHub App on all repositories so new project repos are reachable, then in Project settings set Thread model Sonnet and Thread effort medium (the default is Opus at high effort), and paste operator/templates/project-instructions.md into Project instructions (steps: docs/USING.md).

Plugins and network need a cloud environment (Project settings > Cloud environment > Add cloud environment). Pick the same one in every project that uses this setup.

  • Setup script: paste cloud/setup.sh. It installs 11 of the 13 global plugins at user scope (Context7 and skill-creator stay on the PC: Context7 asks for a sign-in a thread can't do, and threads have Anthropic's skill-creator built in) and makes built-in subagents default to Sonnet. The result is cached and rebuilt about weekly, so threads don't pay for it each time. Plugin tools connect a few seconds after a thread starts.
  • Network access: Custom, tick "Also include default list of common package managers", and add:
  • cdn.jsdelivr.net, unpkg.com (CDN scripts, e.g. GSAP for HyperFrames)
  • *.yskills.workers.dev (your own live sites and branch previews, so threads can open and screenshot what they deployed; blocked otherwise)
  • *.youtube.com, *.googlevideo.com, only if threads work on video
  • No deploy keys here. Cloudflare Workers Builds deploys main and gives every other branch its own preview (the publish skill); GitHub only runs verify and holds no keys. Threads never deploy themselves: wrangler deploy through the cloud proxy fails, because the proxy replaces Workers' asset-upload token (401). Optional: a Stripe test key as an API credential for api.stripe.com (Pro/Max plans); the proxy adds it and Claude never sees it.

On your PC, global/settings.json lets Claude read the official docs of your stack (Nuxt, Vue, Cloudflare, Stripe, GitHub, MDN, German law texts...) without asking each time. Cloud threads don't get these settings (the setup script installs plugins and one env key, not the permission list), so there a link nobody posted in the chat still asks before it opens; whether adding the list to the setup script would stop that is untested. Threads research with WebSearch instead.

Using it

  • New idea: just describe it. The operator skill sizes it, researches, plans and builds it, and sends you three ok/no briefs, and the gate merges every PR at 5/5; toolbox adds the tools to that project only.
  • New to a repo: "onboard this project" runs onboard-project: a short CLAUDE.md, .claude/settings.json with the right per-project plugins, and a verify skill matching CI.
  • Before every push: Claude runs ship-check on its own; ask for it any time.
  • UI work: ui-review takes the screenshots, design-critic lists what to fix.
  • Your own Chrome: start Claude Code with claude --chrome (needs the Claude in Chrome extension) when a page needs your login; Playwright covers everything headless.
  • Bigger features: superpowers' brainstorm and plan skills, or /feature-dev.

Changing it

  • Rules for how Claude works: edit CLAUDE.md.
  • Plugins: edit config/plugins.json (move one between global and project).
  • ECC subset: edit config/ecc.json, check out that ECC version, run node scripts/sync-ecc.mjs <ECC checkout>.
  • node scripts/check.mjs validates everything; CI runs it plus a full install into a throwaway home directory.

Research notes and sources behind these choices: docs/RESEARCH.md. The roadmap every new project follows: docs/WORKFLOW.md. The practice runs before the first real one: docs/TEST-PROJECTS.md.

Source 2 files
hooks/register.tsx 168 lines
1import { atom, read, update } from 'claude-code'
2import type { Register } from 'claude-code'
3
4import type { Preview, RoleId, Run, TurnCost } from '../types'
5
6// yskills' digital team, drawn from what Claude Code already does: the session itself is the
7// Project Manager, each subagent it starts is a teammate in a role. A band above the prompt shows who is
8// working; /team opens a pane with each role's task and a Watch link to what is being built.
9const runs = atom({ plugin: 'team', key: 'runs' } as const, [] as Run[])
10const isLeadWorking = atom({ plugin: 'team', key: 'isLeadWorking' } as const, false)
11const preview = atom({ plugin: 'team', key: 'preview' } as const, null as Preview | null)
12const last = atom({ plugin: 'team', key: 'last' } as const, null as TurnCost | null)
13
14const PANE = 'team'
15const KEPT_RUNS = 40
16
17// The nine roles HQ and the thread titles use. The session itself is the Project Manager.
18export const ROLES: { id: RoleId; name: string; agents: RegExp }[] = [
19  { id: 'pm', name: 'Project Manager', agents: /^planner$|^architect$|^plan$/i },
20  { id: 'researcher', name: 'Researcher', agents: /^$/ },
21  { id: 'designer', name: 'Designer', agents: /design-critic|a11y/i },
22  { id: 'programmer', name: 'Programmer', agents: /^$/ },
23  { id: 'tester', name: 'Tester', agents: /evaluator|e2e/i },
24  { id: 'reviewer', name: 'Reviewer', agents: /code-reviewer|typescript-reviewer|vue-reviewer|database-reviewer|silent-failure/i },
25  { id: 'security', name: 'Security', agents: /security|red-team/i },
26  { id: 'legal', name: 'Legal', agents: /legal/i },
27  { id: 'marketer', name: 'Marketer', agents: /seo/i },
28]
29
30const RESEARCH = /^researcher|research|competitor/i
31
32export function roleOf(agent: string, description = ''): RoleId {
33  // Security and legal first so "security-reviewer" and "legal-reviewer" don't land in Reviewer;
34  // anything unknown is a Programmer.
35  for (const id of ['security', 'legal', 'designer', 'tester', 'reviewer', 'marketer', 'pm'] as const) {
36    if (ROLES.find(r => r.id === id)!.agents.test(agent)) return id
37  }
38  return RESEARCH.test(description) ? 'researcher' : 'programmer'
39}
40
41// A dev server, a Worker preview or a page the test browser opened: what the team is building.
42const PREVIEW_URL = /https?:\/\/(?:localhost|127\.0\.0\.1)(?::\d+)?[^\s'"<>)]*|https:\/\/[\w.-]+\.workers\.dev[^\s'"<>)]*/
43export const findPreview = (text: string) => text.match(PREVIEW_URL)?.[0]?.replace(/[.,;:]+$/, '')
44
45const k = (n: number) => `${Math.round(n / 1000)}k`
46// The band and the pane must never hold up Claude's work: a failed write leaves old rows showing.
47const quiet = () => undefined
48
49export const register: Register = on => {
50  on('session.start', async ($, e, next) => {
51    await $.command.register({ name: 'team', description: 'Show your team: who works on what, and a Watch link' })
52    return next(e)
53  })
54
55  on('command.run', { command: 'team' }, async $ => {
56    await $.ui.open({ id: PANE, title: 'Team' })
57    return { text: 'Team pane opened.' }
58  })
59
60  on('prompt.submit', async ($, e, next) => {
61    await update($, runs, list => list.filter(r => !r.isDone)).catch(quiet)
62    await update($, isLeadWorking, () => true).catch(quiet)
63    return next(e)
64  }).catch(($, e, next) => next(e))
65
66  on('agent.spawn', async ($, e, next) => {
67    const result = await next(e)
68    if (result.agentId) {
69      const agent = e.subagentType || 'general-purpose'
70      const run: Run = { id: result.agentId, role: roleOf(agent, e.description), agent, task: e.description, isDone: false }
71      await update($, runs, list => [...list, run].slice(-KEPT_RUNS)).catch(quiet)
72    }
73    return result
74  }).catch(($, e, next) => next(e))
75
76  on('tool.call', async ($, e, next) => {
77    const ran = await next(e)
78    // Only the test browser and a command that starts a server (dev, preview, wrangler) set it,
79    // so a curl or a test log doesn't move the Watch link.
80    const isServer = e.tool === 'Bash' && /\b(dev|preview|serve|start|wrangler)\b/.test(String((e as { command?: unknown }).command ?? ''))
81    const url =
82      e.tool.endsWith('browser_navigate') && typeof (e as { url?: unknown }).url === 'string'
83        ? (e as { url: string }).url
84        : isServer && !ran.deny ? findPreview(ran.text ?? '') : undefined
85    if (url) await update($, preview, () => ({ url, from: e.tool === 'Bash' ? 'dev server' : 'test browser' })).catch(quiet)
86    return ran
87  }).catch(($, e, next) => next(e))
88
89  on('turn.complete', async ($, e, next) => {
90    if (e.agentId) {
91      await update($, runs, list => list.map(r => (r.id === e.agentId ? { ...r, isDone: true } : r))).catch(quiet)
92      const run = (await read($, runs)).find(r => r.id === e.agentId)
93      if (run) $.ui.toast(`${ROLES.find(r => r.id === run.role)?.name}: finished ${run.task}`)
94    } else {
95      await update($, isLeadWorking, () => false).catch(quiet)
96      if (e.usage) {
97        const u = e.usage
98        const cost: TurnCost = {
99          tokens: u.input_tokens + u.cache_read_input_tokens + u.cache_creation_input_tokens + u.output_tokens,
100          cacheRead: u.cache_read_input_tokens,
101        }
102        await update($, last, () => cost).catch(quiet)
103      }
104    }
105    return next(e)
106  })
107
108  on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
109    const list = await read($, runs)
110    const lead = await read($, isLeadWorking)
111    const cost = await read($, last)
112    if (e.props.hasSurvey || (list.length === 0 && cost === null)) return next(e)
113
114    const { Box, Text } = $.ui.resolve(e)
115    const busy = new Set(list.filter(r => !r.isDone).map(r => r.role))
116    if (lead) busy.add('pm')
117    const shown = ROLES.filter(r => busy.has(r.id) || list.some(x => x.role === r.id))
118
119    return (
120      <Box flexDirection="column">
121        <Text>
122          {shown.map(r => `${busy.has(r.id) ? '●' : '○'} ${r.name}`).join('   ')}
123          <Text dimColor>   /team for details</Text>
124        </Text>
125        {cost && (
126          <Text dimColor>
127            Last turn: {k(cost.tokens)} tokens, {k(cost.cacheRead)} of them re-read context
128          </Text>
129        )}
130      </Box>
131    )
132  })
133
134  on('ui.render', { component: 'Pane', requestId: PANE }, async ($, e) => {
135    const { Box, Text, Link } = $.ui.resolve(e)
136    const list = await read($, runs)
137    const lead = await read($, isLeadWorking)
138    const live = await read($, preview)
139
140    return (
141      <Box flexDirection="column">
142        {ROLES.map(role => {
143          const mine = list.filter(r => r.role === role.id)
144          const now = mine.filter(r => !r.isDone)
145          const isWorking = role.id === 'pm' ? lead || now.length > 0 : now.length > 0
146          const task = now[0]?.task ?? (role.id === 'pm' ? (lead ? 'Working on your request' : 'Waiting for you') : mine.at(-1)?.task)
147          return (
148            <Box key={role.id} flexDirection="column" marginBottom={1}>
149              <Text bold={isWorking} dimColor={!isWorking}>
150                {isWorking ? '●' : '○'} {role.name}
151                {now.length > 1 ? ` (${now.length})` : ''}
152              </Text>
153              <Text dimColor>  {task ?? 'idle'}{!isWorking && mine.length > 0 && role.id !== 'pm' ? ' (done)' : ''}</Text>
154            </Box>
155          )
156        })}
157        {live ? (
158          <Text>
159            Watch ({live.from}): <Link href={live.url} label={live.url} />
160          </Text>
161        ) : (
162          <Text dimColor>Watch: nothing running yet. A dev server or the test browser shows up here.</Text>
163        )}
164      </Box>
165    )
166  })
167}
168
types/index.d.ts 11 lines
1export type RoleId = 'pm' | 'researcher' | 'designer' | 'programmer' | 'tester' | 'reviewer' | 'security' | 'legal' | 'marketer'
2export type Run = { id: string; role: RoleId; agent: string; task: string; isDone: boolean }
3export type Preview = { url: string; from: string }
4export type TurnCost = { tokens: number; cacheRead: number }
5
6declare module 'claude-code' {
7  interface PluginState {
8    team: { runs: Run[]; isLeadWorking: boolean; preview: Preview | null; last: TurnCost | null }
9  }
10}
11