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

Production-grade skills for AI coding agents (OpenClaw, Claude Code, and others).
Built by Fabricio Cavalcante — Software Engineer & AI Specialist.
Plugins are complete workflow bundles — skills + agents + hooks working together as an integrated system. Install a plugin to get the full experience.
| Plugin | Description | Platform |
|---|---|---|
| game-dev | 26 skills, 7 commands, 3 agents, 5 hooks — complete game development workflow with engineering, design, narrative, and infrastructure | Game Dev |
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.
| Mod | Description |
|---|---|
| commit-guard | Blocks commits with machine paths or .env files, git add -A, and pushes to main |
| prod-guard | Holds Dokploy MCP writes until you press Proceed |
| bun-only | Rewrites npm, npx, yarn and pnpm to bun |
| done-gate | Shows edited files and the checks still owed |
| worktrees | Shows git worktrees, their changes, and the count |
| usage-rings | Context and plan usage, coloured by level |
| pr-status | The current branch's PR: CI checks and review |
| sql-guard | Holds writes to non-local databases until you press Proceed |
| pino-guard | Blocks pino transports that break bundled logging |
Skills are individual, cherry-pickable modules. Install only what you need.
| Skill | Description | Platform |
|---|---|---|
| claude-agent-sdk | Build production AI agents with the Claude Agent SDK — query(), hooks, subagents, custom tools, MCP servers, sessions, permissions, hosting | Claude Code / API |
| database-performance-tuning | Diagnose 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 script | PostgreSQL / MySQL |
| foundry-vtt-module-dev | Build, 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 files | Foundry VTT |
| foundry-vtt-system-dev | Build 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, enrichers | Foundry VTT |
| openclaw-genie | Comprehensive OpenClaw gateway skill — installation, configuration, 38+ channels, memory, tools, hooks, deployment, multi-agent | OpenClaw |
| nuvemshop-sdk | Prepare, audit, implement, homologate, and publish Nuvemshop/Tiendanube apps — OAuth, API, NubeSDK, Nexo, Nimbus, webhooks, and App Store approval | Nuvemshop |
| react-animations | React animation patterns and utilities | React |
| Skill | Description |
|---|---|
| game-design-fundamentals | Core loops, MDA framework, progression curves |
| game-economy-design | Virtual economies, currencies, loot tables |
| level-design | Levels, environments, difficulty curves |
| procedural-gen | Dungeon gen, noise terrain, wave function collapse |
| quest-mission-design | Quests, objectives, quest trees |
| skill-progression-trees | Skill trees, talent trees, unlock graphs |
| ui-ux-game | HUD, menus, onboarding, accessibility |
| Skill | Description |
|---|---|
| betterauth-integration | Auth, OAuth, JWT, RBAC |
| bullmq-game-queues | Job queues, async events, scheduled tasks |
| elevenlabs-sound-music | SFX generation, adaptive music, voice acting |
| game-backend-architecture | Game servers, WebSocket, rooms, tick loops |
| game-state-sync | Client-server sync, rollback netcode, delta compression |
| gameplay-analytics | Retention analytics, funnels, D1/D7/D30 |
| matchmaking-system | Matchmaking, lobbies, ELO, skill-based queues |
| postgres-game-schema | Game DB schemas, Drizzle ORM, inventory |
| redis-game-patterns | Caching, leaderboards, pub/sub, rate limiting |
| stripe-game-payments | In-app purchases, subscriptions, webhooks |
| Skill | Description |
|---|---|
| ci-cd-game | CI/CD pipelines, GitHub Actions, deployment |
| claude-code-game-workflow | Entry point for AI agents on game projects |
| cursor-codex-integration | Cursor IDE, .cursorrules, Codex setup |
| monitoring-game-ops | Monitoring, logging, telemetry, alerts |
| Skill | Description |
|---|---|
| character-design-narrative | Characters, NPCs, dialogue, archetypes |
| quest-narrative-coherence | Narrative coherence check, lore validation |
| story-structure-game | Branching narratives, player agency, story arcs |
| worldbuilding | Lore, factions, geography, world bibles |
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 firstboilerplate/ files are starter code, copy and customizetemplates/ are document/config templates to fill inARCHITECTURE.md explains system design decisions/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.
# 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
# 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>/
GPL-3.0
hooks/register.tsx 91 lines1import 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};
91hooks/parse.ts 29 lines1export 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