@bash0816/claude-code 2.1.159-9 → 2.1.161

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,257 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const EventEmitter = require('events');
5
+
6
+ const OFFICIAL_PACKAGE = '@anthropic-ai/claude-code';
7
+ const BLOCK_MESSAGE = [
8
+ 'Official native self-update is disabled on Termux.',
9
+ 'Run: claude update',
10
+ ].join('\n');
11
+
12
+ function lastPathSegment(value) {
13
+ return String(value || '').replace(/\\/g, '/').split('/').pop();
14
+ }
15
+
16
+ function isNpmCommand(command) {
17
+ const text = String(command || '');
18
+ const leaf = lastPathSegment(text);
19
+ return leaf === 'npm' || leaf === 'npm-cli.js';
20
+ }
21
+
22
+ function isPnpmCommand(command) {
23
+ return lastPathSegment(command) === 'pnpm';
24
+ }
25
+
26
+ function isYarnCommand(command) {
27
+ const leaf = lastPathSegment(command);
28
+ return leaf === 'yarn' || leaf === 'yarnpkg';
29
+ }
30
+
31
+ function isNpxCommand(command) {
32
+ const leaf = lastPathSegment(command);
33
+ return leaf === 'npx' || leaf === 'pnpx';
34
+ }
35
+
36
+ function isEnvCommand(command) {
37
+ return lastPathSegment(command) === 'env';
38
+ }
39
+
40
+ function isCorepackCommand(command) {
41
+ return lastPathSegment(command) === 'corepack';
42
+ }
43
+
44
+ function isShellCommand(command) {
45
+ const leaf = lastPathSegment(command);
46
+ return leaf === 'sh' || leaf === 'bash';
47
+ }
48
+
49
+ function isSupportedOperation(value) {
50
+ return ['install', 'i', 'add', 'update', 'up', 'upgrade'].includes(String(value || ''));
51
+ }
52
+
53
+ function isOfficialTarget(value) {
54
+ return String(value || '') === OFFICIAL_PACKAGE || String(value || '').startsWith(`${OFFICIAL_PACKAGE}@`);
55
+ }
56
+
57
+ function normalizeCommand(command, args) {
58
+ let normalizedCommand = command;
59
+ let normalizedArgs = Array.isArray(args) ? args.slice() : [];
60
+
61
+ if (isEnvCommand(normalizedCommand)) {
62
+ while (normalizedArgs.length > 0 && /^[A-Za-z_][A-Za-z0-9_]*=.*/.test(String(normalizedArgs[0]))) {
63
+ normalizedArgs.shift();
64
+ }
65
+ if (normalizedArgs.length === 0) return { command: normalizedCommand, args: normalizedArgs };
66
+ normalizedCommand = normalizedArgs.shift();
67
+ }
68
+
69
+ if (isCorepackCommand(normalizedCommand)) {
70
+ if (normalizedArgs.length === 0) return { command: normalizedCommand, args: normalizedArgs };
71
+ normalizedCommand = normalizedArgs.shift();
72
+ }
73
+
74
+ return { command: normalizedCommand, args: normalizedArgs };
75
+ }
76
+
77
+ function hasOfficialTarget(args) {
78
+ return args.some(isOfficialTarget);
79
+ }
80
+
81
+ function shouldBlockNpmLike(command, args) {
82
+ if (!(isNpmCommand(command) || isPnpmCommand(command))) return false;
83
+ if (!args.find(isSupportedOperation)) return false;
84
+ return hasOfficialTarget(args);
85
+ }
86
+
87
+ function shouldBlockYarn(command, args) {
88
+ if (!isYarnCommand(command)) return false;
89
+ return String(args[0] || '') === 'global' && String(args[1] || '') === 'add' && hasOfficialTarget(args.slice(2));
90
+ }
91
+
92
+ function shouldBlockNpx(command, args) {
93
+ if (!isNpxCommand(command)) return false;
94
+ for (let index = 0; index < args.length; index += 1) {
95
+ const token = String(args[index] || '');
96
+ if (token === '-p' || token === '--package') {
97
+ if (isOfficialTarget(args[index + 1])) return true;
98
+ index += 1;
99
+ continue;
100
+ }
101
+ if (token.startsWith('--package=')) {
102
+ if (isOfficialTarget(token.slice('--package='.length))) return true;
103
+ continue;
104
+ }
105
+ if (!token.startsWith('-')) {
106
+ return isOfficialTarget(token);
107
+ }
108
+ }
109
+ return false;
110
+ }
111
+
112
+ function shouldBlockCommand(command, args) {
113
+ if (!Array.isArray(args)) return false;
114
+ const normalized = normalizeCommand(command, args);
115
+ return (
116
+ shouldBlockNpmLike(normalized.command, normalized.args) ||
117
+ shouldBlockYarn(normalized.command, normalized.args) ||
118
+ shouldBlockNpx(normalized.command, normalized.args)
119
+ );
120
+ }
121
+
122
+ function normalizeTokensForExec(tokens) {
123
+ if (tokens.length === 0) return tokens;
124
+ const normalized = normalizeCommand(tokens[0], tokens.slice(1));
125
+ return [normalized.command, ...normalized.args].filter(value => value !== undefined);
126
+ }
127
+
128
+ function unwrapShellCommand(command, args) {
129
+ if (!isShellCommand(command) || !Array.isArray(args) || args.length < 2) return null;
130
+ const first = String(args[0] || '');
131
+ const second = String(args[1] || '');
132
+ if ((first === '-c' || first === '-lc') && second) return second;
133
+ if (first === '-l' && String(args[1] || '') === '-c' && String(args[2] || '')) return String(args[2]);
134
+ return null;
135
+ }
136
+
137
+ function tokenizeShellString(command) {
138
+ const text = String(command || '').trim();
139
+ if (!text) return [];
140
+ return text.match(/(?:[^\s'"]+|'[^']*'|"[^"]*")+/g) || [];
141
+ }
142
+
143
+ function unquote(token) {
144
+ if (
145
+ (token.startsWith('"') && token.endsWith('"')) ||
146
+ (token.startsWith("'") && token.endsWith("'"))
147
+ ) {
148
+ return token.slice(1, -1);
149
+ }
150
+ return token;
151
+ }
152
+
153
+ function shouldBlockExecString(command) {
154
+ const tokens = normalizeTokensForExec(tokenizeShellString(command).map(unquote));
155
+ if (tokens.length === 0) return false;
156
+ const shellInner = unwrapShellCommand(tokens[0], tokens.slice(1));
157
+ if (shellInner) return shouldBlockExecString(shellInner);
158
+ return shouldBlockCommand(tokens[0], tokens.slice(1));
159
+ }
160
+
161
+ function createBlockedError(blockedStderr) {
162
+ const error = new Error(BLOCK_MESSAGE);
163
+ error.code = 'CLAUDE_TERMUX_OFFICIAL_UPDATE_BLOCKED';
164
+ error.status = 1;
165
+ error.stdout = Buffer.from('');
166
+ error.stderr = Buffer.from(blockedStderr);
167
+ return error;
168
+ }
169
+
170
+ function createBlockedSpawn(blockedStderr, stderrWriter, options) {
171
+ const child = new EventEmitter();
172
+ child.stdout = null;
173
+ child.stderr = null;
174
+ child.stdin = null;
175
+ child.pid = 0;
176
+ child.killed = false;
177
+ child.kill = () => false;
178
+ process.nextTick(() => {
179
+ if (!(options && options.stdio === 'ignore')) {
180
+ stderrWriter(blockedStderr);
181
+ }
182
+ child.emit('close', 1, null);
183
+ child.emit('exit', 1, null);
184
+ });
185
+ return child;
186
+ }
187
+
188
+ function createBlockedSpawnSync(blockedStderr, stderrWriter) {
189
+ stderrWriter(blockedStderr);
190
+ return {
191
+ pid: 0,
192
+ output: [null, Buffer.from(''), Buffer.from(blockedStderr)],
193
+ stdout: Buffer.from(''),
194
+ stderr: Buffer.from(blockedStderr),
195
+ status: 1,
196
+ signal: null,
197
+ error: createBlockedError(blockedStderr),
198
+ };
199
+ }
200
+
201
+ function createBlockedExecResult(blockedStderr, stderrWriter, callback) {
202
+ const child = createBlockedSpawn(blockedStderr, stderrWriter);
203
+ const error = createBlockedError(blockedStderr);
204
+ process.nextTick(() => {
205
+ if (typeof callback === 'function') {
206
+ callback(error, '', blockedStderr);
207
+ }
208
+ });
209
+ return child;
210
+ }
211
+
212
+ function shouldBlockSpawnArgs(args) {
213
+ if (!Array.isArray(args) || args.length === 0) return false;
214
+ const command = args[0];
215
+ const commandArgs = Array.isArray(args[1]) ? args[1] : [];
216
+ const shellInner = unwrapShellCommand(command, commandArgs);
217
+ if (shellInner) return shouldBlockExecString(shellInner);
218
+ return shouldBlockCommand(command, commandArgs);
219
+ }
220
+
221
+ function createGuardedChildProcess(realChild, stderrWriter = value => process.stderr.write(value)) {
222
+ const blockedStderr = `${BLOCK_MESSAGE}\n`;
223
+ return {
224
+ spawn: (...args) => {
225
+ if (shouldBlockSpawnArgs(args)) return createBlockedSpawn(blockedStderr, stderrWriter, args[2]);
226
+ return realChild.spawn(...args);
227
+ },
228
+ execFile: (...args) => {
229
+ if (shouldBlockSpawnArgs(args)) return createBlockedExecResult(blockedStderr, stderrWriter, args[args.length - 1]);
230
+ return realChild.execFile(...args);
231
+ },
232
+ exec: (...args) => {
233
+ if (shouldBlockExecString(args[0])) return createBlockedExecResult(blockedStderr, stderrWriter, args[args.length - 1]);
234
+ return realChild.exec(...args);
235
+ },
236
+ spawnSync: (...args) => {
237
+ if (shouldBlockSpawnArgs(args)) return createBlockedSpawnSync(blockedStderr, stderrWriter);
238
+ return realChild.spawnSync(...args);
239
+ },
240
+ execFileSync: (...args) => {
241
+ if (shouldBlockSpawnArgs(args)) throw createBlockedError(blockedStderr);
242
+ return realChild.execFileSync(...args);
243
+ },
244
+ execSync: (...args) => {
245
+ if (shouldBlockExecString(args[0])) throw createBlockedError(blockedStderr);
246
+ return realChild.execSync(...args);
247
+ },
248
+ };
249
+ }
250
+
251
+ module.exports = {
252
+ BLOCK_MESSAGE,
253
+ OFFICIAL_PACKAGE,
254
+ createGuardedChildProcess,
255
+ shouldBlockCommand,
256
+ shouldBlockExecString,
257
+ };
@@ -0,0 +1,310 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const test = require('node:test');
5
+ const assert = require('node:assert/strict');
6
+ const EventEmitter = require('events');
7
+
8
+ const {
9
+ BLOCK_MESSAGE,
10
+ createGuardedChildProcess,
11
+ shouldBlockCommand,
12
+ shouldBlockExecString,
13
+ } = require('./native-update-guard.js');
14
+
15
+ test('blocks npm install of official package', () => {
16
+ assert.equal(
17
+ shouldBlockCommand('npm', ['install', '-g', '@anthropic-ai/claude-code@latest']),
18
+ true,
19
+ );
20
+ });
21
+
22
+ test('blocks npm update of official package via absolute npm path', () => {
23
+ assert.equal(
24
+ shouldBlockCommand('/usr/bin/npm', ['update', '-g', '@anthropic-ai/claude-code']),
25
+ true,
26
+ );
27
+ });
28
+
29
+ test('does not block canonical package install', () => {
30
+ assert.equal(
31
+ shouldBlockCommand('npm', ['install', '-g', '@bash0816/claude-code@2.1.137']),
32
+ false,
33
+ );
34
+ });
35
+
36
+ test('blocks exec string that installs official package', () => {
37
+ assert.equal(
38
+ shouldBlockExecString('npm install -g @anthropic-ai/claude-code@latest'),
39
+ true,
40
+ );
41
+ });
42
+
43
+ test('does not block unrelated exec string', () => {
44
+ assert.equal(
45
+ shouldBlockExecString('npm install -g @bash0816/claude-code@2.1.137'),
46
+ false,
47
+ );
48
+ });
49
+
50
+ test('blocks env npm install of official package', () => {
51
+ assert.equal(
52
+ shouldBlockCommand('env', ['FOO=bar', 'npm', 'install', '-g', '@anthropic-ai/claude-code@latest']),
53
+ true,
54
+ );
55
+ });
56
+
57
+ test('blocks npx official package invocation', () => {
58
+ assert.equal(shouldBlockCommand('npx', ['@anthropic-ai/claude-code']), true);
59
+ });
60
+
61
+ test('blocks npx package option official package invocation', () => {
62
+ assert.equal(shouldBlockCommand('npx', ['-p', '@anthropic-ai/claude-code', 'claude']), true);
63
+ assert.equal(shouldBlockCommand('npx', ['--package=@anthropic-ai/claude-code', 'claude']), true);
64
+ });
65
+
66
+ test('blocks pnpm add of official package', () => {
67
+ assert.equal(
68
+ shouldBlockCommand('pnpm', ['add', '-g', '@anthropic-ai/claude-code']),
69
+ true,
70
+ );
71
+ });
72
+
73
+ test('blocks yarn global add of official package', () => {
74
+ assert.equal(
75
+ shouldBlockCommand('yarn', ['global', 'add', '@anthropic-ai/claude-code']),
76
+ true,
77
+ );
78
+ });
79
+
80
+ test('blocks corepack pnpm add of official package', () => {
81
+ assert.equal(
82
+ shouldBlockCommand('corepack', ['pnpm', 'add', '-g', '@anthropic-ai/claude-code']),
83
+ true,
84
+ );
85
+ });
86
+
87
+ test('blocks corepack yarn global add of official package', () => {
88
+ assert.equal(
89
+ shouldBlockCommand('corepack', ['yarn', 'global', 'add', '@anthropic-ai/claude-code']),
90
+ true,
91
+ );
92
+ });
93
+
94
+ test('does not block corepack pnpm add of canonical package', () => {
95
+ assert.equal(
96
+ shouldBlockCommand('corepack', ['pnpm', 'add', '-g', '@bash0816/claude-code@2.1.137']),
97
+ false,
98
+ );
99
+ });
100
+
101
+ test('blocks exec string through env npm', () => {
102
+ assert.equal(
103
+ shouldBlockExecString('env FOO=bar npm install -g @anthropic-ai/claude-code@latest'),
104
+ true,
105
+ );
106
+ });
107
+
108
+ test('blocks exec string through corepack yarn', () => {
109
+ assert.equal(
110
+ shouldBlockExecString('corepack yarn global add @anthropic-ai/claude-code'),
111
+ true,
112
+ );
113
+ });
114
+
115
+ test('blocks exec string through sh -c npm install', () => {
116
+ assert.equal(
117
+ shouldBlockExecString('sh -c "npm install -g @anthropic-ai/claude-code"'),
118
+ true,
119
+ );
120
+ });
121
+
122
+ test('blocks exec string through bash -lc pnpm add', () => {
123
+ assert.equal(
124
+ shouldBlockExecString('bash -lc "pnpm add -g @anthropic-ai/claude-code"'),
125
+ true,
126
+ );
127
+ });
128
+
129
+ test('does not block canonical exec string through bash -lc', () => {
130
+ assert.equal(
131
+ shouldBlockExecString('bash -lc "npm install -g @bash0816/claude-code@2.1.137"'),
132
+ false,
133
+ );
134
+ });
135
+
136
+ test('does not block unrelated shell exec string', () => {
137
+ assert.equal(shouldBlockExecString('sh -c "echo ok"'), false);
138
+ });
139
+
140
+ function createRealChildStub() {
141
+ const calls = [];
142
+ return {
143
+ calls,
144
+ spawn(...args) {
145
+ calls.push(['spawn', args]);
146
+ return { kind: 'spawn', args };
147
+ },
148
+ execFile(...args) {
149
+ calls.push(['execFile', args]);
150
+ return { kind: 'execFile', args };
151
+ },
152
+ exec(...args) {
153
+ calls.push(['exec', args]);
154
+ return { kind: 'exec', args };
155
+ },
156
+ spawnSync(...args) {
157
+ calls.push(['spawnSync', args]);
158
+ return { kind: 'spawnSync', args, status: 0 };
159
+ },
160
+ execFileSync(...args) {
161
+ calls.push(['execFileSync', args]);
162
+ return Buffer.from('ok');
163
+ },
164
+ execSync(...args) {
165
+ calls.push(['execSync', args]);
166
+ return Buffer.from('ok');
167
+ },
168
+ };
169
+ }
170
+
171
+ test('guarded spawn blocks official npm update without delegating', async () => {
172
+ const writes = [];
173
+ const realChild = createRealChildStub();
174
+ const guarded = createGuardedChildProcess(realChild, value => writes.push(value));
175
+
176
+ const child = guarded.spawn('npm', ['install', '-g', '@anthropic-ai/claude-code@latest']);
177
+ assert.equal(child instanceof EventEmitter, true);
178
+
179
+ const result = await new Promise(resolve => {
180
+ child.once('close', (code, signal) => resolve({ code, signal }));
181
+ });
182
+
183
+ assert.deepEqual(result, { code: 1, signal: null });
184
+ assert.deepEqual(realChild.calls, []);
185
+ assert.equal(writes[0], `${BLOCK_MESSAGE}\n`);
186
+ });
187
+
188
+ test('guarded spawn delegates canonical install', () => {
189
+ const realChild = createRealChildStub();
190
+ const guarded = createGuardedChildProcess(realChild, () => {});
191
+ const result = guarded.spawn('npm', ['install', '-g', '@bash0816/claude-code@2.1.137']);
192
+
193
+ assert.equal(result.kind, 'spawn');
194
+ assert.equal(realChild.calls.length, 1);
195
+ });
196
+
197
+ test('guarded execFile blocks official npm update callback path', async () => {
198
+ const writes = [];
199
+ const realChild = createRealChildStub();
200
+ const guarded = createGuardedChildProcess(realChild, value => writes.push(value));
201
+
202
+ const callbackResult = await new Promise(resolve => {
203
+ guarded.execFile(
204
+ 'npm',
205
+ ['update', '-g', '@anthropic-ai/claude-code'],
206
+ (error, stdout, stderr) => resolve({ error, stdout, stderr }),
207
+ );
208
+ });
209
+
210
+ assert.equal(callbackResult.error.code, 'CLAUDE_TERMUX_OFFICIAL_UPDATE_BLOCKED');
211
+ assert.equal(callbackResult.stdout, '');
212
+ assert.equal(callbackResult.stderr, `${BLOCK_MESSAGE}\n`);
213
+ assert.deepEqual(realChild.calls, []);
214
+ assert.equal(writes[0], `${BLOCK_MESSAGE}\n`);
215
+ });
216
+
217
+ test('guarded exec blocks official npm update shell string', async () => {
218
+ const realChild = createRealChildStub();
219
+ const guarded = createGuardedChildProcess(realChild, () => {});
220
+
221
+ const callbackResult = await new Promise(resolve => {
222
+ guarded.exec(
223
+ 'npm install -g @anthropic-ai/claude-code@latest',
224
+ (error, stdout, stderr) => resolve({ error, stdout, stderr }),
225
+ );
226
+ });
227
+
228
+ assert.equal(callbackResult.error.code, 'CLAUDE_TERMUX_OFFICIAL_UPDATE_BLOCKED');
229
+ assert.equal(callbackResult.stdout, '');
230
+ assert.equal(callbackResult.stderr, `${BLOCK_MESSAGE}\n`);
231
+ assert.deepEqual(realChild.calls, []);
232
+ });
233
+
234
+ test('guarded spawnSync blocks official npm update with status 1', () => {
235
+ const writes = [];
236
+ const realChild = createRealChildStub();
237
+ const guarded = createGuardedChildProcess(realChild, value => writes.push(value));
238
+ const result = guarded.spawnSync('npm', ['update', '-g', '@anthropic-ai/claude-code']);
239
+
240
+ assert.equal(result.status, 1);
241
+ assert.equal(Buffer.isBuffer(result.stderr), true);
242
+ assert.equal(String(result.stderr), `${BLOCK_MESSAGE}\n`);
243
+ assert.equal(result.error.code, 'CLAUDE_TERMUX_OFFICIAL_UPDATE_BLOCKED');
244
+ assert.deepEqual(realChild.calls, []);
245
+ assert.equal(writes[0], `${BLOCK_MESSAGE}\n`);
246
+ });
247
+
248
+ test('guarded execFileSync throws on official npm update', () => {
249
+ const realChild = createRealChildStub();
250
+ const guarded = createGuardedChildProcess(realChild, () => {});
251
+
252
+ assert.throws(
253
+ () => guarded.execFileSync('npm', ['install', '-g', '@anthropic-ai/claude-code@latest']),
254
+ error => error && error.code === 'CLAUDE_TERMUX_OFFICIAL_UPDATE_BLOCKED',
255
+ );
256
+ assert.deepEqual(realChild.calls, []);
257
+ });
258
+
259
+ test('guarded execSync throws on official npm update shell string', () => {
260
+ const realChild = createRealChildStub();
261
+ const guarded = createGuardedChildProcess(realChild, () => {});
262
+
263
+ assert.throws(
264
+ () => guarded.execSync('npm update -g @anthropic-ai/claude-code'),
265
+ error => error && error.code === 'CLAUDE_TERMUX_OFFICIAL_UPDATE_BLOCKED',
266
+ );
267
+ assert.deepEqual(realChild.calls, []);
268
+ });
269
+
270
+ test('guarded spawn blocks official npm update through sh -c', async () => {
271
+ const writes = [];
272
+ const realChild = createRealChildStub();
273
+ const guarded = createGuardedChildProcess(realChild, value => writes.push(value));
274
+
275
+ const child = guarded.spawn('/bin/sh', ['-c', 'npm install -g @anthropic-ai/claude-code']);
276
+ const result = await new Promise(resolve => {
277
+ child.once('close', (code, signal) => resolve({ code, signal }));
278
+ });
279
+
280
+ assert.deepEqual(result, { code: 1, signal: null });
281
+ assert.deepEqual(realChild.calls, []);
282
+ assert.equal(writes[0], `${BLOCK_MESSAGE}\n`);
283
+ });
284
+
285
+ test('guarded execFile blocks official update through bash -lc', async () => {
286
+ const realChild = createRealChildStub();
287
+ const guarded = createGuardedChildProcess(realChild, () => {});
288
+
289
+ const callbackResult = await new Promise(resolve => {
290
+ guarded.execFile(
291
+ '/usr/bin/bash',
292
+ ['-lc', 'pnpm add -g @anthropic-ai/claude-code'],
293
+ (error, stdout, stderr) => resolve({ error, stdout, stderr }),
294
+ );
295
+ });
296
+
297
+ assert.equal(callbackResult.error.code, 'CLAUDE_TERMUX_OFFICIAL_UPDATE_BLOCKED');
298
+ assert.equal(callbackResult.stdout, '');
299
+ assert.equal(callbackResult.stderr, `${BLOCK_MESSAGE}\n`);
300
+ assert.deepEqual(realChild.calls, []);
301
+ });
302
+
303
+ test('guarded spawn delegates canonical shell install', () => {
304
+ const realChild = createRealChildStub();
305
+ const guarded = createGuardedChildProcess(realChild, () => {});
306
+ const result = guarded.spawn('/bin/sh', ['-c', 'npm install -g @bash0816/claude-code@2.1.137']);
307
+
308
+ assert.equal(result.kind, 'spawn');
309
+ assert.equal(realChild.calls.length, 1);
310
+ });
@@ -10,9 +10,8 @@ if (process.env.CLAUDE_TERMUX_SKIP_POSTINSTALL === '1') {
10
10
 
11
11
  const packageDir = path.resolve(__dirname, '..');
12
12
  const pkg = require(path.join(packageDir, 'package.json'));
13
- const manifest = require(path.join(packageDir, 'config', 'claude-termux-release-manifest.json'));
14
13
  const script = path.join(__dirname, 'prepare-native.js');
15
- const version = process.env.CLAUDE_TERMUX_CLAUDE_VERSION || manifest.default_native_version || manifest.latest_audited_version || pkg.version;
14
+ const version = process.env.CLAUDE_TERMUX_CLAUDE_VERSION || pkg.version;
16
15
 
17
16
  const result = cp.spawnSync(process.execPath, [script, version], {
18
17
  stdio: 'inherit',
package/lib/preinstall.js CHANGED
@@ -1,12 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
 
4
- const [_nodeMajor] = process.versions.node.split('.').map(Number);
5
- if (_nodeMajor < 20) {
6
- process.stderr.write('@bash0816/claude-code requires Node.js v20 or later. Found: v' + process.versions.node + '\n');
7
- process.exit(1);
8
- }
9
-
10
4
  const cp = require('child_process');
11
5
  const fs = require('fs');
12
6
  const os = require('os');