claude-code-achievements 1.0.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.
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "claude-code-achievements",
3
+ "description": "Steam-style achievement system for Claude Code",
4
+ "author": {
5
+ "name": "subinium"
6
+ }
7
+ }
package/README.md ADDED
@@ -0,0 +1,201 @@
1
+ # ๐ŸŽฎ Claude Code Achievements
2
+
3
+ Steam-style achievement system for [Claude Code](https://claude.ai/claude-code). Gamify your coding journey and unlock achievements as you master Claude Code features!
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npx claude-code-achievements
9
+ ```
10
+
11
+ The interactive installer will:
12
+ 1. **Auto-detect** your OS (macOS/Linux/Windows)
13
+ 2. **Auto-detect** system notification capability
14
+ 3. Ask for language preference (English/ํ•œ๊ตญ์–ด)
15
+ 4. Configure notification style (system/terminal/both)
16
+ 5. Install to `~/.claude/plugins/local/claude-code-achievements`
17
+
18
+ ### Manual Installation
19
+
20
+ ```bash
21
+ git clone https://github.com/subinium/claude-code-achievements.git
22
+ cd claude-code-achievements
23
+ node bin/install.js
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ | Command | Description |
29
+ |---------|-------------|
30
+ | `/achievements` | View all achievements with progress |
31
+ | `/achievements hint` | Get tips for unlocking achievements |
32
+ | `/achievements basics` | View "Getting Started" category |
33
+ | `/achievements workflow` | View "Workflow" category |
34
+ | `/achievements tools` | View "Power Tools" category |
35
+ | `/achievements mastery` | View "Mastery" category |
36
+ | `/achievements-settings` | Change language or notification settings |
37
+
38
+ ## Achievements (15 total)
39
+
40
+ ### Getting Started (โฌœ Common / ๐ŸŸฉ Uncommon)
41
+
42
+ | # | Achievement | Description | How to Unlock |
43
+ |---|-------------|-------------|---------------|
44
+ | 1 | โœ๏ธ **First Touch** โฌœ | Edit a file | Use `Edit` tool |
45
+ | 2 | ๐Ÿ“ **Creator** โฌœ | Create a new file | Use `Write` tool |
46
+ | 3 | ๐Ÿ“‹ **Project Curator** ๐ŸŸฉ | Create CLAUDE.md | Write to `CLAUDE.md` |
47
+
48
+ ### Workflow (๐ŸŸฉ Uncommon)
49
+
50
+ | # | Achievement | Description | How to Unlock |
51
+ |---|-------------|-------------|---------------|
52
+ | 4 | ๐ŸŽฏ **Strategic Thinker** | Use Plan mode | Press `Shift+Tab` twice |
53
+ | 5 | ๐Ÿ“ฆ **Version Controller** | Commit with Claude | Run `git add` or `git commit` |
54
+ | 6 | ๐Ÿš€ **Ship It!** | Push to remote | Run `git push` |
55
+ | 7 | ๐Ÿงช **Quality Guardian** | Run tests | Run `npm test`, `pytest`, etc. |
56
+
57
+ ### Power Tools (๐ŸŸฆ Rare)
58
+
59
+ | # | Achievement | Description | How to Unlock |
60
+ |---|-------------|-------------|---------------|
61
+ | 8 | ๐Ÿค– **Delegation Master** | Use sub-agents | Use `Task` tool |
62
+ | 9 | ๐Ÿ”Œ **MCP Pioneer** | Use MCP tool | Use any `mcp__*` tool |
63
+ | 10 | ๐ŸŒ **Web Explorer** | Search the web | Use `WebSearch` tool |
64
+ | 11 | โšก **Skill Master** | Use slash command | Use `Skill` tool (e.g., `/commit`) |
65
+ | 12 | ๐Ÿ““ **Data Scientist** | Edit notebook | Use `NotebookEdit` tool |
66
+ | 13 | โš™๏ธ **Customizer** | Modify settings | Write to `.claude/settings*.json` |
67
+
68
+ ### Mastery (๐ŸŸช Epic / ๐ŸŸจ Legendary)
69
+
70
+ | # | Achievement | Description | How to Unlock |
71
+ |---|-------------|-------------|---------------|
72
+ | 14 | ๐Ÿช **Automation Architect** ๐ŸŸช | Set up hooks | Write file with `"hooks"` config |
73
+ | 15 | ๐Ÿ”„ **Loop Master** ๐ŸŸจ | Start Ralph Loop | Use `/ralph-loop` skill |
74
+
75
+ ## Rarity System
76
+
77
+ | Rarity | Color | Count | Difficulty |
78
+ |--------|-------|-------|------------|
79
+ | Common | โฌœ | 2 | First session |
80
+ | Uncommon | ๐ŸŸฉ | 5 | Regular usage |
81
+ | Rare | ๐ŸŸฆ | 6 | Power user features |
82
+ | Epic | ๐ŸŸช | 1 | Advanced automation |
83
+ | Legendary | ๐ŸŸจ | 1 | Expert level |
84
+
85
+ ## Notifications
86
+
87
+ System notifications are **auto-detected** during installation:
88
+
89
+ | OS | Method | Requirement | Auto-detected |
90
+ |----|--------|-------------|---------------|
91
+ | macOS | `osascript` | Built-in | โœ… Always |
92
+ | Linux | `notify-send` | `libnotify-bin` | โœ… Checked |
93
+ | Windows | PowerShell | Windows 10+ | โœ… Checked |
94
+ | Fallback | Terminal | None | โœ… Always |
95
+
96
+ **Note:** If system notifications aren't available, terminal notifications are used automatically.
97
+
98
+ ### Install `notify-send` on Linux
99
+
100
+ ```bash
101
+ # Ubuntu/Debian
102
+ sudo apt install libnotify-bin
103
+
104
+ # Fedora
105
+ sudo dnf install libnotify
106
+
107
+ # Arch
108
+ sudo pacman -S libnotify
109
+ ```
110
+
111
+ ## How It Works
112
+
113
+ ```
114
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
115
+ โ”‚ You use a tool (Edit, Write, Bash, Task, etc.) โ”‚
116
+ โ”‚ โ”‚ โ”‚
117
+ โ”‚ โ–ผ โ”‚
118
+ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
119
+ โ”‚ โ”‚ PostToolUse Hook โ†’ track-achievement.sh โ”‚ โ”‚
120
+ โ”‚ โ”‚ Checks tool_name, tool_input, permission_mode โ”‚ โ”‚
121
+ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
122
+ โ”‚ โ”‚ โ”‚
123
+ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
124
+ โ”‚ โ–ผ โ–ผ โ”‚
125
+ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
126
+ โ”‚ โ”‚ Match found! โ”‚ โ”‚ No match โ”‚ โ”‚
127
+ โ”‚ โ”‚ โ†’ Unlock โ”‚ โ”‚ โ†’ Continue โ”‚ โ”‚
128
+ โ”‚ โ”‚ โ†’ Notify โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
129
+ โ”‚ โ”‚ โ†’ Save state โ”‚ โ”‚
130
+ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
131
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
132
+ ```
133
+
134
+ ## Files
135
+
136
+ | Path | Description |
137
+ |------|-------------|
138
+ | `~/.claude/achievements/state.json` | Your progress & settings |
139
+ | `~/.claude/plugins/local/claude-code-achievements/` | Plugin files |
140
+ | `~/.claude/commands/achievements.md` | Slash command |
141
+
142
+ ## Settings
143
+
144
+ Edit `~/.claude/achievements/state.json`:
145
+
146
+ ```json
147
+ {
148
+ "settings": {
149
+ "language": "en", // "en" | "ko"
150
+ "notifications": true, // true | false
151
+ "notification_style": "system" // "system" | "terminal" | "both"
152
+ }
153
+ }
154
+ ```
155
+
156
+ ## Troubleshooting
157
+
158
+ ### Achievements not unlocking?
159
+
160
+ ```bash
161
+ # 1. Check hooks are registered
162
+ cat ~/.claude/settings.json | grep -A5 "hooks"
163
+
164
+ # 2. Verify plugin installed
165
+ ls ~/.claude/plugins/local/claude-code-achievements/
166
+
167
+ # 3. Check state file
168
+ cat ~/.claude/achievements/state.json
169
+ ```
170
+
171
+ ### Reset progress
172
+
173
+ ```bash
174
+ rm ~/.claude/achievements/state.json
175
+ ```
176
+
177
+ ### Reinstall
178
+
179
+ ```bash
180
+ npx claude-code-achievements
181
+ ```
182
+
183
+ ## Languages
184
+
185
+ - ๐Ÿ‡บ๐Ÿ‡ธ English
186
+ - ๐Ÿ‡ฐ๐Ÿ‡ท ํ•œ๊ตญ์–ด
187
+
188
+ ## Contributing
189
+
190
+ PRs welcome! Ideas:
191
+ - New achievements
192
+ - New languages
193
+ - Bug fixes
194
+
195
+ ## License
196
+
197
+ MIT
198
+
199
+ ---
200
+
201
+ Happy coding! ๐ŸŽฎ
package/bin/config.js ADDED
@@ -0,0 +1,285 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const os = require('os');
6
+
7
+ const STATE_FILE = path.join(os.homedir(), '.claude', 'achievements', 'state.json');
8
+
9
+ // ANSI escape codes
10
+ const ESC = '\x1b';
11
+ const CLEAR_SCREEN = `${ESC}[2J${ESC}[H`;
12
+ const HIDE_CURSOR = `${ESC}[?25l`;
13
+ const SHOW_CURSOR = `${ESC}[?25h`;
14
+ const BOLD = `${ESC}[1m`;
15
+ const DIM = `${ESC}[2m`;
16
+ const RESET = `${ESC}[0m`;
17
+ const CYAN = `${ESC}[36m`;
18
+ const GREEN = `${ESC}[32m`;
19
+ const YELLOW = `${ESC}[33m`;
20
+ const WHITE = `${ESC}[37m`;
21
+ const BG_GRAY = `${ESC}[48;5;236m`;
22
+
23
+ // Box drawing
24
+ const BOX = {
25
+ topLeft: 'โ•ญ',
26
+ topRight: 'โ•ฎ',
27
+ bottomLeft: 'โ•ฐ',
28
+ bottomRight: 'โ•ฏ',
29
+ horizontal: 'โ”€',
30
+ vertical: 'โ”‚'
31
+ };
32
+
33
+ // Load state
34
+ function loadState() {
35
+ try {
36
+ return JSON.parse(fs.readFileSync(STATE_FILE, 'utf8'));
37
+ } catch {
38
+ return {
39
+ settings: { language: 'en', notifications: true, notification_style: 'terminal' },
40
+ achievements: {},
41
+ counters: { ralph_iterations: 0, files_read: 0, edits_made: 0 },
42
+ session: { files_read_set: [], ralph_loop_active: false }
43
+ };
44
+ }
45
+ }
46
+
47
+ // Save state
48
+ function saveState(state) {
49
+ const dir = path.dirname(STATE_FILE);
50
+ if (!fs.existsSync(dir)) {
51
+ fs.mkdirSync(dir, { recursive: true });
52
+ }
53
+ fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
54
+ }
55
+
56
+ // Get terminal width
57
+ function getTermWidth() {
58
+ return process.stdout.columns || 80;
59
+ }
60
+
61
+ // Settings definition
62
+ function getSettings(state) {
63
+ return [
64
+ {
65
+ id: 'language',
66
+ label: 'Language',
67
+ value: state.settings.language === 'ko' ? 'ํ•œ๊ตญ์–ด' : 'English',
68
+ options: [
69
+ { id: 'en', label: 'English' },
70
+ { id: 'ko', label: 'ํ•œ๊ตญ์–ด' }
71
+ ]
72
+ },
73
+ {
74
+ id: 'notifications',
75
+ label: 'Notifications',
76
+ value: state.settings.notifications ? 'On' : 'Off',
77
+ options: [
78
+ { id: true, label: 'On' },
79
+ { id: false, label: 'Off' }
80
+ ]
81
+ },
82
+ {
83
+ id: 'notification_style',
84
+ label: 'Notification style',
85
+ value: state.settings.notification_style || 'terminal',
86
+ options: [
87
+ { id: 'terminal', label: 'terminal' },
88
+ { id: 'system', label: 'system' },
89
+ { id: 'both', label: 'both' }
90
+ ]
91
+ },
92
+ {
93
+ id: 'reset',
94
+ label: 'Reset all progress',
95
+ value: '',
96
+ options: [
97
+ { id: 'yes', label: 'Yes, reset' },
98
+ { id: 'no', label: 'Cancel' }
99
+ ]
100
+ }
101
+ ];
102
+ }
103
+
104
+ // Config UI
105
+ class ConfigUI {
106
+ constructor() {
107
+ this.state = loadState();
108
+ this.settings = getSettings(this.state);
109
+ this.selected = 0;
110
+ this.editMode = false;
111
+ this.editIndex = 0;
112
+ }
113
+
114
+ render() {
115
+ const width = Math.min(getTermWidth() - 4, 100);
116
+ let output = CLEAR_SCREEN;
117
+
118
+ // Title
119
+ output += `\n ${BOLD}Configure Vibecoding Achievements${RESET}\n\n`;
120
+
121
+ // Search box (decorative)
122
+ const searchWidth = width - 2;
123
+ output += ` ${BOX.topLeft}${BOX.horizontal.repeat(searchWidth)}${BOX.topRight}\n`;
124
+ output += ` ${BOX.vertical} ${DIM}โŒ• Search settings...${' '.repeat(searchWidth - 21)}${RESET}${BOX.vertical}\n`;
125
+ output += ` ${BOX.bottomLeft}${BOX.horizontal.repeat(searchWidth)}${BOX.bottomRight}\n\n`;
126
+
127
+ // Settings list
128
+ this.settings.forEach((setting, index) => {
129
+ const isSelected = index === this.selected;
130
+ const prefix = isSelected ? `${GREEN}โฏ${RESET}` : ' ';
131
+
132
+ const labelWidth = 35;
133
+ const label = setting.label.padEnd(labelWidth);
134
+
135
+ let valueDisplay = setting.value;
136
+ if (this.editMode && index === this.selected) {
137
+ // Show options in edit mode
138
+ const opts = setting.options;
139
+ valueDisplay = opts.map((opt, i) => {
140
+ if (i === this.editIndex) {
141
+ return `${GREEN}${BOLD}${opt.label}${RESET}`;
142
+ }
143
+ return `${DIM}${opt.label}${RESET}`;
144
+ }).join(' ');
145
+ } else {
146
+ valueDisplay = `${DIM}${setting.value}${RESET}`;
147
+ }
148
+
149
+ if (isSelected && !this.editMode) {
150
+ output += ` ${prefix} ${BOLD}${label}${RESET}${valueDisplay}\n`;
151
+ } else {
152
+ output += ` ${prefix} ${label}${valueDisplay}\n`;
153
+ }
154
+ });
155
+
156
+ // Help
157
+ output += `\n ${DIM}โ†‘/โ†“: Navigate Enter: Edit โ†/โ†’: Change value q: Quit${RESET}\n`;
158
+
159
+ process.stdout.write(output);
160
+ }
161
+
162
+ async run() {
163
+ if (!process.stdin.isTTY) {
164
+ console.log('This command requires an interactive terminal.');
165
+ console.log('Current settings:');
166
+ console.log(JSON.stringify(this.state.settings, null, 2));
167
+ return;
168
+ }
169
+
170
+ process.stdin.setRawMode(true);
171
+ process.stdin.resume();
172
+ process.stdin.setEncoding('utf8');
173
+ process.stdout.write(HIDE_CURSOR);
174
+
175
+ this.render();
176
+
177
+ return new Promise((resolve) => {
178
+ const cleanup = () => {
179
+ process.stdin.setRawMode(false);
180
+ process.stdin.pause();
181
+ process.stdout.write(SHOW_CURSOR);
182
+ process.stdout.write(CLEAR_SCREEN);
183
+ };
184
+
185
+ process.stdin.on('data', (key) => {
186
+ // Ctrl+C or q
187
+ if (key === '\u0003' || (key === 'q' && !this.editMode)) {
188
+ cleanup();
189
+ resolve();
190
+ return;
191
+ }
192
+
193
+ // Escape - exit edit mode
194
+ if (key === '\u001b' && this.editMode) {
195
+ this.editMode = false;
196
+ this.render();
197
+ return;
198
+ }
199
+
200
+ if (this.editMode) {
201
+ const setting = this.settings[this.selected];
202
+ const opts = setting.options;
203
+
204
+ // Left arrow
205
+ if (key === '\u001b[D') {
206
+ this.editIndex = Math.max(0, this.editIndex - 1);
207
+ this.render();
208
+ }
209
+ // Right arrow
210
+ else if (key === '\u001b[C') {
211
+ this.editIndex = Math.min(opts.length - 1, this.editIndex + 1);
212
+ this.render();
213
+ }
214
+ // Enter - apply selection
215
+ else if (key === '\r' || key === '\n') {
216
+ this.applyChange(setting.id, opts[this.editIndex].id);
217
+ this.editMode = false;
218
+ this.settings = getSettings(this.state);
219
+ this.render();
220
+ }
221
+ } else {
222
+ // Up arrow
223
+ if (key === '\u001b[A') {
224
+ this.selected = Math.max(0, this.selected - 1);
225
+ this.render();
226
+ }
227
+ // Down arrow
228
+ else if (key === '\u001b[B') {
229
+ this.selected = Math.min(this.settings.length - 1, this.selected + 1);
230
+ this.render();
231
+ }
232
+ // Enter - enter edit mode
233
+ else if (key === '\r' || key === '\n') {
234
+ const setting = this.settings[this.selected];
235
+ this.editMode = true;
236
+ // Set editIndex to current value
237
+ const currentVal = this.state.settings[setting.id];
238
+ this.editIndex = setting.options.findIndex(o => o.id === currentVal);
239
+ if (this.editIndex === -1) this.editIndex = 0;
240
+ this.render();
241
+ }
242
+ // Left/Right - quick toggle
243
+ else if (key === '\u001b[D' || key === '\u001b[C') {
244
+ const setting = this.settings[this.selected];
245
+ const opts = setting.options;
246
+ const currentVal = this.state.settings[setting.id];
247
+ let idx = opts.findIndex(o => o.id === currentVal);
248
+ if (idx === -1) idx = 0;
249
+
250
+ if (key === '\u001b[C') {
251
+ idx = Math.min(opts.length - 1, idx + 1);
252
+ } else {
253
+ idx = Math.max(0, idx - 1);
254
+ }
255
+
256
+ this.applyChange(setting.id, opts[idx].id);
257
+ this.settings = getSettings(this.state);
258
+ this.render();
259
+ }
260
+ }
261
+ });
262
+ });
263
+ }
264
+
265
+ applyChange(settingId, value) {
266
+ if (settingId === 'reset') {
267
+ if (value === 'yes') {
268
+ this.state.achievements = {};
269
+ this.state.counters = { ralph_iterations: 0, files_read: 0, edits_made: 0 };
270
+ this.state.session = { files_read_set: [], ralph_loop_active: false };
271
+ }
272
+ } else {
273
+ this.state.settings[settingId] = value;
274
+ }
275
+ saveState(this.state);
276
+ }
277
+ }
278
+
279
+ // Run
280
+ const ui = new ConfigUI();
281
+ ui.run().catch(err => {
282
+ process.stdout.write(SHOW_CURSOR);
283
+ console.error(err);
284
+ process.exit(1);
285
+ });