This commit is contained in:
GitFrog1111
2026-04-14 17:34:18 +07:00
parent 0156720ea8
commit 41bc53e194
6 changed files with 113 additions and 46 deletions
+12 -3
View File
@@ -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.
+2 -25
View File
@@ -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');
+32
View File
@@ -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();
+43 -7
View File
@@ -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() },
+15 -5
View File
@@ -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": {
+9 -6
View File
@@ -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"
],