A band above the prompt with the pull request of the current branch: CI checks passed, failed and running, and the review decision. Reads gh, refreshes every…

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 70 lines1import type { EngineInterface, Register, Timer } from 'claude-code';
2
3import { GH_FIELDS, parsePr, type PrStatus } from './status';
4
5const REFRESH_MS = 60_000;
6
7let pr: PrStatus | null = null;
8let timer: Timer | undefined;
9
10async function refresh($: EngineInterface): Promise<void> {
11 const cwd = await $.session.cwd();
12 const run = await $.process.run(['gh', 'pr', 'view', '--json', GH_FIELDS], { cwd, timeoutMs: 10_000 }).catch(() => null);
13 // No gh, no repo, no PR for this branch: the band stays hidden.
14 pr = run === null || run.exitCode !== 0 ? null : parsePr(run.stdout);
15 $.ui.invalidate('ui.render');
16}
17
18const REVIEW_TEXT = { approved: '✓ approved', changes: '✗ changes requested', pending: '● review pending', none: '' } as const;
19const REVIEW_COLOR = { approved: '#22c55e', changes: '#ef4444', pending: '#eab308', none: undefined } as const;
20
21export const register: Register = on => {
22 on('session.start', async ($, e, next) => {
23 const started = await next(e);
24 await refresh($).catch(() => undefined);
25 timer?.cancel();
26 timer = $.clock.every(REFRESH_MS, () => {
27 refresh($).catch(() => undefined);
28 });
29 return started;
30 });
31
32 on('turn.complete', async ($, e, next) => {
33 const done = await next(e);
34 // Subagent turns end too; only the main turn's end is worth a gh call.
35 // Started on a timer so the turn is not held up while gh answers.
36 if (e.agentId === undefined) $.clock.after(1, () => refresh($).catch(() => undefined));
37 return done;
38 });
39
40 on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
41 const below = await next(e);
42 if (e.props.hasSurvey || pr === null || pr.state !== 'OPEN') return below;
43
44 const { Box, Text } = $.ui.resolve(e);
45 const narrow = (e.viewport?.columns ?? Number.POSITIVE_INFINITY) < 80;
46 const ciColor = pr.fail > 0 ? '#ef4444' : pr.running > 0 ? '#eab308' : '#22c55e';
47 return (
48 <Box flexDirection="column">
49 <Box gap={1} flexWrap="wrap">
50 <Text bold color={ciColor}>
51 PR #{pr.number}
52 </Text>
53 {pr.isDraft ? <Text dimColor>draft</Text> : null}
54 {narrow ? null : (
55 <Text dimColor wrap="truncate-end">
56 {pr.title}
57 </Text>
58 )}
59 {pr.pass > 0 ? <Text color="#22c55e">✓{pr.pass}</Text> : null}
60 {pr.fail > 0 ? <Text color="#ef4444">✗{pr.fail}</Text> : null}
61 {pr.running > 0 ? <Text color="#eab308">●{pr.running}</Text> : null}
62 {pr.pass + pr.fail + pr.running === 0 ? <Text dimColor>no checks</Text> : null}
63 {pr.review === 'none' ? null : <Text color={REVIEW_COLOR[pr.review]}>{narrow ? REVIEW_TEXT[pr.review].split(' ')[0] : REVIEW_TEXT[pr.review]}</Text>}
64 </Box>
65 {below}
66 </Box>
67 );
68 });
69};
70hooks/status.ts 45 lines1// Turns `gh pr view --json ...` output into what the band draws. Pure, so the tests call it directly.
2
3export type Review = 'approved' | 'changes' | 'pending' | 'none';
4export type PrStatus = { number: number; title: string; isDraft: boolean; state: string; pass: number; fail: number; running: number; review: Review };
5
6export const GH_FIELDS = 'number,title,state,isDraft,reviewDecision,statusCheckRollup';
7
8type Check = { status?: string; conclusion?: string; state?: string };
9
10const FAILED = new Set(['FAILURE', 'ERROR', 'TIMED_OUT', 'CANCELLED', 'ACTION_REQUIRED', 'STARTUP_FAILURE']);
11const PASSED = new Set(['SUCCESS', 'NEUTRAL', 'SKIPPED']);
12
13/** One check's outcome: a CheckRun reads status/conclusion, a StatusContext reads state. */
14function outcomeOf(check: Check): 'pass' | 'fail' | 'running' {
15 const result = (check.conclusion ?? check.state ?? '').toUpperCase();
16 if (check.status !== undefined && check.status.toUpperCase() !== 'COMPLETED') return 'running';
17 if (FAILED.has(result)) return 'fail';
18 if (PASSED.has(result)) return 'pass';
19 return 'running';
20}
21
22const REVIEWS: Record<string, Review> = { APPROVED: 'approved', CHANGES_REQUESTED: 'changes', REVIEW_REQUIRED: 'pending' };
23
24/** Parses gh's JSON; null when there is no PR or the output is not what gh prints. */
25export function parsePr(json: string): PrStatus | null {
26 let data: { number?: unknown; title?: unknown; state?: unknown; isDraft?: unknown; reviewDecision?: unknown; statusCheckRollup?: unknown };
27 try {
28 data = JSON.parse(json);
29 } catch {
30 return null;
31 }
32 if (typeof data.number !== 'number') return null;
33 const checks = Array.isArray(data.statusCheckRollup) ? (data.statusCheckRollup as Check[]) : [];
34 const counts = { pass: 0, fail: 0, running: 0 };
35 for (const check of checks) counts[outcomeOf(check)] += 1;
36 return {
37 number: data.number,
38 title: typeof data.title === 'string' ? data.title : '',
39 state: typeof data.state === 'string' ? data.state : 'OPEN',
40 isDraft: data.isDraft === true,
41 review: REVIEWS[String(data.reviewDecision ?? '')] ?? 'none',
42 ...counts,
43 };
44}
45