SLOPSHOPPER

worktrees

A band above the prompt with the git worktrees of the current repo: branch, uncommitted changes, and the count against a limit of 10.

bandprocesstimer
★ 21v0.2.0GPL-3.0updated 2026-10-19fcsouza/agent-skills/mods/worktrees
A shopper browsing a rack in a slop shop
README

Agent Skills

Production-grade skills for AI coding agents (OpenClaw, Claude Code, and others).

Built by Fabricio Cavalcante — Software Engineer & AI Specialist.

Available Plugins

Plugins are complete workflow bundles — skills + agents + hooks working together as an integrated system. Install a plugin to get the full experience.

PluginDescriptionPlatform
game-dev26 skills, 7 commands, 3 agents, 5 hooks — complete game development workflow with engineering, design, narrative, and infrastructureGame Dev

Claude Code Mods

Mods are plugins built on function hooks: they run inside Claude Code, guard tool calls and draw above the prompt. See mods/ for install steps.

ModDescription
commit-guardBlocks commits with machine paths or .env files, git add -A, and pushes to main
prod-guardHolds Dokploy MCP writes until you press Proceed
bun-onlyRewrites npm, npx, yarn and pnpm to bun
done-gateShows edited files and the checks still owed
worktreesShows git worktrees, their changes, and the count
usage-ringsContext and plan usage, coloured by level
pr-statusThe current branch's PR: CI checks and review
sql-guardHolds writes to non-local databases until you press Proceed
pino-guardBlocks pino transports that break bundled logging

Available Skills

Skills are individual, cherry-pickable modules. Install only what you need.

Core

SkillDescriptionPlatform
claude-agent-sdkBuild production AI agents with the Claude Agent SDK — query(), hooks, subagents, custom tools, MCP servers, sessions, permissions, hostingClaude Code / API
database-performance-tuningDiagnose and fix slow databases like a DBA — pg_stat_statements, EXPLAIN plans, index design, query rewrites, vacuum and bloat, safe online rollout. Ships a read-only diagnostic scriptPostgreSQL / MySQL
foundry-vtt-module-devBuild, extend, and maintain Foundry VTT v14 modules — TypeDataModel, ApplicationV2 (detached windows), ActorSheetV2, hooks, sockets, dice, canvas/PIXI, Scene Levels, Regions as templates, ActiveEffects V2, v13→v14 migration, boilerplate filesFoundry VTT
foundry-vtt-system-devBuild Foundry VTT v14 game systems — system.json documentTypes, Actor/Item TypeDataModel, dice mechanics, combat/initiative, ActiveEffect V2 subtypes, data migration, character sheets, status effects, macros, enrichersFoundry VTT
openclaw-genieComprehensive OpenClaw gateway skill — installation, configuration, 38+ channels, memory, tools, hooks, deployment, multi-agentOpenClaw
nuvemshop-sdkPrepare, audit, implement, homologate, and publish Nuvemshop/Tiendanube apps — OAuth, API, NubeSDK, Nexo, Nimbus, webhooks, and App Store approvalNuvemshop
react-animationsReact animation patterns and utilitiesReact

Game Dev — Design

SkillDescription
game-design-fundamentalsCore loops, MDA framework, progression curves
game-economy-designVirtual economies, currencies, loot tables
level-designLevels, environments, difficulty curves
procedural-genDungeon gen, noise terrain, wave function collapse
quest-mission-designQuests, objectives, quest trees
skill-progression-treesSkill trees, talent trees, unlock graphs
ui-ux-gameHUD, menus, onboarding, accessibility

Game Dev — Engineering

