brainrot-cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +372 -0
- package/dist/AchievementNotification.d.ts +28 -0
- package/dist/AchievementNotification.d.ts.map +1 -0
- package/dist/AchievementNotification.js +74 -0
- package/dist/AchievementNotification.js.map +1 -0
- package/dist/GameSelector.d.ts +25 -0
- package/dist/GameSelector.d.ts.map +1 -0
- package/dist/GameSelector.js +105 -0
- package/dist/GameSelector.js.map +1 -0
- package/dist/HelpOverlay.d.ts +15 -0
- package/dist/HelpOverlay.d.ts.map +1 -0
- package/dist/HelpOverlay.js +134 -0
- package/dist/HelpOverlay.js.map +1 -0
- package/dist/Layout.d.ts +49 -0
- package/dist/Layout.d.ts.map +1 -0
- package/dist/Layout.js +83 -0
- package/dist/Layout.js.map +1 -0
- package/dist/Leaderboard.d.ts +46 -0
- package/dist/Leaderboard.d.ts.map +1 -0
- package/dist/Leaderboard.js +68 -0
- package/dist/Leaderboard.js.map +1 -0
- package/dist/LogViewer.d.ts +33 -0
- package/dist/LogViewer.d.ts.map +1 -0
- package/dist/LogViewer.js +179 -0
- package/dist/LogViewer.js.map +1 -0
- package/dist/LoopAlertOverlay.d.ts +15 -0
- package/dist/LoopAlertOverlay.d.ts.map +1 -0
- package/dist/LoopAlertOverlay.js +17 -0
- package/dist/LoopAlertOverlay.js.map +1 -0
- package/dist/LoopManagementPanel.d.ts +44 -0
- package/dist/LoopManagementPanel.d.ts.map +1 -0
- package/dist/LoopManagementPanel.js +220 -0
- package/dist/LoopManagementPanel.js.map +1 -0
- package/dist/SettingsMenu.d.ts +22 -0
- package/dist/SettingsMenu.d.ts.map +1 -0
- package/dist/SettingsMenu.js +367 -0
- package/dist/SettingsMenu.js.map +1 -0
- package/dist/SplitPane.d.ts +63 -0
- package/dist/SplitPane.d.ts.map +1 -0
- package/dist/SplitPane.js +104 -0
- package/dist/SplitPane.js.map +1 -0
- package/dist/StatsMenu.d.ts +15 -0
- package/dist/StatsMenu.d.ts.map +1 -0
- package/dist/StatsMenu.js +230 -0
- package/dist/StatsMenu.js.map +1 -0
- package/dist/StatusBar.d.ts +58 -0
- package/dist/StatusBar.d.ts.map +1 -0
- package/dist/StatusBar.js +106 -0
- package/dist/StatusBar.js.map +1 -0
- package/dist/__tests__/ralph-loop-parser.test.d.ts +2 -0
- package/dist/__tests__/ralph-loop-parser.test.d.ts.map +1 -0
- package/dist/__tests__/ralph-loop-parser.test.js +143 -0
- package/dist/__tests__/ralph-loop-parser.test.js.map +1 -0
- package/dist/claude-code-process.d.ts +76 -0
- package/dist/claude-code-process.d.ts.map +1 -0
- package/dist/claude-code-process.js +221 -0
- package/dist/claude-code-process.js.map +1 -0
- package/dist/cli.d.ts +42 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +265 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +206 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +270 -0
- package/dist/config.js.map +1 -0
- package/dist/game-types.d.ts +177 -0
- package/dist/game-types.d.ts.map +1 -0
- package/dist/game-types.js +55 -0
- package/dist/game-types.js.map +1 -0
- package/dist/games/MinesweeperGame.d.ts +15 -0
- package/dist/games/MinesweeperGame.d.ts.map +1 -0
- package/dist/games/MinesweeperGame.js +555 -0
- package/dist/games/MinesweeperGame.js.map +1 -0
- package/dist/games/PongGame.d.ts +15 -0
- package/dist/games/PongGame.d.ts.map +1 -0
- package/dist/games/PongGame.js +379 -0
- package/dist/games/PongGame.js.map +1 -0
- package/dist/games/SnakeGame.d.ts +15 -0
- package/dist/games/SnakeGame.d.ts.map +1 -0
- package/dist/games/SnakeGame.js +333 -0
- package/dist/games/SnakeGame.js.map +1 -0
- package/dist/games/TetrisGame.d.ts +15 -0
- package/dist/games/TetrisGame.d.ts.map +1 -0
- package/dist/games/TetrisGame.js +654 -0
- package/dist/games/TetrisGame.js.map +1 -0
- package/dist/games/index.d.ts +23 -0
- package/dist/games/index.d.ts.map +1 -0
- package/dist/games/index.js +47 -0
- package/dist/games/index.js.map +1 -0
- package/dist/high-scores.d.ts +57 -0
- package/dist/high-scores.d.ts.map +1 -0
- package/dist/high-scores.js +230 -0
- package/dist/high-scores.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +264 -0
- package/dist/index.js.map +1 -0
- package/dist/ralph-loop-parser.d.ts +58 -0
- package/dist/ralph-loop-parser.d.ts.map +1 -0
- package/dist/ralph-loop-parser.js +315 -0
- package/dist/ralph-loop-parser.js.map +1 -0
- package/dist/stats.d.ts +142 -0
- package/dist/stats.d.ts.map +1 -0
- package/dist/stats.js +521 -0
- package/dist/stats.js.map +1 -0
- package/dist/styled-components.d.ts +231 -0
- package/dist/styled-components.d.ts.map +1 -0
- package/dist/styled-components.js +192 -0
- package/dist/styled-components.js.map +1 -0
- package/dist/theme.d.ts +301 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +372 -0
- package/dist/theme.js.map +1 -0
- package/dist/themes.d.ts +117 -0
- package/dist/themes.d.ts.map +1 -0
- package/dist/themes.js +296 -0
- package/dist/themes.js.map +1 -0
- package/dist/ui/index.d.ts +13 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +29 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/use-claude-code.d.ts +30 -0
- package/dist/use-claude-code.d.ts.map +1 -0
- package/dist/use-claude-code.js +84 -0
- package/dist/use-claude-code.js.map +1 -0
- package/dist/use-config.d.ts +58 -0
- package/dist/use-config.d.ts.map +1 -0
- package/dist/use-config.js +113 -0
- package/dist/use-config.js.map +1 -0
- package/dist/use-game-loop.d.ts +47 -0
- package/dist/use-game-loop.d.ts.map +1 -0
- package/dist/use-game-loop.js +136 -0
- package/dist/use-game-loop.js.map +1 -0
- package/dist/use-high-scores.d.ts +41 -0
- package/dist/use-high-scores.d.ts.map +1 -0
- package/dist/use-high-scores.js +94 -0
- package/dist/use-high-scores.js.map +1 -0
- package/dist/use-layout-state.d.ts +77 -0
- package/dist/use-layout-state.d.ts.map +1 -0
- package/dist/use-layout-state.js +160 -0
- package/dist/use-layout-state.js.map +1 -0
- package/dist/use-ralph-loop.d.ts +41 -0
- package/dist/use-ralph-loop.d.ts.map +1 -0
- package/dist/use-ralph-loop.js +106 -0
- package/dist/use-ralph-loop.js.map +1 -0
- package/dist/use-spinner.d.ts +46 -0
- package/dist/use-spinner.d.ts.map +1 -0
- package/dist/use-spinner.js +71 -0
- package/dist/use-spinner.js.map +1 -0
- package/dist/use-stats.d.ts +59 -0
- package/dist/use-stats.d.ts.map +1 -0
- package/dist/use-stats.js +150 -0
- package/dist/use-stats.js.map +1 -0
- package/dist/use-terminal-size.d.ts +29 -0
- package/dist/use-terminal-size.d.ts.map +1 -0
- package/dist/use-terminal-size.js +48 -0
- package/dist/use-terminal-size.js.map +1 -0
- package/dist/useTheme.d.ts +76 -0
- package/dist/useTheme.d.ts.map +1 -0
- package/dist/useTheme.js +136 -0
- package/dist/useTheme.js.map +1 -0
- package/package.json +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
# BrainRot CLI
|
|
2
|
+
|
|
3
|
+
```
|
|
4
|
+
____ _ ____ _ ____ _ ___
|
|
5
|
+
| __ ) _ __ __ _(_)_ __ | _ \ ___ | |_ / ___| | |_ _|
|
|
6
|
+
| _ \| '__/ _` | | '_ \| |_) / _ \| __| | | | | | |
|
|
7
|
+
| |_) | | | (_| | | | | | _ < (_) | |_ | |___| |___ | |
|
|
8
|
+
|____/|_| \__,_|_|_| |_|_| \_\___/ \__| \____|_____|___|
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
> Play games while Claude Code works
|
|
12
|
+
|
|
13
|
+
[](package.json)
|
|
14
|
+
[](LICENSE)
|
|
15
|
+
[](package.json)
|
|
16
|
+
|
|
17
|
+
A terminal-native CLI application that wraps Claude Code with built-in games to play while AI agents work. Built with TypeScript, React 19, and Ink 6.
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
- **4 Built-in Games**: Snake, Pong, Tetris, and Minesweeper
|
|
22
|
+
- **Split-Pane Interface**: Play games while monitoring Claude Code output
|
|
23
|
+
- **4 Color Themes**: Default, Dark, Light, and Retro
|
|
24
|
+
- **Auto-Pause**: Games pause automatically when Claude Code needs your attention
|
|
25
|
+
- **Persistent High Scores**: Compete against yourself with local leaderboards
|
|
26
|
+
- **Full Keyboard Navigation**: No mouse needed
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
### Prerequisites
|
|
31
|
+
|
|
32
|
+
- Node.js 18.0.0 or higher
|
|
33
|
+
- [Claude Code](https://claude.ai/code) installed and available in PATH
|
|
34
|
+
|
|
35
|
+
### Install Globally
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install -g brainrot-cli
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Or Run from Source
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
git clone https://github.com/brainrot-cli/brainrot.git
|
|
45
|
+
cd brainrot
|
|
46
|
+
npm install
|
|
47
|
+
npm run build
|
|
48
|
+
npm start
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Quick Start
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# Start with defaults
|
|
55
|
+
brainrot
|
|
56
|
+
|
|
57
|
+
# Start with vertical layout
|
|
58
|
+
brainrot --layout-direction vertical
|
|
59
|
+
|
|
60
|
+
# Start with dark theme
|
|
61
|
+
brainrot --color-scheme dark
|
|
62
|
+
|
|
63
|
+
# Start directly in Snake
|
|
64
|
+
brainrot --default-game snake
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Keyboard Shortcuts
|
|
68
|
+
|
|
69
|
+
### Global Controls
|
|
70
|
+
|
|
71
|
+
| Key | Action |
|
|
72
|
+
|-----|--------|
|
|
73
|
+
| `Tab` | Switch focus between game and management panes |
|
|
74
|
+
| `?` | Toggle help overlay |
|
|
75
|
+
| `Ctrl+S` | Open settings menu |
|
|
76
|
+
| `Ctrl+C` | Exit application |
|
|
77
|
+
| `[` / `]` | Resize panes (when focused) |
|
|
78
|
+
|
|
79
|
+
### In-Game Controls
|
|
80
|
+
|
|
81
|
+
| Key | Action |
|
|
82
|
+
|-----|--------|
|
|
83
|
+
| `P` | Pause / Resume game |
|
|
84
|
+
| `R` | Restart game |
|
|
85
|
+
| `Q` or `Esc` | Return to game menu |
|
|
86
|
+
| `H` | View high scores (where available) |
|
|
87
|
+
|
|
88
|
+
## Games
|
|
89
|
+
|
|
90
|
+
### Snake
|
|
91
|
+
|
|
92
|
+
Classic snake game - eat food to grow, avoid walls and yourself!
|
|
93
|
+
|
|
94
|
+
| Key | Action |
|
|
95
|
+
|-----|--------|
|
|
96
|
+
| `Arrow Keys` or `WASD` | Move snake |
|
|
97
|
+
| `P` | Pause |
|
|
98
|
+
| `R` | Restart |
|
|
99
|
+
| `H` | High scores |
|
|
100
|
+
|
|
101
|
+
### Pong
|
|
102
|
+
|
|
103
|
+
Single-player pong against AI - first to 5 wins!
|
|
104
|
+
|
|
105
|
+
| Key | Action |
|
|
106
|
+
|-----|--------|
|
|
107
|
+
| `Up/Down` or `W/S` | Move paddle |
|
|
108
|
+
| `P` | Pause |
|
|
109
|
+
| `R` | Restart |
|
|
110
|
+
|
|
111
|
+
### Tetris
|
|
112
|
+
|
|
113
|
+
Classic Tetris - clear lines with falling blocks!
|
|
114
|
+
|
|
115
|
+
| Key | Action |
|
|
116
|
+
|-----|--------|
|
|
117
|
+
| `Left/Right` | Move piece |
|
|
118
|
+
| `Up` or `W` | Rotate piece |
|
|
119
|
+
| `Down` | Soft drop |
|
|
120
|
+
| `Space` | Hard drop |
|
|
121
|
+
| `P` | Pause |
|
|
122
|
+
| `R` | Restart |
|
|
123
|
+
|
|
124
|
+
### Minesweeper
|
|
125
|
+
|
|
126
|
+
Find all mines without triggering them!
|
|
127
|
+
|
|
128
|
+
| Key | Action |
|
|
129
|
+
|-----|--------|
|
|
130
|
+
| `Arrow Keys` | Move cursor |
|
|
131
|
+
| `Space` or `Enter` | Reveal cell |
|
|
132
|
+
| `F` | Flag/unflag cell |
|
|
133
|
+
| `P` | Pause |
|
|
134
|
+
| `R` | Restart |
|
|
135
|
+
|
|
136
|
+
## Configuration
|
|
137
|
+
|
|
138
|
+
BrainRot CLI uses XDG Base Directory conventions for configuration storage.
|
|
139
|
+
|
|
140
|
+
### File Locations
|
|
141
|
+
|
|
142
|
+
| Type | Location |
|
|
143
|
+
|------|----------|
|
|
144
|
+
| Config | `~/.config/brainrot-cli/config.json` |
|
|
145
|
+
| High Scores | `~/.local/share/brainrot-cli/high-scores.json` |
|
|
146
|
+
| Stats | `~/.local/share/brainrot-cli/stats.json` |
|
|
147
|
+
|
|
148
|
+
### Example Configuration
|
|
149
|
+
|
|
150
|
+
```json
|
|
151
|
+
{
|
|
152
|
+
"version": 1,
|
|
153
|
+
"games": {
|
|
154
|
+
"defaultDifficulty": "medium",
|
|
155
|
+
"snake": {
|
|
156
|
+
"initialSpeed": 5,
|
|
157
|
+
"growthRate": 1
|
|
158
|
+
},
|
|
159
|
+
"tetris": {
|
|
160
|
+
"startingLevel": 1,
|
|
161
|
+
"showGhostPiece": true
|
|
162
|
+
},
|
|
163
|
+
"minesweeper": {
|
|
164
|
+
"defaultDifficulty": "easy",
|
|
165
|
+
"showTimer": true
|
|
166
|
+
},
|
|
167
|
+
"pong": {
|
|
168
|
+
"aiDifficulty": 5,
|
|
169
|
+
"ballSpeedMultiplier": 1.0
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
"layout": {
|
|
173
|
+
"direction": "horizontal",
|
|
174
|
+
"splitRatio": 0.5,
|
|
175
|
+
"minRatio": 0.25,
|
|
176
|
+
"maxRatio": 0.75,
|
|
177
|
+
"resizeStep": 0.05,
|
|
178
|
+
"showSecondary": true,
|
|
179
|
+
"defaultFocusedPane": 0
|
|
180
|
+
},
|
|
181
|
+
"theme": {
|
|
182
|
+
"colorScheme": "default",
|
|
183
|
+
"borderStyle": "round",
|
|
184
|
+
"spinnerStyle": "spinner",
|
|
185
|
+
"enableAnimations": true,
|
|
186
|
+
"colors": {
|
|
187
|
+
"primary": "cyan",
|
|
188
|
+
"secondary": "magenta",
|
|
189
|
+
"accent": "yellow"
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
"claudeCode": {
|
|
193
|
+
"executablePath": "claude",
|
|
194
|
+
"workingDirectory": null,
|
|
195
|
+
"defaultArgs": [],
|
|
196
|
+
"shutdownTimeout": 5000
|
|
197
|
+
},
|
|
198
|
+
"app": {
|
|
199
|
+
"maxScoresPerGame": 10,
|
|
200
|
+
"defaultGame": null,
|
|
201
|
+
"debugMode": false,
|
|
202
|
+
"logLevel": "info"
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Configuration Options
|
|
208
|
+
|
|
209
|
+
#### Games
|
|
210
|
+
|
|
211
|
+
| Option | Type | Default | Description |
|
|
212
|
+
|--------|------|---------|-------------|
|
|
213
|
+
| `defaultDifficulty` | `easy` \| `medium` \| `hard` | `medium` | Default difficulty for games |
|
|
214
|
+
| `snake.initialSpeed` | `1-10` | `5` | Snake movement speed |
|
|
215
|
+
| `snake.growthRate` | `number` | `1` | Segments added per food |
|
|
216
|
+
| `tetris.startingLevel` | `1-10` | `1` | Starting level |
|
|
217
|
+
| `tetris.showGhostPiece` | `boolean` | `true` | Show piece preview |
|
|
218
|
+
| `minesweeper.defaultDifficulty` | `easy` \| `medium` \| `hard` | `easy` | Board size/mines |
|
|
219
|
+
| `minesweeper.showTimer` | `boolean` | `true` | Display timer |
|
|
220
|
+
| `pong.aiDifficulty` | `1-10` | `5` | AI opponent skill |
|
|
221
|
+
| `pong.ballSpeedMultiplier` | `0.5-2.0` | `1.0` | Ball speed modifier |
|
|
222
|
+
|
|
223
|
+
#### Layout
|
|
224
|
+
|
|
225
|
+
| Option | Type | Default | Description |
|
|
226
|
+
|--------|------|---------|-------------|
|
|
227
|
+
| `direction` | `horizontal` \| `vertical` | `horizontal` | Split direction |
|
|
228
|
+
| `splitRatio` | `0.0-1.0` | `0.5` | Initial split ratio |
|
|
229
|
+
| `minRatio` | `0.0-1.0` | `0.25` | Minimum pane size |
|
|
230
|
+
| `maxRatio` | `0.0-1.0` | `0.75` | Maximum pane size |
|
|
231
|
+
| `resizeStep` | `number` | `0.05` | Keyboard resize increment |
|
|
232
|
+
| `showSecondary` | `boolean` | `true` | Show management pane |
|
|
233
|
+
| `defaultFocusedPane` | `0` \| `1` | `0` | Initial focus (0=game) |
|
|
234
|
+
|
|
235
|
+
#### Theme
|
|
236
|
+
|
|
237
|
+
| Option | Type | Default | Description |
|
|
238
|
+
|--------|------|---------|-------------|
|
|
239
|
+
| `colorScheme` | `default` \| `dark` \| `light` \| `retro` | `default` | Color theme |
|
|
240
|
+
| `borderStyle` | `single` \| `round` \| `double` \| `heavy` | `round` | Border characters |
|
|
241
|
+
| `spinnerStyle` | `spinner` \| `dots` \| `braille` | `spinner` | Loading animation |
|
|
242
|
+
| `enableAnimations` | `boolean` | `true` | Enable animations |
|
|
243
|
+
| `colors.*` | `string` | varies | Custom color overrides |
|
|
244
|
+
|
|
245
|
+
#### Claude Code
|
|
246
|
+
|
|
247
|
+
| Option | Type | Default | Description |
|
|
248
|
+
|--------|------|---------|-------------|
|
|
249
|
+
| `executablePath` | `string` | `claude` | Path to Claude Code |
|
|
250
|
+
| `workingDirectory` | `string` | `null` | Default working directory |
|
|
251
|
+
| `defaultArgs` | `string[]` | `[]` | CLI arguments to pass |
|
|
252
|
+
| `shutdownTimeout` | `number` | `5000` | Graceful shutdown timeout (ms) |
|
|
253
|
+
|
|
254
|
+
#### App
|
|
255
|
+
|
|
256
|
+
| Option | Type | Default | Description |
|
|
257
|
+
|--------|------|---------|-------------|
|
|
258
|
+
| `maxScoresPerGame` | `number` | `10` | Leaderboard entries to keep |
|
|
259
|
+
| `defaultGame` | `string` | `null` | Auto-select game on start |
|
|
260
|
+
| `debugMode` | `boolean` | `false` | Enable debug output |
|
|
261
|
+
| `logLevel` | `error` \| `warn` \| `info` \| `debug` | `info` | Log verbosity |
|
|
262
|
+
|
|
263
|
+
## Themes
|
|
264
|
+
|
|
265
|
+
BrainRot CLI includes four color themes:
|
|
266
|
+
|
|
267
|
+
| Theme | Description |
|
|
268
|
+
|-------|-------------|
|
|
269
|
+
| `default` | Modern terminal - cyan/magenta/yellow |
|
|
270
|
+
| `dark` | Muted colors for extended sessions |
|
|
271
|
+
| `light` | High contrast for light backgrounds |
|
|
272
|
+
| `retro` | Classic green phosphor terminal |
|
|
273
|
+
|
|
274
|
+
Switch themes via settings menu (`Ctrl+S`) or CLI flag:
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
brainrot --color-scheme retro
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## CLI Flags
|
|
281
|
+
|
|
282
|
+
```
|
|
283
|
+
USAGE:
|
|
284
|
+
brainrot [OPTIONS]
|
|
285
|
+
|
|
286
|
+
OPTIONS:
|
|
287
|
+
-h, --help Show help message and exit
|
|
288
|
+
-v, --version Show version and exit
|
|
289
|
+
-c, --config <path> Use custom config file path
|
|
290
|
+
|
|
291
|
+
LAYOUT OPTIONS:
|
|
292
|
+
--layout-direction <dir> Split direction: horizontal, vertical
|
|
293
|
+
--split-ratio <ratio> Split ratio (0.0-1.0)
|
|
294
|
+
|
|
295
|
+
THEME OPTIONS:
|
|
296
|
+
--color-scheme <scheme> Color scheme: default, dark, light, retro
|
|
297
|
+
--border-style <style> Border style: single, round, double, heavy
|
|
298
|
+
|
|
299
|
+
CLAUDE CODE OPTIONS:
|
|
300
|
+
--claude-executable <path> Path to Claude Code executable
|
|
301
|
+
--claude-args <args> Arguments to pass (comma-separated)
|
|
302
|
+
-w, --working-dir <path> Working directory for Claude Code
|
|
303
|
+
|
|
304
|
+
APP OPTIONS:
|
|
305
|
+
-d, --debug Enable debug mode
|
|
306
|
+
--log-level <level> Log level: error, warn, info, debug
|
|
307
|
+
-g, --default-game <id> Default game: snake, tetris, pong, minesweeper
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Examples
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
# Start with vertical layout and 70% game pane
|
|
314
|
+
brainrot --layout-direction vertical --split-ratio 0.7
|
|
315
|
+
|
|
316
|
+
# Use retro theme with heavy borders
|
|
317
|
+
brainrot --color-scheme retro --border-style heavy
|
|
318
|
+
|
|
319
|
+
# Debug mode with verbose logging
|
|
320
|
+
brainrot --debug --log-level debug
|
|
321
|
+
|
|
322
|
+
# Custom config file
|
|
323
|
+
brainrot -c ~/.my-brainrot-config.json
|
|
324
|
+
|
|
325
|
+
# Pass arguments to Claude Code
|
|
326
|
+
brainrot --claude-args "--model,claude-3-opus"
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
## Troubleshooting
|
|
330
|
+
|
|
331
|
+
### Claude Code not found
|
|
332
|
+
|
|
333
|
+
If you see "claude: command not found":
|
|
334
|
+
|
|
335
|
+
1. Ensure Claude Code is installed
|
|
336
|
+
2. Add it to your PATH, or use `--claude-executable` to specify the full path:
|
|
337
|
+
```bash
|
|
338
|
+
brainrot --claude-executable /path/to/claude
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### Terminal too small
|
|
342
|
+
|
|
343
|
+
Games require minimum terminal dimensions. If you see dimension warnings:
|
|
344
|
+
|
|
345
|
+
1. Resize your terminal window
|
|
346
|
+
2. Or try vertical layout which may fit better:
|
|
347
|
+
```bash
|
|
348
|
+
brainrot --layout-direction vertical
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Configuration not saving
|
|
352
|
+
|
|
353
|
+
Ensure you have write permissions to the config directory:
|
|
354
|
+
|
|
355
|
+
```bash
|
|
356
|
+
mkdir -p ~/.config/brainrot-cli
|
|
357
|
+
chmod 755 ~/.config/brainrot-cli
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Games not responding to input
|
|
361
|
+
|
|
362
|
+
Make sure the game pane has focus (use `Tab` to switch). The focused pane has a highlighted border.
|
|
363
|
+
|
|
364
|
+
## Development
|
|
365
|
+
|
|
366
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines.
|
|
367
|
+
|
|
368
|
+
See [docs/GAME_DEVELOPMENT.md](docs/GAME_DEVELOPMENT.md) for creating new games.
|
|
369
|
+
|
|
370
|
+
## License
|
|
371
|
+
|
|
372
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Achievement Notification Component
|
|
3
|
+
*
|
|
4
|
+
* Displays a notification when an achievement is unlocked.
|
|
5
|
+
* Supports queuing multiple achievements and auto-dismiss.
|
|
6
|
+
*/
|
|
7
|
+
export interface AchievementNotificationProps {
|
|
8
|
+
/** Achievement IDs to display */
|
|
9
|
+
achievementIds: string[];
|
|
10
|
+
/** Callback when all notifications have been shown */
|
|
11
|
+
onComplete?: () => void;
|
|
12
|
+
/** Duration to show each notification (ms) */
|
|
13
|
+
duration?: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Achievement notification queue manager
|
|
17
|
+
*/
|
|
18
|
+
export declare function AchievementNotification({ achievementIds, onComplete, duration, }: AchievementNotificationProps): import("react/jsx-runtime").JSX.Element | null;
|
|
19
|
+
/**
|
|
20
|
+
* Hook for managing achievement notifications
|
|
21
|
+
*/
|
|
22
|
+
export declare function useAchievementNotifications(): {
|
|
23
|
+
addAchievements: (ids: string[]) => void;
|
|
24
|
+
NotificationComponent: import("react/jsx-runtime").JSX.Element | null;
|
|
25
|
+
hasNotifications: boolean;
|
|
26
|
+
};
|
|
27
|
+
export default AchievementNotification;
|
|
28
|
+
//# sourceMappingURL=AchievementNotification.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AchievementNotification.d.ts","sourceRoot":"","sources":["../src/AchievementNotification.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,MAAM,WAAW,4BAA4B;IAC3C,iCAAiC;IACjC,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,sDAAsD;IACtD,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAkCD;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,cAAc,EACd,UAAU,EACV,QAAe,GAChB,EAAE,4BAA4B,kDA+B9B;AAED;;GAEG;AACH,wBAAgB,2BAA2B;2BAIC,MAAM,EAAE;;;EAuBnD;AAED,eAAe,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Achievement Notification Component
|
|
4
|
+
*
|
|
5
|
+
* Displays a notification when an achievement is unlocked.
|
|
6
|
+
* Supports queuing multiple achievements and auto-dismiss.
|
|
7
|
+
*/
|
|
8
|
+
import { Box, Text } from "ink";
|
|
9
|
+
import { useState, useEffect, useCallback } from "react";
|
|
10
|
+
import { getAchievementById } from "./stats.js";
|
|
11
|
+
import { useThemeColors } from "./useTheme.js";
|
|
12
|
+
/**
|
|
13
|
+
* Single achievement notification display
|
|
14
|
+
*/
|
|
15
|
+
function AchievementBanner({ achievement }) {
|
|
16
|
+
const colors = useThemeColors();
|
|
17
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "double", borderColor: colors.warning, paddingX: 2, paddingY: 1, alignItems: "center", children: [_jsx(Box, { children: _jsxs(Text, { color: colors.warning, bold: true, children: [achievement.icon, " ACHIEVEMENT UNLOCKED ", achievement.icon] }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { bold: true, color: colors.primary, children: achievement.name }) }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: achievement.description }) })] }));
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Achievement notification queue manager
|
|
21
|
+
*/
|
|
22
|
+
export function AchievementNotification({ achievementIds, onComplete, duration = 3000, }) {
|
|
23
|
+
const [currentIndex, setCurrentIndex] = useState(0);
|
|
24
|
+
const [currentAchievement, setCurrentAchievement] = useState(null);
|
|
25
|
+
// Get current achievement details
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (currentIndex < achievementIds.length) {
|
|
28
|
+
const achievement = getAchievementById(achievementIds[currentIndex]);
|
|
29
|
+
setCurrentAchievement(achievement ?? null);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
setCurrentAchievement(null);
|
|
33
|
+
onComplete?.();
|
|
34
|
+
}
|
|
35
|
+
}, [currentIndex, achievementIds, onComplete]);
|
|
36
|
+
// Auto-advance to next achievement
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
if (!currentAchievement)
|
|
39
|
+
return;
|
|
40
|
+
const timer = setTimeout(() => {
|
|
41
|
+
setCurrentIndex((prev) => prev + 1);
|
|
42
|
+
}, duration);
|
|
43
|
+
return () => clearTimeout(timer);
|
|
44
|
+
}, [currentAchievement, duration]);
|
|
45
|
+
if (!currentAchievement) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
return _jsx(AchievementBanner, { achievement: currentAchievement });
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Hook for managing achievement notifications
|
|
52
|
+
*/
|
|
53
|
+
export function useAchievementNotifications() {
|
|
54
|
+
const [queue, setQueue] = useState([]);
|
|
55
|
+
const [isShowing, setIsShowing] = useState(false);
|
|
56
|
+
const addAchievements = useCallback((ids) => {
|
|
57
|
+
if (ids.length === 0)
|
|
58
|
+
return;
|
|
59
|
+
setQueue((prev) => [...prev, ...ids]);
|
|
60
|
+
setIsShowing(true);
|
|
61
|
+
}, []);
|
|
62
|
+
const handleComplete = useCallback(() => {
|
|
63
|
+
setQueue([]);
|
|
64
|
+
setIsShowing(false);
|
|
65
|
+
}, []);
|
|
66
|
+
const NotificationComponent = isShowing && queue.length > 0 ? (_jsx(AchievementNotification, { achievementIds: queue, onComplete: handleComplete })) : null;
|
|
67
|
+
return {
|
|
68
|
+
addAchievements,
|
|
69
|
+
NotificationComponent,
|
|
70
|
+
hasNotifications: isShowing && queue.length > 0,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
export default AchievementNotification;
|
|
74
|
+
//# sourceMappingURL=AchievementNotification.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AchievementNotification.js","sourceRoot":"","sources":["../src/AchievementNotification.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GameSelector Component
|
|
3
|
+
*
|
|
4
|
+
* A menu component for selecting and launching games.
|
|
5
|
+
* Supports keyboard navigation with arrow keys and Enter to select.
|
|
6
|
+
*/
|
|
7
|
+
import type { GameInfo, GameDimensions } from "./game-types.js";
|
|
8
|
+
export interface GameSelectorProps {
|
|
9
|
+
/** Available games to select from */
|
|
10
|
+
games: GameInfo[];
|
|
11
|
+
/** Whether the selector has keyboard focus */
|
|
12
|
+
hasFocus: boolean;
|
|
13
|
+
/** Available dimensions */
|
|
14
|
+
dimensions: GameDimensions;
|
|
15
|
+
/** Callback when a game is selected */
|
|
16
|
+
onSelectGame: (gameId: string) => void;
|
|
17
|
+
/** Callback when stats menu is requested */
|
|
18
|
+
onOpenStats?: () => void;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Game selector menu component
|
|
22
|
+
*/
|
|
23
|
+
export declare function GameSelector({ games, hasFocus, dimensions, onSelectGame, onOpenStats, }: GameSelectorProps): import("react/jsx-runtime").JSX.Element;
|
|
24
|
+
export default GameSelector;
|
|
25
|
+
//# sourceMappingURL=GameSelector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GameSelector.d.ts","sourceRoot":"","sources":["../src/GameSelector.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAKhE,MAAM,WAAW,iBAAiB;IAChC,qCAAqC;IACrC,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,8CAA8C;IAC9C,QAAQ,EAAE,OAAO,CAAC;IAClB,2BAA2B;IAC3B,UAAU,EAAE,cAAc,CAAC;IAC3B,uCAAuC;IACvC,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;CAC1B;AAgHD;;GAEG;AACH,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,QAAQ,EACR,UAAU,EACV,YAAY,EACZ,WAAW,GACZ,EAAE,iBAAiB,2CA8GnB;AAED,eAAe,YAAY,CAAC"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* GameSelector Component
|
|
4
|
+
*
|
|
5
|
+
* A menu component for selecting and launching games.
|
|
6
|
+
* Supports keyboard navigation with arrow keys and Enter to select.
|
|
7
|
+
*/
|
|
8
|
+
import { Box, Text, useInput } from "ink";
|
|
9
|
+
import { useState, useCallback, useEffect } from "react";
|
|
10
|
+
import { navIcons } from "./theme.js";
|
|
11
|
+
import { useThemeColors } from "./useTheme.js";
|
|
12
|
+
import { getAchievementCount } from "./stats.js";
|
|
13
|
+
function GameCard({ game, isSelected, isHighlighted, dimensions }) {
|
|
14
|
+
const colors = useThemeColors();
|
|
15
|
+
const borderColor = isHighlighted ? colors.primary : isSelected ? colors.accent : colors.border;
|
|
16
|
+
const titleColor = isHighlighted ? colors.primary : isSelected ? colors.accent : colors.text;
|
|
17
|
+
// Calculate card width based on available space
|
|
18
|
+
const cardWidth = Math.min(Math.max(dimensions.width - 4, 30), 50);
|
|
19
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: borderColor, paddingX: 1, width: cardWidth, children: [_jsx(Box, { children: _jsxs(Text, { bold: true, color: titleColor, children: [isHighlighted ? `${navIcons.arrowRight} ` : " ", game.name] }) }), _jsx(Text, { dimColor: true, wrap: "truncate", children: game.description }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: colors.textMuted, children: "Controls: " }), _jsx(Text, { dimColor: true, children: game.controls })] }), game.minWidth && game.minHeight && (_jsxs(Text, { dimColor: true, children: ["Min size: ", game.minWidth, "x", game.minHeight] }))] }));
|
|
20
|
+
}
|
|
21
|
+
function SelectorHeader({ hasFocus }) {
|
|
22
|
+
const colors = useThemeColors();
|
|
23
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { bold: true, color: colors.primary, children: "Select a Game" }), _jsx(Text, { dimColor: true, children: hasFocus
|
|
24
|
+
? "↑/↓: Navigate | Enter: Select | S: Stats | Q: Back"
|
|
25
|
+
: "Press Tab to focus game selector" })] }));
|
|
26
|
+
}
|
|
27
|
+
function StatsMenuCard({ isHighlighted, dimensions, achievementCount }) {
|
|
28
|
+
const colors = useThemeColors();
|
|
29
|
+
const borderColor = isHighlighted ? colors.warning : colors.border;
|
|
30
|
+
const titleColor = isHighlighted ? colors.warning : colors.textMuted;
|
|
31
|
+
const cardWidth = Math.min(Math.max(dimensions.width - 4, 30), 50);
|
|
32
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: borderColor, paddingX: 1, width: cardWidth, children: [_jsx(Box, { children: _jsxs(Text, { bold: true, color: titleColor, children: [isHighlighted ? `${navIcons.arrowRight} ` : " ", "Stats & Achievements"] }) }), _jsx(Text, { dimColor: true, children: "View your gameplay statistics and unlock achievements" }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: colors.textMuted, children: "Progress: " }), _jsxs(Text, { color: colors.success, children: [achievementCount.unlocked, "/", achievementCount.total, " achievements"] })] })] }));
|
|
33
|
+
}
|
|
34
|
+
function EmptyState() {
|
|
35
|
+
const colors = useThemeColors();
|
|
36
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 2, children: [_jsx(Text, { color: colors.accent, children: "No games available" }), _jsx(Text, { dimColor: true, children: "Check back later for new games!" })] }));
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Game selector menu component
|
|
40
|
+
*/
|
|
41
|
+
export function GameSelector({ games, hasFocus, dimensions, onSelectGame, onOpenStats, }) {
|
|
42
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
43
|
+
const [achievementCount, setAchievementCount] = useState({ unlocked: 0, total: 0 });
|
|
44
|
+
// Total items: games + stats menu
|
|
45
|
+
const totalItems = games.length + 1;
|
|
46
|
+
const statsIndex = games.length; // Stats is always last
|
|
47
|
+
// Load achievement count
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
const loadCount = async () => {
|
|
50
|
+
const count = await getAchievementCount();
|
|
51
|
+
setAchievementCount(count);
|
|
52
|
+
};
|
|
53
|
+
void loadCount();
|
|
54
|
+
}, []);
|
|
55
|
+
const handleSelect = useCallback(() => {
|
|
56
|
+
if (selectedIndex === statsIndex) {
|
|
57
|
+
onOpenStats?.();
|
|
58
|
+
}
|
|
59
|
+
else if (games.length > 0 && games[selectedIndex]) {
|
|
60
|
+
onSelectGame(games[selectedIndex].id);
|
|
61
|
+
}
|
|
62
|
+
}, [games, selectedIndex, onSelectGame, onOpenStats, statsIndex]);
|
|
63
|
+
useInput((input, key) => {
|
|
64
|
+
if (!hasFocus)
|
|
65
|
+
return;
|
|
66
|
+
// Navigate up
|
|
67
|
+
if (key.upArrow || input === "k") {
|
|
68
|
+
setSelectedIndex((prev) => (prev > 0 ? prev - 1 : totalItems - 1));
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
// Navigate down
|
|
72
|
+
if (key.downArrow || input === "j") {
|
|
73
|
+
setSelectedIndex((prev) => (prev < totalItems - 1 ? prev + 1 : 0));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
// Select item
|
|
77
|
+
if (key.return || input === " ") {
|
|
78
|
+
handleSelect();
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// Quick open stats with S key
|
|
82
|
+
if (input === "s" || input === "S") {
|
|
83
|
+
onOpenStats?.();
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
// Quick select game by number (1-9)
|
|
87
|
+
const num = parseInt(input, 10);
|
|
88
|
+
if (!isNaN(num) && num >= 1 && num <= games.length) {
|
|
89
|
+
setSelectedIndex(num - 1);
|
|
90
|
+
if (games[num - 1]) {
|
|
91
|
+
onSelectGame(games[num - 1].id);
|
|
92
|
+
}
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
}, { isActive: hasFocus });
|
|
96
|
+
// Keep selected index in bounds
|
|
97
|
+
if (selectedIndex >= totalItems && totalItems > 0) {
|
|
98
|
+
setSelectedIndex(totalItems - 1);
|
|
99
|
+
}
|
|
100
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, height: "100%", children: [_jsx(SelectorHeader, { hasFocus: hasFocus }), games.length === 0 ? (_jsx(EmptyState, {})) : (_jsxs(Box, { flexDirection: "column", gap: 1, children: [games.map((game, index) => (_jsx(GameCard, { game: game, isSelected: index === selectedIndex, isHighlighted: hasFocus && index === selectedIndex, dimensions: dimensions }, game.id))), _jsx(StatsMenuCard, { isHighlighted: hasFocus && selectedIndex === statsIndex, dimensions: dimensions, achievementCount: achievementCount })] })), totalItems > 0 && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: selectedIndex === statsIndex
|
|
101
|
+
? "Stats & Achievements"
|
|
102
|
+
: `Game ${selectedIndex + 1} of ${games.length}` }) }))] }));
|
|
103
|
+
}
|
|
104
|
+
export default GameSelector;
|
|
105
|
+
//# sourceMappingURL=GameSelector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GameSelector.js","sourceRoot":"","sources":["../src/GameSelector.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HelpOverlay Component
|
|
3
|
+
*
|
|
4
|
+
* Modal overlay that displays comprehensive keyboard shortcuts organized by context.
|
|
5
|
+
* Triggered by ? or F1 key globally.
|
|
6
|
+
*/
|
|
7
|
+
export interface HelpOverlayProps {
|
|
8
|
+
/** Whether the overlay has focus */
|
|
9
|
+
hasFocus: boolean;
|
|
10
|
+
/** Callback to close the overlay */
|
|
11
|
+
onClose: () => void;
|
|
12
|
+
}
|
|
13
|
+
export declare function HelpOverlay({ hasFocus, onClose }: HelpOverlayProps): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export default HelpOverlay;
|
|
15
|
+
//# sourceMappingURL=HelpOverlay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HelpOverlay.d.ts","sourceRoot":"","sources":["../src/HelpOverlay.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAwBH,MAAM,WAAW,gBAAgB;IAC/B,oCAAoC;IACpC,QAAQ,EAAE,OAAO,CAAC;IAClB,oCAAoC;IACpC,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AA+ID,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,gBAAgB,2CAoFlE;AAED,eAAe,WAAW,CAAC"}
|