cc-caffeine 0.2.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 ADDED
@@ -0,0 +1,185 @@
1
+ # cc-caffeine ☕⚡
2
+
3
+ **Transform your 9-to-5 into 9:30-to-4:30.** Arrive 30min later, leave 30min earlier, while getting the same work done because **Claude Code stays powered in your backpack while commuting.**
4
+
5
+ Work smarter, not longer.
6
+
7
+ ## 🌍 The Modern Developer's Freedom
8
+
9
+ Tired of your laptop going to sleep during that perfect coding session because you left your desk 10 minutes? Frustrated when Claude Code disconnects mid-commute because your computer decided it was "idle"?
10
+
11
+ **cc-caffeine is your personal rebellion against screen timeout.** It keeps your machine awake so you can:
12
+
13
+ - 🚇 Code on the RER between Paris and suburbs
14
+ - ☕ Sip a latte at Starbucks during 3-hour debugging sessions
15
+ - 🚴‍♂️ Pedal to the coworking space while maintaining your active connection
16
+ - 📱 Respond to your girlfriend calls during work hours, without Claude Code interruptions
17
+
18
+ ## 🎯 Installation
19
+
20
+ ```bash
21
+ /plugin marketplace add samber/cc
22
+ /plugin install cc-caffeine@samber
23
+ ```
24
+
25
+ ## 🌟 The Nomad Developer Manifesto
26
+
27
+ > "I'll never choose between coding and traveling again. With cc-caffeine, I can do both. My laptop will never sleep while I traverse cities in 5G, my Claude Code will stay connected in my backpack, and my productivity will soar. The future of mobile development is here, and it smells like coffee."
28
+
29
+ ## ✨ Why It's Pure Magic
30
+
31
+ **Automatic Intelligence**: cc-caffeine knows when Claude Code is working and prevents your computer from sleeping. Period.
32
+
33
+ **System Tray Chic**: A tiny ☕️ icon in your status bar to know instantly if you're protected.
34
+
35
+ **Perfect Sessions**: Multiple simultaneous Claude Code sessions? No problem.
36
+
37
+ **Zero Configuration**: Install, run, forget. It's like coffee, but for your computer.
38
+
39
+ ## 🎯 Use Cases That Will Change Your Life
40
+
41
+ ### ☕ **The Coffee Shop Marathon**
42
+ - 3 hours of focus without ever losing your connection
43
+ - No more waking your screen every 5 minutes
44
+ - Baristas will recognize you as "the developer who never sleeps"
45
+ - Your productivity increases proportionally to your caffeine consumption
46
+
47
+ ### 🏠 **Flexible Remote Work**
48
+ - Transform your balcony into an outdoor office
49
+ - Code from the terrace in fresh air
50
+ - No more choosing between "work" and "enjoy the sunshine"
51
+ - Your boss will think you're working 24/7 (that's an advantage, right?)
52
+
53
+ ## 🛠️ Technical Features (With Style)
54
+
55
+ - **🎯 Session-Based Management**: Intelligently manages multiple simultaneous Claude Code sessions
56
+ - **🔄 Auto-Cleanup**: Forget to disable - sessions automatically expire after 15 minutes without tool call or user input
57
+ - **🚀 Headless**: Just an elegant discreet system tray icon
58
+ - **⚡ Native Sleep Prevention**: Electron's power management - cross-platform sleep prevention
59
+ - **🍎 Cross-Platform**: Works on macOS, Linux, and Windows (yes, even Windows!)
60
+
61
+ ## 🎭 Claude Code Integration
62
+
63
+ Hooks will be configured automatically if you import the project as a Claude Code plugin.
64
+
65
+ Otherwise, configure your Claude Code hooks for a seamless experience:
66
+
67
+ ```json
68
+ {
69
+ "UserPromptSubmit": [
70
+ {
71
+ "hooks": [
72
+ {
73
+ "type": "command",
74
+ "command": "npx cc-caffeine caffeinate"
75
+ }
76
+ ]
77
+ }
78
+ ],
79
+ "PreToolUse": [
80
+ {
81
+ "hooks": [
82
+ {
83
+ "type": "command",
84
+ "command": "npx cc-caffeine caffeinate"
85
+ }
86
+ ]
87
+ }
88
+ ],
89
+ "PostToolUse": [
90
+ {
91
+ "hooks": [
92
+ {
93
+ "type": "command",
94
+ "command": "npx cc-caffeine caffeinate"
95
+ }
96
+ ]
97
+ }
98
+ ],
99
+ "Notification": [
100
+ {
101
+ "hooks": [
102
+ {
103
+ "type": "command",
104
+ "command": "npx cc-caffeine uncaffeinate"
105
+ }
106
+ ]
107
+ }
108
+ ],
109
+ "Stop": [
110
+ {
111
+ "hooks": [
112
+ {
113
+ "type": "command",
114
+ "command": "npx cc-caffeine uncaffeinate"
115
+ }
116
+ ]
117
+ }
118
+ ],
119
+ "SessionEnd": [
120
+ {
121
+ "hooks": [
122
+ {
123
+ "type": "command",
124
+ "command": "npx cc-caffeine uncaffeinate"
125
+ }
126
+ ]
127
+ }
128
+ ]
129
+ }
130
+ ```
131
+
132
+ ## 💡 The Secret Sauce
133
+
134
+ cc-caffeine uses an intelligent client-server approach:
135
+
136
+ 1. **Lightweight Client** (`caffeinate`/`uncaffeinate`) - No Electron loading, just fast JSON writes
137
+ 2. **System Server** (`server`) - Headless Electron app that monitors sessions and manages power
138
+ 3. **Communication** - JSON file with atomic locking for perfect coordination
139
+
140
+ ## 📋 Requirements
141
+
142
+ - Node.js >= 14.0.0 (your coffee of choice)
143
+ - Electron (included automatically, like sugar in your espresso)
144
+ - A burning desire to code everywhere, all the time
145
+
146
+ ## 🚀 Run without Claude Code
147
+
148
+ ```bash
149
+ # Start server + system tray
150
+ # (optional - will be started automatically)
151
+ npx cc-caffeine server
152
+
153
+ claude -p 'Write 10 pages of "lorem ipsum"'
154
+
155
+ npx cc-caffeine status
156
+ ```
157
+
158
+ Manual switch:
159
+
160
+ ```bash
161
+ # Activate caffeine for your coding session
162
+ echo '{"session_id": "session-abcd"}' | npx cc-caffeine caffeinate
163
+
164
+ # Your session is now protected!
165
+ # Claude can keep working while you sip coffee
166
+
167
+ # When you're done (or after 15 minutes of auto-cleanup)
168
+ echo '{"session_id": "session-abcd"}' | npx cc-caffeine uncaffeinate
169
+ ```
170
+
171
+ ## 💫 Fuel the Revolution
172
+
173
+ - ⭐️ **Star this repo** - Your star powers the caffeine engine!
174
+ - ☕️ **Buy me a coffee** - I'll literally use it to build more features while drinking actual coffee
175
+ - 🚀 **Sponsor the revolution** - Help me defeat screen timeouts worldwide!
176
+
177
+ [![💖 GitHub Sponsors](https://img.shields.io/github/sponsors/samber?style=for-the-badge)](https://github.com/sponsors/samber)
178
+
179
+ *Every sponsor gets a virtual high-five and the knowledge that somewhere, a developer is "coding" from a ski track because of you.* ✨
180
+
181
+ **PS**: If you encounter bugs, remember that even the best coffee has some grounds sometimes. But most of the time, it works like thunder. ⚡☕
182
+
183
+ ## 📄 License
184
+
185
+ MIT - Use it, modify it, share it. Like good coffee, it's meant to be shared.
Binary file
@@ -0,0 +1,10 @@
1
+ <svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
2
+ <!-- Coffee cup outline - smaller cup with more margin -->
3
+ <path d="M8 10 L8 22 L10 24 L22 24 L24 22 L24 10 Z"
4
+ fill="none" stroke="#FF6B35" stroke-width="2" stroke-linejoin="miter"/>
5
+ <!-- Coffee handle - smaller handle with margin -->
6
+ <path d="M24 14 L28 14 L28 18 L24 18 Z"
7
+ fill="none" stroke="#FF6B35" stroke-width="2" stroke-linejoin="miter"/>
8
+ <!-- Coffee residue - smaller with more margin -->
9
+ <line x1="10" y1="22" x2="22" y2="22" stroke="#FF6B35" stroke-width="1" opacity="0.5"/>
10
+ </svg>
Binary file
@@ -0,0 +1,15 @@
1
+ <svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
2
+ <!-- Coffee cup - smaller cup with more margin -->
3
+ <path d="M8 10 L8 22 L10 24 L22 24 L24 22 L24 10 Z"
4
+ fill="#FF6B35"/>
5
+ <!-- Coffee liquid - smaller liquid with margin -->
6
+ <path d="M10 14 L10 18 L12 20 L20 20 L22 18 L22 14 Z"
7
+ fill="#E55A2B"/>
8
+ <!-- Coffee handle - smaller handle with margin -->
9
+ <path d="M24 14 L28 14 L28 18 L24 18 Z"
10
+ fill="#FF6B35"/>
11
+ <!-- Steam - simple flat lines with more margin -->
12
+ <line x1="12" y1="6" x2="12" y2="2" stroke="#FF6B35" stroke-width="1" opacity="0.7"/>
13
+ <line x1="16" y1="6" x2="16" y2="2" stroke="#FF6B35" stroke-width="1" opacity="0.7"/>
14
+ <line x1="20" y1="6" x2="20" y2="2" stroke="#FF6B35" stroke-width="1" opacity="0.7"/>
15
+ </svg>
package/caffeine.js ADDED
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env node
2
+
3
+ const path = require('path');
4
+ const os = require('os');
5
+ const fs = require('fs');
6
+
7
+ /**
8
+ * Main entry point for CC-Caffeine application
9
+ *
10
+ * This file contains the main() function and orchestrates all modules.
11
+ * All functionality has been split into separate modules for better organization.
12
+ */
13
+
14
+ const { handleCaffeinate, handleUncaffeinate, handleStatus, handleVersion, handleUsage } = require('./src/commands');
15
+ const { handleServer } = require('./src/server');
16
+
17
+ const CONFIG_DIR = path.join(os.homedir(), '.claude', 'plugins', 'cc-caffeine');
18
+
19
+ const ensureConfigDir = () => {
20
+ try {
21
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
22
+ } catch (error) {
23
+ if (error.code !== 'EEXIST') {
24
+ throw error;
25
+ }
26
+ }
27
+ };
28
+
29
+ /**
30
+ * Main application entry point
31
+ * Handles command routing and delegates to appropriate modules
32
+ */
33
+ const main = async () => {
34
+ ensureConfigDir();
35
+
36
+ const command = process.argv[2];
37
+
38
+ switch (command) {
39
+ case 'caffeinate':
40
+ await handleCaffeinate();
41
+ break;
42
+ case 'uncaffeinate':
43
+ await handleUncaffeinate();
44
+ break;
45
+ case 'server':
46
+ await handleServer();
47
+ break;
48
+ case 'status':
49
+ await handleStatus();
50
+ break;
51
+ case 'version':
52
+ await handleVersion();
53
+ break;
54
+ default:
55
+ await handleUsage();
56
+ }
57
+ };
58
+
59
+ // Handle uncaught errors gracefully
60
+ main().catch(error => {
61
+ console.error('Fatal error:', error);
62
+ process.exit(1);
63
+ });
@@ -0,0 +1,67 @@
1
+ {
2
+ "description": "Prevents your computer from sleeping while Claude Code works hard",
3
+ "hooks": {
4
+ "UserPromptSubmit": [
5
+ {
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "npx cc-caffeine caffeinate"
10
+ }
11
+ ]
12
+ }
13
+ ],
14
+ "PreToolUse": [
15
+ {
16
+ "matcher": "*",
17
+ "hooks": [
18
+ {
19
+ "type": "command",
20
+ "command": "npx cc-caffeine caffeinate"
21
+ }
22
+ ]
23
+ }
24
+ ],
25
+ "PostToolUse": [
26
+ {
27
+ "matcher": "*",
28
+ "hooks": [
29
+ {
30
+ "type": "command",
31
+ "command": "npx cc-caffeine caffeinate"
32
+ }
33
+ ]
34
+ }
35
+ ],
36
+ "Notification": [
37
+ {
38
+ "hooks": [
39
+ {
40
+ "type": "command",
41
+ "command": "npx cc-caffeine uncaffeinate"
42
+ }
43
+ ]
44
+ }
45
+ ],
46
+ "Stop": [
47
+ {
48
+ "hooks": [
49
+ {
50
+ "type": "command",
51
+ "command": "npx cc-caffeine uncaffeinate"
52
+ }
53
+ ]
54
+ }
55
+ ],
56
+ "SessionEnd": [
57
+ {
58
+ "hooks": [
59
+ {
60
+ "type": "command",
61
+ "command": "npx cc-caffeine uncaffeinate"
62
+ }
63
+ ]
64
+ }
65
+ ]
66
+ }
67
+ }
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "cc-caffeine",
3
+ "version": "0.2.0",
4
+ "description": "Prevents your computer from sleeping while Claude Code works hard",
5
+ "main": "caffeine.js",
6
+ "bin": {
7
+ "cc-caffeine": "./caffeine.js"
8
+ },
9
+ "scripts": {
10
+ "start": "npm run server",
11
+ "caffeinate": "node caffeine.js caffeinate",
12
+ "uncaffeinate": "node caffeine.js uncaffeinate",
13
+ "status": "node caffeine.js status",
14
+ "version": "node caffeine.js version",
15
+ "server": "npx electron caffeine.js server",
16
+ "lint": "eslint --fix *.js src/*.js || echo 'eslint not installed'",
17
+ "format": "prettier --write *.js src/*.js || echo 'prettier not installed'"
18
+ },
19
+ "keywords": [
20
+ "claude-code",
21
+ "claude-code-plugin",
22
+ "plugin",
23
+ "nap",
24
+ "sleep",
25
+ "sleep-prevention",
26
+ "caffeine",
27
+ "caffeinate",
28
+ "system-tray",
29
+ "electron"
30
+ ],
31
+ "author": {
32
+ "name": "Samuel Berthe",
33
+ "email": "hey@samuel-berthe.fr",
34
+ "url": "https://samuel-berthe.fr"
35
+ },
36
+ "license": "MIT",
37
+ "dependencies": {
38
+ "electron": "^38.2.2",
39
+ "proper-lockfile": "^4.1.2"
40
+ },
41
+ "devDependencies": {
42
+ "eslint": "^9.37.0",
43
+ "prettier": "^3.6.2"
44
+ },
45
+ "engines": {
46
+ "node": ">=14.0.0"
47
+ },
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "https://github.com/samber/cc-caffeine.git"
51
+ },
52
+ "os": [
53
+ "darwin",
54
+ "linux",
55
+ "win32"
56
+ ]
57
+ }
@@ -0,0 +1,168 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Commands module - Handles all command-line interface functionality
5
+ */
6
+
7
+ const path = require('path');
8
+ const fs = require('fs');
9
+
10
+ const { addSessionWithLock, removeSessionWithLock, getActiveSessionsWithLock } = require('./session');
11
+ const { isServerRunningWithLock } = require('./pid');
12
+ const { runServerProcessIfNotStarted } = require('./server');
13
+
14
+ /**
15
+ * Handle session commands with JSON input from Claude Code hooks
16
+ */
17
+ const handleSessionCommand = async (action, sessionOperation) => {
18
+ try {
19
+ // Read session_id from stdin (Claude Code hook format)
20
+ let input = '';
21
+ process.stdin.setEncoding('utf8');
22
+
23
+ await new Promise((resolve, reject) => {
24
+ process.stdin.on('data', chunk => (input += chunk));
25
+ process.stdin.on('end', resolve);
26
+ process.stdin.on('error', reject);
27
+ });
28
+
29
+ const data = JSON.parse(input);
30
+ const sessionId = data.session_id;
31
+
32
+ if (!sessionId) {
33
+ console.error('Error: session_id required in JSON input');
34
+ process.exit(1);
35
+ }
36
+
37
+ // Execute the session operation
38
+ const result = await sessionOperation(sessionId);
39
+
40
+ // For caffeinate command, ensure server is running
41
+ if (action === 'caffeinate') {
42
+ await runServerProcessIfNotStarted();
43
+ }
44
+
45
+ console.error(
46
+ `${action === 'caffeinate' ? 'Enabled' : 'Disabled'} caffeine for session: ${sessionId}`
47
+ );
48
+
49
+ // Log cleanup results if any
50
+ if (result.cleaned_sessions > 0) {
51
+ console.error(`Cleaned up ${result.cleaned_sessions} expired sessions`);
52
+ }
53
+
54
+ return result;
55
+ } catch (error) {
56
+ console.error('Error:', error.message);
57
+ process.exit(1);
58
+ }
59
+ };
60
+
61
+ /**
62
+ * Handle caffeinate command
63
+ */
64
+ const handleCaffeinate = () => {
65
+ return handleSessionCommand('caffeinate', addSessionWithLock);
66
+ };
67
+
68
+ /**
69
+ * Handle uncaffeinate command
70
+ */
71
+ const handleUncaffeinate = () => {
72
+ return handleSessionCommand('uncaffeinate', removeSessionWithLock);
73
+ };
74
+
75
+ /**
76
+ * Handle version command - show version from package.json and plugin.json
77
+ */
78
+ const handleVersion = () => {
79
+ try {
80
+ // Read package.json
81
+ const packagePath = path.join(__dirname, '..', 'package.json');
82
+ const packageData = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
83
+ const packageVersion = packageData.version || 'unknown';
84
+
85
+ // Read plugin.json
86
+ const pluginPath = path.join(__dirname, '..', '.claude-plugin', 'plugin.json');
87
+ let pluginVersion = 'unknown';
88
+
89
+ try {
90
+ const pluginData = JSON.parse(fs.readFileSync(pluginPath, 'utf8'));
91
+ pluginVersion = pluginData.version || 'unknown';
92
+ } catch (error) {
93
+ pluginVersion = 'not found';
94
+ }
95
+
96
+ console.error('=== CC-Caffeine Version ===');
97
+ console.error(`Package version: ${packageVersion}`);
98
+ console.error(`Plugin version: ${pluginVersion}`);
99
+
100
+ if (packageVersion !== pluginVersion && pluginVersion !== 'not found') {
101
+ console.error('⚠️ Warning: Package and plugin versions do not match!');
102
+ }
103
+ } catch (error) {
104
+ console.error('Error getting version:', error.message);
105
+ process.exit(1);
106
+ }
107
+ };
108
+
109
+ /**
110
+ * Handle status command - show current sessions and server status
111
+ */
112
+ const handleStatus = async () => {
113
+ try {
114
+ const serverRunning = await isServerRunningWithLock();
115
+ const activeSessions = await getActiveSessionsWithLock();
116
+
117
+ console.error('=== CC-Caffeine Status ===');
118
+ console.error(`Server Status: ${serverRunning ? '✅ Running' : '❌ Stopped'}`);
119
+ console.error(`Active Sessions: ${activeSessions.length}`);
120
+
121
+ if (activeSessions.length > 0) {
122
+ console.error('\nActive Sessions:');
123
+ activeSessions.forEach((session, index) => {
124
+ const created = new Date(session.created_at).toLocaleString();
125
+ const lastActivity = new Date(session.last_activity).toLocaleString();
126
+ console.error(` ${index + 1}. ${session.id}`);
127
+ console.error(` Created: ${created}`);
128
+ console.error(` Last Activity: ${lastActivity}`);
129
+ if (session.project_dir) {
130
+ console.error(` Project: ${session.project_dir}`);
131
+ }
132
+ });
133
+ }
134
+
135
+ console.error('\nSession timeout: 15 minutes of inactivity');
136
+ } catch (error) {
137
+ console.error('Error getting status:', error.message);
138
+ process.exit(1);
139
+ }
140
+ };
141
+
142
+ /**
143
+ * Show usage help
144
+ */
145
+ const handleUsage = () => {
146
+ console.error('Usage: npx electron caffeine.js [caffeinate|uncaffeinate|server|status|version]');
147
+ console.error('');
148
+ console.error('Commands:');
149
+ console.error(' caffeinate [session_id] - Enable caffeine for current session');
150
+ console.error(' uncaffeinate [session_id] - Disable caffeine for current session');
151
+ console.error(' server - Start caffeine server with system tray');
152
+ console.error(' status - Show current status and active sessions');
153
+ console.error(' version - Show version information from package.json and plugin.json');
154
+ process.exit(1);
155
+ };
156
+
157
+ // Export command handlers and utilities
158
+ module.exports = {
159
+ // Command handlers
160
+ handleCaffeinate,
161
+ handleUncaffeinate,
162
+ handleStatus,
163
+ handleVersion,
164
+ handleUsage,
165
+
166
+ // Session operations
167
+ handleSessionCommand
168
+ };