SkillDescription
betterauth-integrationAuth, OAuth, JWT, RBAC
bullmq-game-queuesJob queues, async events, scheduled tasks
elevenlabs-sound-musicSFX generation, adaptive music, voice acting
game-backend-architectureGame servers, WebSocket, rooms, tick loops
game-state-syncClient-server sync, rollback netcode, delta compression
gameplay-analyticsRetention analytics, funnels, D1/D7/D30
matchmaking-systemMatchmaking, lobbies, ELO, skill-based queues
postgres-game-schemaGame DB schemas, Drizzle ORM, inventory
redis-game-patternsCaching, leaderboards, pub/sub, rate limiting
stripe-game-paymentsIn-app purchases, subscriptions, webhooks

Game Dev — Infrastructure

SkillDescription
ci-cd-gameCI/CD pipelines, GitHub Actions, deployment
claude-code-game-workflowEntry point for AI agents on game projects
cursor-codex-integrationCursor IDE, .cursorrules, Codex setup
monitoring-game-opsMonitoring, logging, telemetry, alerts

Game Dev — Narrative

SkillDescription
character-design-narrativeCharacters, NPCs, dialogue, archetypes
quest-narrative-coherenceNarrative coherence check, lore validation
story-structure-gameBranching narratives, player agency, story arcs
worldbuildingLore, factions, geography, world bibles

Structure

All skills follow a flat layout:

skills/<skill-name>/
├── README.md           # Overview and installation instructions
├── SKILL.md            # Main skill file (YAML frontmatter + body)
├── boilerplate/        # Starter code to copy and customize (optional)
├── templates/          # Document and config templates (optional)
├── references/         # Deep-dive reference files (optional)
└── ARCHITECTURE.md     # System design decisions (optional)
  • SKILL.md is always the entry point — read this first
  • boilerplate/ files are starter code, copy and customize
  • templates/ are document/config templates to fill in
  • ARCHITECTURE.md explains system design decisions

Installation

game-dev plugin (Claude Code)

/plugin marketplace add fcsouza/agent-skills
/plugin install game-dev@fcsouza-agent-skills

See plugins/game-dev/README.md for the full skill index, commands, and agents.

Individual skills (cherry-pick)

# Install a specific skill
npx skills add fcsouza/agent-skills --skill game-design-fundamentals

# Install all game-dev engineering skills at once
npx skills add fcsouza/agent-skills --skill betterauth-integration --skill postgres-game-schema --skill game-backend-architecture

# List all available skills
npx skills add fcsouza/agent-skills --list

Manual

# Plugin (full bundle)
cp -r plugins/game-dev/ .claude/plugins/game-dev/

# Individual skill
cp -r skills/<skill-name>/ .claude/skills/<skill-name>/

# For OpenClaw
cp -r skills/<skill-name>/ ~/.openclaw/workspace/skills/<skill-name>/

License

GPL-3.0

