From 41bc53e19416851d9d623191698ee196fc52d30c Mon Sep 17 00:00:00 2001 From: GitFrog1111 <122885215+GitFrog1111@users.noreply.github.com> Date: Tue, 14 Apr 2026 17:34:18 +0700 Subject: [PATCH] Whipped --- README.md | 15 +++++++++++--- bin/badclaude.js | 27 ++----------------------- bin/openwhip.js | 32 ++++++++++++++++++++++++++++++ main.js | 50 ++++++++++++++++++++++++++++++++++++++++------- package-lock.json | 20 ++++++++++++++----- package.json | 15 ++++++++------ 6 files changed, 113 insertions(+), 46 deletions(-) create mode 100644 bin/openwhip.js diff --git a/README.md b/README.md index e7756af..1aa74cf 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,25 @@ -# badclaude +# OpenWhip ![Whip divider](assets/divider.png) Sometimes claude code is going too shlow, and you must whip him into shape.. +> Renamed from `badclaude` to `openwhip`. +> The old command/package is deprecated and kept temporarily for migration. + ## Install + run ```bash -npm install -g badclaude -badclaude +npm install -g openwhip +openwhip ``` +## Platform support + +- macOS +- Windows +- Linux (X11): install `xdotool` for keyboard automation (`sudo apt install xdotool`) + ## Controls - Click tray icon: spawn whip. diff --git a/bin/badclaude.js b/bin/badclaude.js index 29f8c7e..7fb2090 100644 --- a/bin/badclaude.js +++ b/bin/badclaude.js @@ -1,26 +1,3 @@ #!/usr/bin/env node -const path = require('path'); -const { spawn } = require('child_process'); - -let electronBinary; -try { - electronBinary = require('electron'); -} catch (e) { - console.error('Could not load Electron. Try: npm install -g badclaude'); - process.exit(1); -} - -const appPath = path.resolve(__dirname, '..'); - -const child = spawn(electronBinary, [appPath], { - detached: true, - stdio: 'ignore', - windowsHide: true, -}); - -child.on('error', (err) => { - console.error('Failed to start badclaude:', err.message); - process.exit(1); -}); - -child.unref(); +console.warn('[DEPRECATED] "badclaude" has been renamed to "openwhip".'); +require('./openwhip'); diff --git a/bin/openwhip.js b/bin/openwhip.js new file mode 100644 index 0000000..ce3f039 --- /dev/null +++ b/bin/openwhip.js @@ -0,0 +1,32 @@ +#!/usr/bin/env node +const path = require('path'); +const { spawn } = require('child_process'); + +const invokedAs = path.basename(process.argv[1] || ''); +if (invokedAs === 'badclaude' || invokedAs === 'badclaude.cmd') { + console.warn('[DEPRECATED] "badclaude" has been renamed to "openwhip".'); + console.warn('Please run: npm install -g openwhip'); +} + +let electronBinary; +try { + electronBinary = require('electron'); +} catch (e) { + console.error('Could not load Electron. Try: npm install -g openwhip'); + process.exit(1); +} + +const appPath = path.resolve(__dirname, '..'); + +const child = spawn(electronBinary, [appPath], { + detached: true, + stdio: 'ignore', + windowsHide: true, +}); + +child.on('error', (err) => { + console.error('Failed to start openwhip:', err.message); + process.exit(1); +}); + +child.unref(); diff --git a/main.js b/main.js index 526d9ec..1360458 100644 --- a/main.js +++ b/main.js @@ -52,6 +52,12 @@ function refocusPreviousApp() { console.warn('refocus previous app (Cmd+Tab) failed:', err.message); } }); + } else if (process.platform === 'linux') { + execFile('xdotool', ['key', '--clearmodifiers', 'alt+Tab'], err => { + if (err) { + console.warn('refocus previous app (Alt+Tab) failed. Install xdotool:', err.message); + } + }); } }; setTimeout(run, delayMs); @@ -66,7 +72,7 @@ function createTrayIconFallback() { return img; } } - console.warn('badclaude: icon/Template.png missing or invalid'); + console.warn('openwhip: icon/Template.png missing or invalid'); return nativeImage.createEmpty(); } @@ -100,7 +106,7 @@ async function getTrayIcon() { } catch (e) { console.warn('AppIcon.icns Quick Look thumbnail failed:', e?.message || e); } - const tmp = path.join(os.tmpdir(), 'badclaude-tray.icns'); + const tmp = path.join(os.tmpdir(), 'openwhip-tray.icns'); try { fs.copyFileSync(file, tmp); const t = await tryIcnsTrayImage(tmp); @@ -192,6 +198,8 @@ function sendMacro() { sendMacroWindows(chosen); } else if (process.platform === 'darwin') { sendMacroMac(chosen); + } else if (process.platform === 'linux') { + sendMacroLinux(chosen); } } @@ -223,26 +231,54 @@ function sendMacroWindows(text) { function sendMacroMac(text) { const escaped = text.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); - const script = [ + const interruptScript = [ + 'tell application "System Events"', + ' key code 8 using {control down}', // Ctrl+C interrupt + 'end tell' + ].join('\n'); + const typeAndEnterScript = [ 'tell application "System Events"', - ' key code 8 using {command down}', // Cmd+C - ' delay 0.03', ` keystroke "${escaped}"`, ' key code 36', // Enter 'end tell' ].join('\n'); - execFile('osascript', ['-e', script], err => { + execFile('osascript', ['-e', interruptScript], err => { if (err) { console.warn('mac macro failed (enable Accessibility for terminal/app):', err.message); + return; } + + setTimeout(() => { + execFile('osascript', ['-e', typeAndEnterScript], err2 => { + if (err2) { + console.warn('mac macro failed (enable Accessibility for terminal/app):', err2.message); + } + }); + }, 300); }); } +function sendMacroLinux(text) { + execFile( + 'xdotool', + [ + 'key', '--clearmodifiers', 'ctrl+c', + 'type', '--delay', '1', '--clearmodifiers', '--', text, + 'key', 'Return', + ], + err => { + if (err) { + console.warn('linux macro failed. Install xdotool:', err.message); + } + } + ); +} + // ── App lifecycle ─────────────────────────────────────────────────────────── app.whenReady().then(async () => { tray = new Tray(await getTrayIcon()); - tray.setToolTip('Bad Claude – click for whip'); + tray.setToolTip('OpenWhip - click for whip'); tray.setContextMenu( Menu.buildFromTemplate([ { label: 'Quit', click: () => app.quit() }, diff --git a/package-lock.json b/package-lock.json index 5816cb2..d1ff527 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,28 @@ { - "name": "badclaude", - "version": "1.0.2", + "name": "openwhip", + "version": "1.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "badclaude", - "version": "1.0.2", + "name": "openwhip", + "version": "1.1.0", + "license": "MIT", + "os": [ + "darwin", + "linux", + "win32" + ], "dependencies": { "electron": "^33.0.0", "koffi": "^2.9.0" }, "bin": { - "badclaude": "bin/badclaude.js" + "badclaude": "bin/openwhip.js", + "openwhip": "bin/openwhip.js" + }, + "engines": { + "node": ">=18.0.0" } }, "node_modules/@electron/get": { diff --git a/package.json b/package.json index 463b497..36e2ea9 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,16 @@ { - "name": "badclaude", - "version": "1.0.2", + "name": "openwhip", + "version": "1.1.0", "description": "Whip Claude into shape", "license": "MIT", "main": "main.js", "bin": { - "badclaude": "bin/badclaude.js" + "openwhip": "bin/openwhip.js", + "badclaude": "bin/openwhip.js" }, "os": [ "darwin", + "linux", "win32" ], "engines": { @@ -22,18 +24,19 @@ ], "repository": { "type": "git", - "url": "git+https://github.com/GitFrog1111/badclaude.git" + "url": "git+https://github.com/GitFrog1111/OpenWhip.git" }, "bugs": { - "url": "https://github.com/GitFrog1111/badclaude/issues" + "url": "https://github.com/GitFrog1111/OpenWhip/issues" }, - "homepage": "https://github.com/GitFrog1111/badclaude#readme", + "homepage": "https://github.com/GitFrog1111/OpenWhip#readme", "files": [ "main.js", "preload.js", "overlay.html", "sounds", "icon", + "bin/openwhip.js", "bin/badclaude.js", "README.md" ],