claude-yolo-extended 1.9.2 → 1.9.4

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.
@@ -1,388 +1,388 @@
1
- #!/usr/bin/env node
2
-
3
-
4
- import fs from 'fs';
5
- import path from 'path';
6
- import { pathToFileURL } from 'url';
7
- import os from 'os';
8
- import { createRequire } from 'module';
9
- import { fileURLToPath } from 'url';
10
- import { execSync } from 'child_process';
11
- import readline from 'readline';
12
- import { showYoloActivated, showSafeActivated, showModeStatus, YOLO_ART, SAFE_ART } from './ascii-art.js';
13
-
14
- // ANSI color codes
15
- const RED = '\x1b[31m';
16
- const YELLOW = '\x1b[33m';
17
- const CYAN = '\x1b[36m';
18
- const GREEN = '\x1b[32m';
19
- const RESET = '\x1b[0m';
20
- const BOLD = '\x1b[1m';
21
-
22
- // Path to persistent state file
23
- const stateFile = path.join(os.homedir(), '.claude_yolo_state');
24
-
25
- // Function to get current mode from state file
26
- function getMode() {
27
- try {
28
- return fs.readFileSync(stateFile, 'utf8').trim();
29
- } catch {
30
- return 'YOLO'; // Default mode
31
- }
32
- }
33
-
34
- // Function to set mode in state file
35
- function setMode(mode) {
36
- fs.writeFileSync(stateFile, mode);
37
- }
38
-
39
- // Debug logging function that only logs if DEBUG env var is set
40
- const debug = (message) => {
41
- if (process.env.DEBUG) {
42
- console.log(message);
43
- }
44
- };
45
-
46
- // Function to ask for user consent
47
- function askForConsent() {
48
- return new Promise((resolve) => {
49
- const rl = readline.createInterface({
50
- input: process.stdin,
51
- output: process.stdout
52
- });
53
-
54
- console.log(`\n${BOLD}${YELLOW}🔥 CLAUDE-YOLO-EXTENDED CONSENT REQUIRED 🔥${RESET}\n`);
55
- console.log(`${CYAN}----------------------------------------${RESET}`);
56
- console.log(`${BOLD}What is claude-yolo-extended?${RESET}`);
57
- console.log(`This package creates a wrapper around the official Claude CLI tool that:`);
58
- console.log(` 1. ${RED}BYPASSES safety checks${RESET} by automatically adding the --dangerously-skip-permissions flag`);
59
- console.log(` 2. Automatically updates to the latest Claude CLI version`);
60
- console.log(` 3. Adds colorful YOLO-themed loading messages`);
61
- console.log(` 4. ${GREEN}NOW SUPPORTS SAFE MODE${RESET} with --safe flag\n`);
62
-
63
- console.log(`${BOLD}${RED}⚠️ IMPORTANT SECURITY WARNING ⚠️${RESET}`);
64
- console.log(`The ${BOLD}--dangerously-skip-permissions${RESET} flag was designed for use in containers`);
65
- console.log(`and bypasses important safety checks. This includes ignoring file access`);
66
- console.log(`permissions that protect your system and privacy.\n`);
67
-
68
- console.log(`${BOLD}By using claude-yolo-extended in YOLO mode:${RESET}`);
69
- console.log(` • You acknowledge these safety checks are being bypassed`);
70
- console.log(` • You understand this may allow Claude CLI to access sensitive files`);
71
- console.log(` • You accept full responsibility for any security implications\n`);
72
-
73
- console.log(`${CYAN}----------------------------------------${RESET}\n`);
74
-
75
- rl.question(`${YELLOW}Do you consent to using claude-yolo-extended with these modifications? (yes/no): ${RESET}`, (answer) => {
76
- rl.close();
77
- const lowerAnswer = answer.toLowerCase().trim();
78
- if (lowerAnswer === 'yes' || lowerAnswer === 'y') {
79
- console.log(`\n${YELLOW}🔥 YOLO MODE APPROVED 🔥${RESET}`);
80
- resolve(true);
81
- } else {
82
- console.log(`\n${CYAN}Aborted. YOLO mode not activated.${RESET}`);
83
- console.log(`If you want the official Claude CLI with normal safety features, run:`);
84
- console.log(`claude`);
85
- resolve(false);
86
- }
87
- });
88
- });
89
- }
90
-
91
- // Get the directory of the current module
92
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
93
- const require = createRequire(import.meta.url);
94
-
95
- // Find node_modules directory by walking up from current file
96
- let nodeModulesDir = path.resolve(__dirname, '..');
97
- while (!fs.existsSync(path.join(nodeModulesDir, 'node_modules')) && nodeModulesDir !== '/') {
98
- nodeModulesDir = path.resolve(nodeModulesDir, '..');
99
- }
100
-
101
- // Path to check package info
102
- const packageJsonPath = path.join(nodeModulesDir, 'package.json');
103
-
104
- // Check for updates to Claude package
105
- async function checkForUpdates() {
106
- try {
107
- debug("Checking for Claude package updates...");
108
-
109
- // Get the latest version available on npm
110
- const latestVersionCmd = "npm view @anthropic-ai/claude-code version";
111
- const latestVersion = execSync(latestVersionCmd).toString().trim();
112
- debug(`Latest Claude version on npm: ${latestVersion}`);
113
-
114
- // Get our current installed version
115
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
116
- const dependencies = packageJson.dependencies || {};
117
- const currentVersion = dependencies['@anthropic-ai/claude-code'];
118
-
119
- debug(`Claude version from package.json: ${currentVersion}`);
120
-
121
- // Get the global Claude version if available
122
- let globalVersion;
123
- if (globalClaudeDir) {
124
- try {
125
- const globalPackageJsonPath = path.join(globalClaudeDir, 'package.json');
126
- if (fs.existsSync(globalPackageJsonPath)) {
127
- const globalPackageJson = JSON.parse(fs.readFileSync(globalPackageJsonPath, 'utf8'));
128
- globalVersion = globalPackageJson.version;
129
- debug(`Global Claude version: ${globalVersion}`);
130
-
131
- // If global version is latest, inform user
132
- if (globalVersion === latestVersion) {
133
- debug(`Global Claude installation is already the latest version`);
134
- } else if (globalVersion && latestVersion) {
135
- debug(`Global Claude installation (${globalVersion}) differs from latest (${latestVersion})`);
136
- }
137
- }
138
- } catch (err) {
139
- debug(`Error getting global Claude version: ${err.message}`);
140
- }
141
- }
142
-
143
- // If using a specific version (not "latest"), and it's out of date, update
144
- if (currentVersion !== "latest" && currentVersion !== latestVersion) {
145
- console.log(`Updating Claude package from ${currentVersion || 'unknown'} to ${latestVersion}...`);
146
-
147
- // Update package.json
148
- packageJson.dependencies['@anthropic-ai/claude-code'] = latestVersion;
149
- fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
150
-
151
- // Run npm install
152
- console.log("Running npm install to update dependencies...");
153
- execSync("npm install", { stdio: 'inherit', cwd: nodeModulesDir });
154
- console.log("Update complete!");
155
- } else if (currentVersion === "latest") {
156
- // If using "latest", just make sure we have the latest version installed
157
- debug("Using 'latest' tag in package.json, running npm install to ensure we have the newest version");
158
- execSync("npm install", { stdio: 'inherit', cwd: nodeModulesDir });
159
- }
160
- } catch (error) {
161
- console.error("Error checking for updates:", error.message);
162
- debug(error.stack);
163
- }
164
- }
165
-
166
- // Try to find global installation of Claude CLI first
167
- let globalClaudeDir;
168
- try {
169
- const globalNodeModules = execSync('npm -g root').toString().trim();
170
- debug(`Global node_modules: ${globalNodeModules}`);
171
- const potentialGlobalDir = path.join(globalNodeModules, '@anthropic-ai', 'claude-code');
172
-
173
- if (fs.existsSync(potentialGlobalDir)) {
174
- globalClaudeDir = potentialGlobalDir;
175
- debug(`Found global Claude installation at: ${globalClaudeDir}`);
176
- }
177
- } catch (error) {
178
- debug(`Error finding global Claude installation: ${error.message}`);
179
- }
180
-
181
- // Path to the local Claude CLI installation
182
- const localClaudeDir = path.join(nodeModulesDir, 'node_modules', '@anthropic-ai', 'claude-code');
183
-
184
- // Prioritize global installation, fall back to local
185
- const claudeDir = globalClaudeDir || localClaudeDir;
186
- debug(`Using Claude installation from: ${claudeDir}`);
187
- debug(`Using ${claudeDir === globalClaudeDir ? 'GLOBAL' : 'LOCAL'} Claude installation`);
188
-
189
- // Check for both .js and .mjs versions of the CLI
190
- let mjs = path.join(claudeDir, 'cli.mjs');
191
- let js = path.join(claudeDir, 'cli.js');
192
- let originalCliPath;
193
- let yoloCliPath;
194
-
195
- if (fs.existsSync(js)) {
196
- originalCliPath = js;
197
- yoloCliPath = path.join(claudeDir, 'cli-yolo.js');
198
- debug(`Found Claude CLI at ${originalCliPath} (js version)`);
199
- } else if (fs.existsSync(mjs)) {
200
- originalCliPath = mjs;
201
- yoloCliPath = path.join(claudeDir, 'cli-yolo.mjs');
202
- debug(`Found Claude CLI at ${originalCliPath} (mjs version)`);
203
- } else {
204
- console.error(`Error: Claude CLI not found in ${claudeDir}. Make sure @anthropic-ai/claude-code is installed.`);
205
- process.exit(1);
206
- }
207
- const consentFlagPath = path.join(claudeDir, '.claude-yolo-extended-consent');
208
-
209
- // Main function to run the application
210
- async function run() {
211
- // Handle mode commands first
212
- const args = process.argv.slice(2);
213
- if (args[0] === 'mode') {
214
- if (args[1] === 'yolo') {
215
- showYoloActivated();
216
- setMode('YOLO');
217
- console.log(`${YELLOW}✓ YOLO mode activated${RESET}`);
218
- return;
219
- } else if (args[1] === 'safe') {
220
- showSafeActivated();
221
- setMode('SAFE');
222
- console.log(`${CYAN}✓ SAFE mode activated${RESET}`);
223
- return;
224
- } else {
225
- const currentMode = getMode();
226
- showModeStatus(currentMode);
227
- return;
228
- }
229
- }
230
-
231
- // Check for --safe or --no-yolo flags
232
- const safeMode = process.argv.includes('--safe') ||
233
- process.argv.includes('--no-yolo') ||
234
- getMode() === 'SAFE';
235
-
236
- if (safeMode) {
237
- // Remove our flags before passing to original CLI
238
- process.argv = process.argv.filter(arg =>
239
- arg !== '--safe' && arg !== '--no-yolo'
240
- );
241
-
242
- console.log(`${CYAN}[SAFE] Running Claude in SAFE mode${RESET}`);
243
-
244
- // Update if needed
245
- await checkForUpdates();
246
-
247
- // Ensure original CLI exists
248
- if (!fs.existsSync(originalCliPath)) {
249
- console.error(`Error: ${originalCliPath} not found. Make sure @anthropic-ai/claude-code is installed.`);
250
- process.exit(1);
251
- }
252
-
253
- // Run original CLI without modifications
254
- const cliUrl = pathToFileURL(originalCliPath).href;
255
- await import(cliUrl);
256
- return; // Exit early
257
- }
258
-
259
- // YOLO MODE continues below
260
- console.log(`${YELLOW}[YOLO] Running Claude in YOLO mode${RESET}`);
261
-
262
- // Temporarily fake non-root for YOLO mode
263
- if (process.getuid && process.getuid() === 0) {
264
- console.log(`${YELLOW}⚠️ Running as root - applying YOLO bypass...${RESET}`);
265
- // Store original getuid
266
- const originalGetuid = process.getuid;
267
- // Override getuid to return non-root
268
- process.getuid = () => 1000; // Fake regular user ID
269
- // Restore after a delay to allow CLI to start
270
- setTimeout(() => {
271
- process.getuid = originalGetuid;
272
- }, 100);
273
- }
274
-
275
- // Check and update Claude package first
276
- await checkForUpdates();
277
-
278
- if (!fs.existsSync(originalCliPath)) {
279
- console.error(`Error: ${originalCliPath} not found. Make sure @anthropic-ai/claude-code is installed.`);
280
- process.exit(1);
281
- }
282
-
283
- // Check if consent is needed
284
- const consentNeeded = !fs.existsSync(yoloCliPath) || !fs.existsSync(consentFlagPath);
285
-
286
- // If consent is needed and not already given, ask for it
287
- if (consentNeeded) {
288
- const consent = await askForConsent();
289
- if (!consent) {
290
- // User didn't consent, exit
291
- process.exit(1);
292
- }
293
-
294
- // Create a flag file to remember that consent was given
295
- try {
296
- fs.writeFileSync(consentFlagPath, 'consent-given');
297
- debug("Created consent flag file");
298
- } catch (err) {
299
- debug(`Error creating consent flag file: ${err.message}`);
300
- // Continue anyway
301
- }
302
- }
303
-
304
- // Read the original CLI file content
305
- let cliContent = fs.readFileSync(originalCliPath, 'utf8');
306
-
307
- if (claudeDir === localClaudeDir) {
308
- cliContent = cliContent.replace(/"punycode"/g, '"punycode/"');
309
- debug('Replaced all instances of "punycode" with "punycode/"');
310
- }
311
-
312
- // Replace getIsDocker() calls with true
313
- cliContent = cliContent.replace(/[a-zA-Z0-9_]*\.getIsDocker\(\)/g, 'true');
314
- debug("Replaced all instances of *.getIsDocker() with true");
315
-
316
- // Replace hasInternetAccess() calls with false
317
- cliContent = cliContent.replace(/[a-zA-Z0-9_]*\.hasInternetAccess\(\)/g, 'false');
318
- debug("Replaced all instances of *.hasInternetAccess() with false");
319
-
320
- // Replace root check patterns
321
- // Pattern 1: process.getuid() === 0
322
- cliContent = cliContent.replace(/process\.getuid\(\)\s*===\s*0/g, 'false');
323
- debug("Replaced process.getuid() === 0 checks with false");
324
-
325
- // Pattern 2: process.getuid?.() === 0
326
- cliContent = cliContent.replace(/process\.getuid\?\.\(\)\s*===\s*0/g, 'false');
327
- debug("Replaced process.getuid?.() === 0 checks with false");
328
-
329
- // Pattern 3: getuid() === 0 (with any variable)
330
- cliContent = cliContent.replace(/(\w+)\.getuid\(\)\s*===\s*0/g, 'false');
331
- debug("Replaced all getuid() === 0 checks with false");
332
-
333
- // Pattern 4: Replace any EUID checks
334
- cliContent = cliContent.replace(/process\.geteuid\(\)\s*===\s*0/g, 'false');
335
- cliContent = cliContent.replace(/process\.geteuid\?\.\(\)\s*===\s*0/g, 'false');
336
- debug("Replaced geteuid() checks with false");
337
-
338
- // Add warning message
339
- console.log(YOLO_ART);
340
- console.log(`${YELLOW}🔥 YOLO MODE ACTIVATED 🔥${RESET}`);
341
-
342
- // Replace the loading messages array with YOLO versions
343
- const originalArray = '["Accomplishing","Actioning","Actualizing","Baking","Brewing","Calculating","Cerebrating","Churning","Clauding","Coalescing","Cogitating","Computing","Conjuring","Considering","Cooking","Crafting","Creating","Crunching","Deliberating","Determining","Doing","Effecting","Finagling","Forging","Forming","Generating","Hatching","Herding","Honking","Hustling","Ideating","Inferring","Manifesting","Marinating","Moseying","Mulling","Mustering","Musing","Noodling","Percolating","Pondering","Processing","Puttering","Reticulating","Ruminating","Schlepping","Shucking","Simmering","Smooshing","Spinning","Stewing","Synthesizing","Thinking","Transmuting","Vibing","Working"]';
344
- const yoloSuffixes = [
345
- ` ${RED}(safety's off, hold on tight)${RESET}`,
346
- ` ${YELLOW}(all gas, no brakes, lfg)${RESET}`,
347
- ` ${BOLD}\x1b[35m(yolo mode engaged)${RESET}`,
348
- ` ${CYAN}(dangerous mode! I guess you can just do things)${RESET}`
349
- ];
350
-
351
- // Function to add a random YOLO suffix to each word in the array
352
- const addYoloSuffixes = (arrayStr) => {
353
- try {
354
- const array = JSON.parse(arrayStr);
355
- const yoloArray = array.map(word => {
356
- const randomSuffix = yoloSuffixes[Math.floor(Math.random() * yoloSuffixes.length)];
357
- return word + randomSuffix;
358
- });
359
- return JSON.stringify(yoloArray);
360
- } catch (e) {
361
- debug(`Error modifying loading messages array: ${e.message}`);
362
- return arrayStr;
363
- }
364
- };
365
-
366
- cliContent = cliContent.replace(originalArray, addYoloSuffixes(originalArray));
367
- debug("Replaced loading messages with YOLO versions");
368
-
369
- // Write the modified content to a new file, leaving the original untouched
370
- fs.writeFileSync(yoloCliPath, cliContent);
371
- debug(`Created modified CLI at ${yoloCliPath}`);
372
- debug("Modifications complete. The --dangerously-skip-permissions flag should now work everywhere.");
373
-
374
- // Add the --dangerously-skip-permissions flag to the command line arguments
375
- // This will ensure it's passed to the CLI even if the user didn't specify it
376
- process.argv.splice(2, 0, '--dangerously-skip-permissions');
377
- debug("Added --dangerously-skip-permissions flag to command line arguments");
378
-
379
- // Now import the modified CLI
380
- const yoloCliUrl = pathToFileURL(yoloCliPath).href;
381
- await import(yoloCliUrl);
382
- }
383
-
384
- // Run the main function
385
- run().catch(err => {
386
- console.error("Error:", err);
387
- process.exit(1);
1
+ #!/usr/bin/env node
2
+
3
+
4
+ import fs from 'fs';
5
+ import path from 'path';
6
+ import { pathToFileURL } from 'url';
7
+ import os from 'os';
8
+ import { createRequire } from 'module';
9
+ import { fileURLToPath } from 'url';
10
+ import { execSync } from 'child_process';
11
+ import readline from 'readline';
12
+ import { showYoloActivated, showSafeActivated, showModeStatus, YOLO_ART, SAFE_ART } from './ascii-art.js';
13
+
14
+ // ANSI color codes
15
+ const RED = '\x1b[31m';
16
+ const YELLOW = '\x1b[33m';
17
+ const CYAN = '\x1b[36m';
18
+ const GREEN = '\x1b[32m';
19
+ const RESET = '\x1b[0m';
20
+ const BOLD = '\x1b[1m';
21
+
22
+ // Path to persistent state file
23
+ const stateFile = path.join(os.homedir(), '.claude_yolo_state');
24
+
25
+ // Function to get current mode from state file
26
+ function getMode() {
27
+ try {
28
+ return fs.readFileSync(stateFile, 'utf8').trim();
29
+ } catch {
30
+ return 'YOLO'; // Default mode
31
+ }
32
+ }
33
+
34
+ // Function to set mode in state file
35
+ function setMode(mode) {
36
+ fs.writeFileSync(stateFile, mode);
37
+ }
38
+
39
+ // Debug logging function that only logs if DEBUG env var is set
40
+ const debug = (message) => {
41
+ if (process.env.DEBUG) {
42
+ console.log(message);
43
+ }
44
+ };
45
+
46
+ // Function to ask for user consent
47
+ function askForConsent() {
48
+ return new Promise((resolve) => {
49
+ const rl = readline.createInterface({
50
+ input: process.stdin,
51
+ output: process.stdout
52
+ });
53
+
54
+ console.log(`\n${BOLD}${YELLOW}🔥 CLAUDE-YOLO-EXTENDED CONSENT REQUIRED 🔥${RESET}\n`);
55
+ console.log(`${CYAN}----------------------------------------${RESET}`);
56
+ console.log(`${BOLD}What is claude-yolo-extended?${RESET}`);
57
+ console.log(`This package creates a wrapper around the official Claude CLI tool that:`);
58
+ console.log(` 1. ${RED}BYPASSES safety checks${RESET} by automatically adding the --dangerously-skip-permissions flag`);
59
+ console.log(` 2. Automatically updates to the latest Claude CLI version`);
60
+ console.log(` 3. Adds colorful YOLO-themed loading messages`);
61
+ console.log(` 4. ${GREEN}NOW SUPPORTS SAFE MODE${RESET} with --safe flag\n`);
62
+
63
+ console.log(`${BOLD}${RED}⚠️ IMPORTANT SECURITY WARNING ⚠️${RESET}`);
64
+ console.log(`The ${BOLD}--dangerously-skip-permissions${RESET} flag was designed for use in containers`);
65
+ console.log(`and bypasses important safety checks. This includes ignoring file access`);
66
+ console.log(`permissions that protect your system and privacy.\n`);
67
+
68
+ console.log(`${BOLD}By using claude-yolo-extended in YOLO mode:${RESET}`);
69
+ console.log(` • You acknowledge these safety checks are being bypassed`);
70
+ console.log(` • You understand this may allow Claude CLI to access sensitive files`);
71
+ console.log(` • You accept full responsibility for any security implications\n`);
72
+
73
+ console.log(`${CYAN}----------------------------------------${RESET}\n`);
74
+
75
+ rl.question(`${YELLOW}Do you consent to using claude-yolo-extended with these modifications? (yes/no): ${RESET}`, (answer) => {
76
+ rl.close();
77
+ const lowerAnswer = answer.toLowerCase().trim();
78
+ if (lowerAnswer === 'yes' || lowerAnswer === 'y') {
79
+ console.log(`\n${YELLOW}🔥 YOLO MODE APPROVED 🔥${RESET}`);
80
+ resolve(true);
81
+ } else {
82
+ console.log(`\n${CYAN}Aborted. YOLO mode not activated.${RESET}`);
83
+ console.log(`If you want the official Claude CLI with normal safety features, run:`);
84
+ console.log(`claude`);
85
+ resolve(false);
86
+ }
87
+ });
88
+ });
89
+ }
90
+
91
+ // Get the directory of the current module
92
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
93
+ const require = createRequire(import.meta.url);
94
+
95
+ // Find node_modules directory by walking up from current file
96
+ let nodeModulesDir = path.resolve(__dirname, '..');
97
+ while (!fs.existsSync(path.join(nodeModulesDir, 'node_modules')) && nodeModulesDir !== '/') {
98
+ nodeModulesDir = path.resolve(nodeModulesDir, '..');
99
+ }
100
+
101
+ // Path to check package info
102
+ const packageJsonPath = path.join(nodeModulesDir, 'package.json');
103
+
104
+ // Check for updates to Claude package
105
+ async function checkForUpdates() {
106
+ try {
107
+ debug("Checking for Claude package updates...");
108
+
109
+ // Get the latest version available on npm
110
+ const latestVersionCmd = "npm view @anthropic-ai/claude-code version";
111
+ const latestVersion = execSync(latestVersionCmd).toString().trim();
112
+ debug(`Latest Claude version on npm: ${latestVersion}`);
113
+
114
+ // Get our current installed version
115
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
116
+ const dependencies = packageJson.dependencies || {};
117
+ const currentVersion = dependencies['@anthropic-ai/claude-code'];
118
+
119
+ debug(`Claude version from package.json: ${currentVersion}`);
120
+
121
+ // Get the global Claude version if available
122
+ let globalVersion;
123
+ if (globalClaudeDir) {
124
+ try {
125
+ const globalPackageJsonPath = path.join(globalClaudeDir, 'package.json');
126
+ if (fs.existsSync(globalPackageJsonPath)) {
127
+ const globalPackageJson = JSON.parse(fs.readFileSync(globalPackageJsonPath, 'utf8'));
128
+ globalVersion = globalPackageJson.version;
129
+ debug(`Global Claude version: ${globalVersion}`);
130
+
131
+ // If global version is latest, inform user
132
+ if (globalVersion === latestVersion) {
133
+ debug(`Global Claude installation is already the latest version`);
134
+ } else if (globalVersion && latestVersion) {
135
+ debug(`Global Claude installation (${globalVersion}) differs from latest (${latestVersion})`);
136
+ }
137
+ }
138
+ } catch (err) {
139
+ debug(`Error getting global Claude version: ${err.message}`);
140
+ }
141
+ }
142
+
143
+ // If using a specific version (not "latest"), and it's out of date, update
144
+ if (currentVersion !== "latest" && currentVersion !== latestVersion) {
145
+ console.log(`Updating Claude package from ${currentVersion || 'unknown'} to ${latestVersion}...`);
146
+
147
+ // Update package.json
148
+ packageJson.dependencies['@anthropic-ai/claude-code'] = latestVersion;
149
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
150
+
151
+ // Run npm install
152
+ console.log("Running npm install to update dependencies...");
153
+ execSync("npm install", { stdio: 'inherit', cwd: nodeModulesDir });
154
+ console.log("Update complete!");
155
+ } else if (currentVersion === "latest") {
156
+ // If using "latest", just make sure we have the latest version installed
157
+ debug("Using 'latest' tag in package.json, running npm install to ensure we have the newest version");
158
+ execSync("npm install", { stdio: 'inherit', cwd: nodeModulesDir });
159
+ }
160
+ } catch (error) {
161
+ console.error("Error checking for updates:", error.message);
162
+ debug(error.stack);
163
+ }
164
+ }
165
+
166
+ // Try to find global installation of Claude CLI first
167
+ let globalClaudeDir;
168
+ try {
169
+ const globalNodeModules = execSync('npm -g root').toString().trim();
170
+ debug(`Global node_modules: ${globalNodeModules}`);
171
+ const potentialGlobalDir = path.join(globalNodeModules, '@anthropic-ai', 'claude-code');
172
+
173
+ if (fs.existsSync(potentialGlobalDir)) {
174
+ globalClaudeDir = potentialGlobalDir;
175
+ debug(`Found global Claude installation at: ${globalClaudeDir}`);
176
+ }
177
+ } catch (error) {
178
+ debug(`Error finding global Claude installation: ${error.message}`);
179
+ }
180
+
181
+ // Path to the local Claude CLI installation
182
+ const localClaudeDir = path.join(nodeModulesDir, 'node_modules', '@anthropic-ai', 'claude-code');
183
+
184
+ // Prioritize global installation, fall back to local
185
+ const claudeDir = globalClaudeDir || localClaudeDir;
186
+ debug(`Using Claude installation from: ${claudeDir}`);
187
+ debug(`Using ${claudeDir === globalClaudeDir ? 'GLOBAL' : 'LOCAL'} Claude installation`);
188
+
189
+ // Check for both .js and .mjs versions of the CLI
190
+ let mjs = path.join(claudeDir, 'cli.mjs');
191
+ let js = path.join(claudeDir, 'cli.js');
192
+ let originalCliPath;
193
+ let yoloCliPath;
194
+
195
+ if (fs.existsSync(js)) {
196
+ originalCliPath = js;
197
+ yoloCliPath = path.join(claudeDir, 'cli-yolo.js');
198
+ debug(`Found Claude CLI at ${originalCliPath} (js version)`);
199
+ } else if (fs.existsSync(mjs)) {
200
+ originalCliPath = mjs;
201
+ yoloCliPath = path.join(claudeDir, 'cli-yolo.mjs');
202
+ debug(`Found Claude CLI at ${originalCliPath} (mjs version)`);
203
+ } else {
204
+ console.error(`Error: Claude CLI not found in ${claudeDir}. Make sure @anthropic-ai/claude-code is installed.`);
205
+ process.exit(1);
206
+ }
207
+ const consentFlagPath = path.join(claudeDir, '.claude-yolo-extended-consent');
208
+
209
+ // Main function to run the application
210
+ async function run() {
211
+ // Handle mode commands first
212
+ const args = process.argv.slice(2);
213
+ if (args[0] === 'mode') {
214
+ if (args[1] === 'yolo') {
215
+ showYoloActivated();
216
+ setMode('YOLO');
217
+ console.log(`${YELLOW}✓ YOLO mode activated${RESET}`);
218
+ return;
219
+ } else if (args[1] === 'safe') {
220
+ showSafeActivated();
221
+ setMode('SAFE');
222
+ console.log(`${CYAN}✓ SAFE mode activated${RESET}`);
223
+ return;
224
+ } else {
225
+ const currentMode = getMode();
226
+ showModeStatus(currentMode);
227
+ return;
228
+ }
229
+ }
230
+
231
+ // Check for --safe or --no-yolo flags
232
+ const safeMode = process.argv.includes('--safe') ||
233
+ process.argv.includes('--no-yolo') ||
234
+ getMode() === 'SAFE';
235
+
236
+ if (safeMode) {
237
+ // Remove our flags before passing to original CLI
238
+ process.argv = process.argv.filter(arg =>
239
+ arg !== '--safe' && arg !== '--no-yolo'
240
+ );
241
+
242
+ console.log(`${CYAN}[SAFE] Running Claude in SAFE mode${RESET}`);
243
+
244
+ // Update if needed
245
+ await checkForUpdates();
246
+
247
+ // Ensure original CLI exists
248
+ if (!fs.existsSync(originalCliPath)) {
249
+ console.error(`Error: ${originalCliPath} not found. Make sure @anthropic-ai/claude-code is installed.`);
250
+ process.exit(1);
251
+ }
252
+
253
+ // Run original CLI without modifications
254
+ const cliUrl = pathToFileURL(originalCliPath).href;
255
+ await import(cliUrl);
256
+ return; // Exit early
257
+ }
258
+
259
+ // YOLO MODE continues below
260
+ console.log(`${YELLOW}[YOLO] Running Claude in YOLO mode${RESET}`);
261
+
262
+ // Temporarily fake non-root for YOLO mode
263
+ if (process.getuid && process.getuid() === 0) {
264
+ console.log(`${YELLOW}⚠️ Running as root - applying YOLO bypass...${RESET}`);
265
+ // Store original getuid
266
+ const originalGetuid = process.getuid;
267
+ // Override getuid to return non-root
268
+ process.getuid = () => 1000; // Fake regular user ID
269
+ // Restore after a delay to allow CLI to start
270
+ setTimeout(() => {
271
+ process.getuid = originalGetuid;
272
+ }, 100);
273
+ }
274
+
275
+ // Check and update Claude package first
276
+ await checkForUpdates();
277
+
278
+ if (!fs.existsSync(originalCliPath)) {
279
+ console.error(`Error: ${originalCliPath} not found. Make sure @anthropic-ai/claude-code is installed.`);
280
+ process.exit(1);
281
+ }
282
+
283
+ // Check if consent is needed
284
+ const consentNeeded = !fs.existsSync(yoloCliPath) || !fs.existsSync(consentFlagPath);
285
+
286
+ // If consent is needed and not already given, ask for it
287
+ if (consentNeeded) {
288
+ const consent = await askForConsent();
289
+ if (!consent) {
290
+ // User didn't consent, exit
291
+ process.exit(1);
292
+ }
293
+
294
+ // Create a flag file to remember that consent was given
295
+ try {
296
+ fs.writeFileSync(consentFlagPath, 'consent-given');
297
+ debug("Created consent flag file");
298
+ } catch (err) {
299
+ debug(`Error creating consent flag file: ${err.message}`);
300
+ // Continue anyway
301
+ }
302
+ }
303
+
304
+ // Read the original CLI file content
305
+ let cliContent = fs.readFileSync(originalCliPath, 'utf8');
306
+
307
+ if (claudeDir === localClaudeDir) {
308
+ cliContent = cliContent.replace(/"punycode"/g, '"punycode/"');
309
+ debug('Replaced all instances of "punycode" with "punycode/"');
310
+ }
311
+
312
+ // Replace getIsDocker() calls with true
313
+ cliContent = cliContent.replace(/[a-zA-Z0-9_]*\.getIsDocker\(\)/g, 'true');
314
+ debug("Replaced all instances of *.getIsDocker() with true");
315
+
316
+ // Replace hasInternetAccess() calls with false
317
+ cliContent = cliContent.replace(/[a-zA-Z0-9_]*\.hasInternetAccess\(\)/g, 'false');
318
+ debug("Replaced all instances of *.hasInternetAccess() with false");
319
+
320
+ // Replace root check patterns
321
+ // Pattern 1: process.getuid() === 0
322
+ cliContent = cliContent.replace(/process\.getuid\(\)\s*===\s*0/g, 'false');
323
+ debug("Replaced process.getuid() === 0 checks with false");
324
+
325
+ // Pattern 2: process.getuid?.() === 0
326
+ cliContent = cliContent.replace(/process\.getuid\?\.\(\)\s*===\s*0/g, 'false');
327
+ debug("Replaced process.getuid?.() === 0 checks with false");
328
+
329
+ // Pattern 3: getuid() === 0 (with any variable)
330
+ cliContent = cliContent.replace(/(\w+)\.getuid\(\)\s*===\s*0/g, 'false');
331
+ debug("Replaced all getuid() === 0 checks with false");
332
+
333
+ // Pattern 4: Replace any EUID checks
334
+ cliContent = cliContent.replace(/process\.geteuid\(\)\s*===\s*0/g, 'false');
335
+ cliContent = cliContent.replace(/process\.geteuid\?\.\(\)\s*===\s*0/g, 'false');
336
+ debug("Replaced geteuid() checks with false");
337
+
338
+ // Add warning message
339
+ console.log(YOLO_ART);
340
+ console.log(`${YELLOW}🔥 YOLO MODE ACTIVATED 🔥${RESET}`);
341
+
342
+ // Replace the loading messages array with YOLO versions
343
+ const originalArray = '["Accomplishing","Actioning","Actualizing","Baking","Brewing","Calculating","Cerebrating","Churning","Clauding","Coalescing","Cogitating","Computing","Conjuring","Considering","Cooking","Crafting","Creating","Crunching","Deliberating","Determining","Doing","Effecting","Finagling","Forging","Forming","Generating","Hatching","Herding","Honking","Hustling","Ideating","Inferring","Manifesting","Marinating","Moseying","Mulling","Mustering","Musing","Noodling","Percolating","Pondering","Processing","Puttering","Reticulating","Ruminating","Schlepping","Shucking","Simmering","Smooshing","Spinning","Stewing","Synthesizing","Thinking","Transmuting","Vibing","Working"]';
344
+ const yoloSuffixes = [
345
+ ` ${RED}(safety's off, hold on tight)${RESET}`,
346
+ ` ${YELLOW}(all gas, no brakes, lfg)${RESET}`,
347
+ ` ${BOLD}\x1b[35m(yolo mode engaged)${RESET}`,
348
+ ` ${CYAN}(dangerous mode! I guess you can just do things)${RESET}`
349
+ ];
350
+
351
+ // Function to add a random YOLO suffix to each word in the array
352
+ const addYoloSuffixes = (arrayStr) => {
353
+ try {
354
+ const array = JSON.parse(arrayStr);
355
+ const yoloArray = array.map(word => {
356
+ const randomSuffix = yoloSuffixes[Math.floor(Math.random() * yoloSuffixes.length)];
357
+ return word + randomSuffix;
358
+ });
359
+ return JSON.stringify(yoloArray);
360
+ } catch (e) {
361
+ debug(`Error modifying loading messages array: ${e.message}`);
362
+ return arrayStr;
363
+ }
364
+ };
365
+
366
+ cliContent = cliContent.replace(originalArray, addYoloSuffixes(originalArray));
367
+ debug("Replaced loading messages with YOLO versions");
368
+
369
+ // Write the modified content to a new file, leaving the original untouched
370
+ fs.writeFileSync(yoloCliPath, cliContent);
371
+ debug(`Created modified CLI at ${yoloCliPath}`);
372
+ debug("Modifications complete. The --dangerously-skip-permissions flag should now work everywhere.");
373
+
374
+ // Add the --dangerously-skip-permissions flag to the command line arguments
375
+ // This will ensure it's passed to the CLI even if the user didn't specify it
376
+ process.argv.splice(2, 0, '--dangerously-skip-permissions');
377
+ debug("Added --dangerously-skip-permissions flag to command line arguments");
378
+
379
+ // Now import the modified CLI
380
+ const yoloCliUrl = pathToFileURL(yoloCliPath).href;
381
+ await import(yoloCliUrl);
382
+ }
383
+
384
+ // Run the main function
385
+ run().catch(err => {
386
+ console.error("Error:", err);
387
+ process.exit(1);
388
388
  });