Source 2 files
hooks/register.tsx 91 lines
1import type { EngineInterface, Register, Timer } from 'claude-code';
2
3import { countChanges, parseWorktrees, shorten, type Worktree } from './parse';
4
5const LIMIT = 10;
6const REFRESH_MS = 60_000;
7
8let trees: Worktree[] = [];
9let timer: Timer | undefined;
10let current = '';
11
12function git($: EngineInterface, cwd: string, args: readonly string[]) {
13  return $.process.run(['git', '--no-optional-locks', '-C', cwd, ...args], { timeoutMs: 5_000 });
14}
15
16async function refresh($: EngineInterface): Promise<void> {
17  const cwd = await $.session.cwd();
18  const list = await git($, cwd, ['worktree', 'list', '--porcelain']).catch(() => null);
19  if (list === null || list.exitCode !== 0) {
20    trees = [];
21    $.ui.invalidate('ui.render');
22    return;
23  }
24  const top = await git($, cwd, ['rev-parse', '--show-toplevel']);
25  current = top.stdout.trim();
26  trees = await Promise.all(
27    parseWorktrees(list.stdout).map(async tree => {
28      const status = await git($, tree.path, ['status', '--porcelain']).catch(() => null);
29      return { ...tree, changes: status === null ? 0 : countChanges(status.stdout) };
30    }),
31  );
32  $.ui.invalidate('ui.render');
33}
34
35export const register: Register = on => {
36  on('session.start', async ($, e, next) => {
37    const started = await next(e);
38    await refresh($).catch(() => undefined);
39    timer?.cancel();
40    timer = $.clock.every(REFRESH_MS, () => {
41      refresh($).catch(() => undefined);
42    });
43    return started;
44  });
45
46  on('turn.complete', async ($, e, next) => {
47    const done = await next(e);
48    // Started on a timer so the turn is not held up by one git call per worktree.
49    if (e.agentId === undefined) $.clock.after(1, () => refresh($).catch(() => undefined));
50    return done;
51  });
52
53  on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
54    const below = await next(e);
55    if (e.props.hasSurvey) {
56      return below;
57    }
58    // One worktree is just the repo: nothing to show.
59    if (trees.length < 2) {
60      return below;
61    }
62
63    const { Box, Text } = $.ui.resolve(e);
64    const full = trees.length >= LIMIT;
65    // Narrow: only the tree this session is in, and a count of the rest.
66    const narrow = (e.viewport?.columns ?? Number.POSITIVE_INFINITY) < 80;
67    const shown = narrow ? trees.filter(tree => tree.path === current) : trees;
68    const others = trees.length - shown.length;
69    const dirtyOthers = trees.filter(tree => tree.path !== current && tree.changes > 0).length;
70    return (
71      <Box flexDirection="column">
72        <Box gap={1} flexWrap="wrap">
73          <Text color={full ? '#ef4444' : '#a78bfa'} bold>
74            ⑂ {trees.length}/{LIMIT}
75          </Text>
76          {shown.map(tree => (
77            <Text>
78              <Text color={tree.path === current ? '#38bdf8' : undefined} bold={tree.path === current}>
79                {shorten(tree.branch, narrow ? 24 : 32)}
80              </Text>
81              <Text color={tree.changes > 0 ? '#f59e0b' : '#22c55e'}>{tree.changes > 0 ? ` ●${tree.changes}` : ' ✓'}</Text>
82            </Text>
83          ))}
84          {others > 0 ? <Text dimColor>{`+${others}${dirtyOthers > 0 ? ` (${dirtyOthers} with changes)` : ''}`}</Text> : null}
85        </Box>
86        {below}
87      </Box>
88    );
89  });
90};
91
hooks/parse.ts 29 lines
1export type Worktree = { path: string; branch: string; changes: number };
2
3/** Parses `git worktree list --porcelain`. Detached heads show their short commit. */
4export function parseWorktrees(porcelain: string): Omit<Worktree, 'changes'>[] {
5  const trees: Omit<Worktree, 'changes'>[] = [];
6  for (const block of porcelain.split('\n\n')) {
7    const lines = block.split('\n');
8    const path = lines.find(line => line.startsWith('worktree '))?.slice('worktree '.length);
9    if (path === undefined || lines.includes('bare')) {
10      continue;
11    }
12    const branch = lines.find(line => line.startsWith('branch '))?.slice('branch refs/heads/'.length);
13    const head = lines.find(line => line.startsWith('HEAD '))?.slice('HEAD '.length, 'HEAD '.length + 7);
14    trees.push({ path, branch: branch ?? `(${head ?? 'detached'})` });
15  }
16  return trees;
17}
18
19/** Lines of `git status --porcelain`: one per changed or untracked file. */
20export const countChanges = (porcelain: string): number => porcelain.split('\n').filter(line => line.trim() !== '').length;
21
22/** Shortens a long name in the middle so both ends stay readable: feature/rob…ch-319677 */
23export function shorten(name: string, max: number): string {
24  if (name.length <= max) return name;
25  const keep = max - 1;
26  const head = Math.ceil(keep / 2);
27  return `${name.slice(0, head)}…${name.slice(name.length - (keep - head))}`;
28}
29