SLOPSHOPPER

pr-status

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…

bandprocesstimer
★ 21v0.1.0GPL-3.0updated 2026-10-19fcsouza/agent-skills/mods/pr-status
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 70 lines
1import 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};
70
hooks/status.ts 45 lines
1// 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