Claude-skill-registry gentleman-trainer
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/gentleman-trainer" ~/.claude/skills/majiayu000-claude-skill-registry-gentleman-trainer && rm -rf "$T"
manifest:
skills/data/gentleman-trainer/SKILL.mdsource content
When to Use
Use this skill when:
- Adding new Vim training modules
- Creating exercises or boss fights
- Modifying progression/unlock system
- Working on the Vim command simulator
- Adding practice mode features
Critical Patterns
Pattern 1: ModuleID Constants
All modules MUST be defined as
ModuleID constants in types.go:
type ModuleID string const ( ModuleHorizontal ModuleID = "horizontal" ModuleVertical ModuleID = "vertical" ModuleTextObjects ModuleID = "textobjects" ModuleChangeRepeat ModuleID = "cgn" ModuleSubstitution ModuleID = "substitution" ModuleRegex ModuleID = "regex" ModuleMacros ModuleID = "macros" // Add new modules here )
Pattern 2: Exercise Structure
Every exercise follows this structure:
type Exercise struct { ID string // "horizontal_001" Module ModuleID // Parent module Level int // 1-10 difficulty Type ExerciseType // lesson, practice, boss Code []string // Lines of code shown CursorPos Position // Initial cursor CursorTarget *Position // Target position (movement exercises) Mission string // What user must do Solutions []string // ALL valid solutions Optimal string // Best/shortest solution Hint string // Help text Explanation string // Post-answer teaching TimeoutSecs int // Before showing solution Points int // Base score }
Pattern 3: Module Unlock Order
Modules unlock sequentially - user must defeat boss to unlock next:
var moduleUnlockOrder = []ModuleID{ ModuleHorizontal, // Always unlocked ModuleVertical, // After horizontal boss ModuleTextObjects, // After vertical boss ModuleChangeRepeat, // After textobjects boss // ... etc }
Pattern 4: Progression Flow
Lessons (sequential) → Practice (80% accuracy) → Boss Fight → Next Module
Decision Tree
Adding new module? ├── Add ModuleID constant in types.go ├── Add to moduleUnlockOrder slice ├── Add ModuleInfo in GetAllModules() ├── Create exercises_{module}.go file ├── Implement GetLessons(moduleID) ├── Implement GetBoss(moduleID) └── Add practice exercises Adding exercises? ├── Create Exercise with unique ID format: "{module}_{number}" ├── Provide multiple Solutions (all valid answers) ├── Set Optimal to shortest/best solution ├── Include Hint for learning └── Add Explanation for post-answer Adding boss fight? ├── Create BossExercise in exercises_{module}.go ├── Add 5-7 BossSteps (exercise chain) ├── Set Lives (usually 3) ├── Include variety of module skills └── Return from GetBoss(moduleID)
Code Examples
Example 1: Creating a Module's Exercises File
// exercises_newmodule.go package trainer // NewModule lessons func getNewModuleLessons() []Exercise { return []Exercise{ { ID: "newmodule_001", Module: ModuleNewModule, Level: 1, Type: ExerciseLesson, Code: []string{"function example() {", " return true;", "}"}, CursorPos: Position{Line: 0, Col: 0}, Mission: "Use 'xx' to delete two characters", Solutions: []string{"xx", "2x", "dl dl"}, Optimal: "xx", Hint: "x deletes character under cursor", Explanation: "x is Vim's character delete. 2x or xx deletes two.", Points: 10, }, // ... more exercises } }
Example 2: Registering Module in GetAllModules
func GetAllModules() []ModuleInfo { return []ModuleInfo{ // ... existing modules { ID: ModuleNewModule, Name: "New Module", Icon: "🆕", Description: "Commands: xx, yy, zz", BossName: "The New Boss", }, } }
Example 3: Boss Fight Structure
func getNewModuleBoss() *BossExercise { return &BossExercise{ ID: "newmodule_boss", Module: ModuleNewModule, Name: "The New Boss", Lives: 3, Steps: []BossStep{ { Exercise: Exercise{ ID: "newmodule_boss_1", Module: ModuleNewModule, Code: []string{"challenge code here"}, CursorPos: Position{Line: 0, Col: 0}, Mission: "First boss challenge", Solutions: []string{"w", "W"}, Optimal: "w", }, TimeLimit: 10, }, // ... more steps (5-7 total) }, } }
Example 4: Exercise Validation
// Validation checks if answer is in Solutions func ValidateAnswer(exercise *Exercise, answer string) bool { answer = strings.TrimSpace(answer) for _, solution := range exercise.Solutions { if answer == solution { return true } } // Also check via simulator for creative solutions return validateViaSimulator(exercise, answer) }
Exercise Guidelines
Good Exercise Design
- Clear Mission: User knows exactly what to do
- Multiple Solutions: Accept all valid Vim ways
- Optimal Marked: Teach the best approach
- Progressive Difficulty: Level 1-10 within module
- Real Code: Use realistic code snippets
Solutions Array Rules
// GOOD: Accept all valid variations Solutions: []string{"w", "W", "e", "E", "f "}, // BAD: Only accept one way Solutions: []string{"w"},
Exercise ID Format
{module}_{number} → "horizontal_001" {module}_boss_{step} → "horizontal_boss_1"
Commands
cd installer && go test ./internal/tui/trainer/... # Run all trainer tests cd installer && go test -run TestExercise # Test exercises cd installer && go test -run TestSimulator # Test Vim simulator cd installer && go test -run TestProgression # Test unlock system
Resources
- Types: See
for data structuresinstaller/internal/tui/trainer/types.go - Exercises: See
for patternsinstaller/internal/tui/trainer/exercises_*.go - Simulator: See
for Vim emulationinstaller/internal/tui/trainer/simulator.go - Validation: See
for answer checkinginstaller/internal/tui/trainer/validation.go - Stats: See
for persistenceinstaller/internal/tui/trainer/stats.go