@xortex/xcode 3.0.4 → 3.0.8
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/bin/xcode +118 -117
- package/bun-bundle-hook.js +38 -17
- package/bun-bundle-shim.ts +12 -0
- package/entrypoints/cli.tsx +2 -22
- package/macro.ts +1 -1
- package/package.json +5 -3
- package/tsconfig.json +16 -0
- package/utils/bunBundleCompat.ts +0 -44
package/bin/xcode
CHANGED
|
@@ -1,164 +1,165 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* XCode CLI Entry Point
|
|
4
|
-
*
|
|
5
|
-
* This is
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
*
|
|
5
|
+
* This codebase is built for Bun (bun.sh). If Bun is not installed,
|
|
6
|
+
* this script installs it automatically, then hands off to Bun.
|
|
7
|
+
*
|
|
8
|
+
* Installed via: npm install -g @xortex/xcode
|
|
8
9
|
*/
|
|
9
10
|
|
|
11
|
+
const { execSync, spawnSync } = require('child_process');
|
|
10
12
|
const { spawn } = require('child_process');
|
|
11
13
|
const path = require('path');
|
|
12
14
|
const fs = require('fs');
|
|
15
|
+
const os = require('os');
|
|
13
16
|
|
|
14
|
-
// Get the project root (where this script is located)
|
|
15
17
|
const projectRoot = path.resolve(__dirname, '..');
|
|
16
18
|
const mainScript = path.join(projectRoot, 'entrypoints', 'cli.tsx');
|
|
17
|
-
const bunBundleHook = path.join(projectRoot, 'bun-bundle-hook.js');
|
|
18
19
|
|
|
19
|
-
//
|
|
20
|
-
|
|
20
|
+
// ── helpers ──────────────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
function tryExec(cmd) {
|
|
23
|
+
try { execSync(cmd, { stdio: 'ignore' }); return true; } catch { return false; }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function bunBinPath() {
|
|
27
|
+
// Common Bun install locations
|
|
28
|
+
const candidates = [
|
|
29
|
+
path.join(os.homedir(), '.bun', 'bin', 'bun'),
|
|
30
|
+
'/usr/local/bin/bun',
|
|
31
|
+
'/usr/bin/bun',
|
|
32
|
+
];
|
|
33
|
+
for (const c of candidates) {
|
|
34
|
+
if (fs.existsSync(c)) return c;
|
|
35
|
+
}
|
|
36
|
+
// Try PATH
|
|
21
37
|
try {
|
|
22
|
-
|
|
23
|
-
return 'bun';
|
|
38
|
+
return execSync('which bun', { encoding: 'utf8' }).trim();
|
|
24
39
|
} catch {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function installBun() {
|
|
45
|
+
console.log('');
|
|
46
|
+
console.log('📦 XCode requires Bun runtime. Installing Bun now...');
|
|
47
|
+
console.log(' (This is a one-time setup)');
|
|
48
|
+
console.log('');
|
|
49
|
+
|
|
50
|
+
const platform = os.platform();
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
if (platform === 'win32') {
|
|
54
|
+
execSync('powershell -c "irm bun.sh/install.ps1 | iex"', { stdio: 'inherit' });
|
|
55
|
+
} else {
|
|
56
|
+
// curl or wget
|
|
57
|
+
if (tryExec('which curl')) {
|
|
58
|
+
execSync('curl -fsSL https://bun.sh/install | bash', {
|
|
59
|
+
stdio: 'inherit',
|
|
60
|
+
shell: '/bin/bash',
|
|
61
|
+
});
|
|
62
|
+
} else if (tryExec('which wget')) {
|
|
63
|
+
execSync('wget -qO- https://bun.sh/install | bash', {
|
|
64
|
+
stdio: 'inherit',
|
|
65
|
+
shell: '/bin/bash',
|
|
66
|
+
});
|
|
67
|
+
} else {
|
|
68
|
+
throw new Error('Neither curl nor wget is available');
|
|
69
|
+
}
|
|
36
70
|
}
|
|
71
|
+
|
|
72
|
+
console.log('');
|
|
73
|
+
console.log('✅ Bun installed successfully!');
|
|
74
|
+
console.log('');
|
|
75
|
+
|
|
76
|
+
// Re-source PATH
|
|
77
|
+
const bunBin = bunBinPath();
|
|
78
|
+
if (bunBin) return bunBin;
|
|
79
|
+
|
|
80
|
+
// Add ~/.bun/bin to PATH for this process
|
|
81
|
+
const bunBinDir = path.join(os.homedir(), '.bun', 'bin');
|
|
82
|
+
process.env.PATH = `${bunBinDir}${path.delimiter}${process.env.PATH}`;
|
|
83
|
+
return path.join(bunBinDir, 'bun');
|
|
84
|
+
|
|
85
|
+
} catch (err) {
|
|
86
|
+
console.error('');
|
|
87
|
+
console.error('❌ Could not auto-install Bun. Please install it manually:');
|
|
88
|
+
console.error('');
|
|
89
|
+
console.error(' curl -fsSL https://bun.sh/install | bash');
|
|
90
|
+
console.error('');
|
|
91
|
+
console.error(' Then run: xcode');
|
|
92
|
+
console.error('');
|
|
93
|
+
process.exit(1);
|
|
37
94
|
}
|
|
38
95
|
}
|
|
39
96
|
|
|
40
|
-
//
|
|
97
|
+
// ── CLI flags ─────────────────────────────────────────────────────────────────
|
|
98
|
+
|
|
41
99
|
process.env.XCODE_CLI = 'true';
|
|
42
100
|
process.env.NODE_ENV = process.env.NODE_ENV || 'production';
|
|
43
101
|
|
|
44
|
-
// Handle special commands
|
|
45
102
|
const args = process.argv.slice(2);
|
|
46
|
-
const command = args[0];
|
|
47
103
|
|
|
48
|
-
if (
|
|
49
|
-
console.log('xcode v3.0.
|
|
104
|
+
if (args[0] === '--version' || args[0] === '-v' || args[0] === '-V') {
|
|
105
|
+
console.log('xcode v3.0.8 — AI coding assistant with XMem memory');
|
|
50
106
|
process.exit(0);
|
|
51
107
|
}
|
|
52
108
|
|
|
53
|
-
if (
|
|
109
|
+
if (args[0] === '--help' || args[0] === '-h') {
|
|
54
110
|
console.log(`
|
|
55
111
|
XCode - AI-powered coding assistant with XMem memory
|
|
56
112
|
|
|
57
|
-
Usage: xcode [options]
|
|
113
|
+
Usage: xcode [options]
|
|
58
114
|
|
|
59
115
|
Options:
|
|
60
|
-
-v, --version
|
|
61
|
-
-h, --help
|
|
62
|
-
--model <
|
|
116
|
+
-v, --version Show version
|
|
117
|
+
-h, --help Show this help
|
|
118
|
+
--model <name> Choose model: gemini-2.5-pro, kimi-k2.5, deepseek-v3.2
|
|
119
|
+
--openrouter Force OpenRouter as provider
|
|
63
120
|
|
|
64
|
-
|
|
65
|
-
(none) Start interactive session
|
|
66
|
-
|
|
67
|
-
Environment Variables:
|
|
68
|
-
OPENROUTER_API_KEY OpenRouter API key (optional, has default)
|
|
69
|
-
XMEM_API_URL XMem memory server URL (default: http://localhost:8000)
|
|
121
|
+
Environment:
|
|
70
122
|
ANTHROPIC_API_KEY Anthropic API key (for Claude models)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
xcode # Start with default model
|
|
74
|
-
xcode --model gemini-2.5-pro # Use Gemini via OpenRouter
|
|
75
|
-
xcode --model kimi-k2.5 # Use Kimi via OpenRouter
|
|
123
|
+
OPENROUTER_API_KEY OpenRouter API key (optional, default provided)
|
|
124
|
+
XMEM_API_URL XMem server URL (default: http://localhost:8000)
|
|
76
125
|
`);
|
|
77
126
|
process.exit(0);
|
|
78
127
|
}
|
|
79
128
|
|
|
80
|
-
//
|
|
81
|
-
const
|
|
82
|
-
if (
|
|
83
|
-
const model = args[
|
|
129
|
+
// Model / provider flags
|
|
130
|
+
const modelIdx = args.indexOf('--model');
|
|
131
|
+
if (modelIdx !== -1 && args[modelIdx + 1]) {
|
|
132
|
+
const model = args[modelIdx + 1];
|
|
84
133
|
process.env.ANTHROPIC_MODEL = model;
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const openRouterModels = ['kimi', 'deepseek', 'gemini', 'moonshot', 'google/gemini'];
|
|
88
|
-
if (openRouterModels.some(m => model.toLowerCase().includes(m.toLowerCase()))) {
|
|
134
|
+
const orModels = ['kimi', 'deepseek', 'gemini', 'moonshot', 'google/'];
|
|
135
|
+
if (orModels.some(m => model.toLowerCase().includes(m.toLowerCase()))) {
|
|
89
136
|
process.env.CLAUDE_CODE_USE_OPENROUTER = 'true';
|
|
90
137
|
}
|
|
91
138
|
}
|
|
92
|
-
|
|
93
|
-
// Check for explicit OpenRouter flag
|
|
94
139
|
if (args.includes('--openrouter')) {
|
|
95
140
|
process.env.CLAUDE_CODE_USE_OPENROUTER = 'true';
|
|
96
141
|
}
|
|
97
142
|
|
|
98
|
-
//
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if (
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
try {
|
|
117
|
-
// Try local tsx first
|
|
118
|
-
tsxPath = require.resolve('tsx/dist/cli.mjs', { paths: [projectRoot] });
|
|
119
|
-
} catch {
|
|
120
|
-
try {
|
|
121
|
-
// Try global tsx
|
|
122
|
-
tsxPath = require.resolve('tsx/dist/cli.mjs');
|
|
123
|
-
} catch {
|
|
124
|
-
// Fallback to npx tsx
|
|
125
|
-
tsxPath = null;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
let child;
|
|
130
|
-
const hasHook = fs.existsSync(bunBundleHook);
|
|
131
|
-
|
|
132
|
-
if (tsxPath) {
|
|
133
|
-
// Use direct tsx path with bun:bundle shim
|
|
134
|
-
const nodeArgs = hasHook ? ['--require', bunBundleHook, tsxPath, mainScript] : [tsxPath, mainScript];
|
|
135
|
-
child = spawn('node', [...nodeArgs, ...args], {
|
|
136
|
-
cwd: projectRoot,
|
|
137
|
-
stdio: 'inherit',
|
|
138
|
-
env: process.env,
|
|
139
|
-
});
|
|
143
|
+
// ── find / install Bun, then run ──────────────────────────────────────────────
|
|
144
|
+
|
|
145
|
+
let bun = bunBinPath();
|
|
146
|
+
if (!bun) {
|
|
147
|
+
bun = installBun();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const child = spawn(bun, ['run', mainScript, ...args], {
|
|
151
|
+
cwd: projectRoot,
|
|
152
|
+
stdio: 'inherit',
|
|
153
|
+
env: process.env,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
child.on('exit', code => process.exit(code ?? 0));
|
|
157
|
+
child.on('error', err => {
|
|
158
|
+
if (err.code === 'ENOENT') {
|
|
159
|
+
console.error('\n❌ Bun not found at: ' + bun);
|
|
160
|
+
console.error('Please run: curl -fsSL https://bun.sh/install | bash && xcode');
|
|
140
161
|
} else {
|
|
141
|
-
|
|
142
|
-
const npxArgs = hasHook ? ['--node-options', `--require ${bunBundleHook}`, 'tsx'] : ['tsx'];
|
|
143
|
-
child = spawn('npx', [...npxArgs, mainScript, ...args], {
|
|
144
|
-
cwd: projectRoot,
|
|
145
|
-
stdio: 'inherit',
|
|
146
|
-
env: process.env,
|
|
147
|
-
});
|
|
162
|
+
console.error('\n❌ Failed to start XCode:', err.message);
|
|
148
163
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
process.exit(code || 0);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
child.on('error', (err) => {
|
|
155
|
-
console.error('❌ Failed to start XCode:', err.message);
|
|
156
|
-
console.error('');
|
|
157
|
-
console.error('Please ensure tsx is installed:');
|
|
158
|
-
console.error(' npm install -g tsx');
|
|
159
|
-
console.error('');
|
|
160
|
-
console.error('Or use Bun instead:');
|
|
161
|
-
console.error(' curl -fsSL https://bun.sh/install | bash');
|
|
162
|
-
process.exit(1);
|
|
163
|
-
});
|
|
164
|
-
}
|
|
164
|
+
process.exit(1);
|
|
165
|
+
});
|
package/bun-bundle-hook.js
CHANGED
|
@@ -1,25 +1,46 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Node.js require hook to shim bun:bundle
|
|
3
|
-
*
|
|
2
|
+
* Node.js require hook to shim 'bun:bundle' for tsx/Node.js compatibility.
|
|
3
|
+
*
|
|
4
|
+
* Strategy:
|
|
5
|
+
* 1. Intercept Module._resolveFilename so 'bun:bundle' resolves to a
|
|
6
|
+
* sentinel path (not a real file) instead of throwing MODULE_NOT_FOUND.
|
|
7
|
+
* 2. Pre-populate Module._cache with the shim exports under that sentinel
|
|
8
|
+
* path, so Module._load finds it in cache and never tries to read a file.
|
|
9
|
+
*
|
|
10
|
+
* This works even when tsx wraps _resolveFilename after this hook is installed,
|
|
11
|
+
* because tsx calls the original _resolveFilename as a fallback.
|
|
12
|
+
*
|
|
13
|
+
* Loaded via --require before tsx.
|
|
4
14
|
*/
|
|
15
|
+
if (!global.__bunBundleHookInstalled) {
|
|
16
|
+
global.__bunBundleHookInstalled = true;
|
|
5
17
|
|
|
6
|
-
const Module = require('module');
|
|
7
|
-
const path = require('path');
|
|
18
|
+
const Module = require('module');
|
|
8
19
|
|
|
9
|
-
|
|
20
|
+
// Use a sentinel that looks like an absolute path so tsx / Node accept it.
|
|
21
|
+
// It never needs to exist on disk because we pre-populate the cache.
|
|
22
|
+
const SENTINEL = '\0bun:bundle';
|
|
10
23
|
|
|
11
|
-
//
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
// Redirect to our compatibility module
|
|
18
|
-
const compatPath = path.join(projectRoot, 'utils', 'bunBundleCompat.js');
|
|
19
|
-
return compatPath;
|
|
24
|
+
// feature() shim: in Node.js all DCE gates are off by default.
|
|
25
|
+
// Override via CLAUDE_CODE_FEATURE_<NAME>=1 if needed.
|
|
26
|
+
function feature(name) {
|
|
27
|
+
const key = 'CLAUDE_CODE_FEATURE_' + String(name).replace(/[^A-Za-z0-9]/g, '_').toUpperCase();
|
|
28
|
+
const v = process.env[key];
|
|
29
|
+
return v === '1' || v === 'true';
|
|
20
30
|
}
|
|
21
31
|
|
|
22
|
-
|
|
23
|
-
|
|
32
|
+
// Populate the module cache before tsx can intercept _load
|
|
33
|
+
const shimModule = new Module(SENTINEL, null);
|
|
34
|
+
shimModule.id = SENTINEL;
|
|
35
|
+
shimModule.filename = SENTINEL;
|
|
36
|
+
shimModule.loaded = true;
|
|
37
|
+
shimModule.exports = { feature };
|
|
38
|
+
Module._cache[SENTINEL] = shimModule;
|
|
24
39
|
|
|
25
|
-
|
|
40
|
+
// Intercept _resolveFilename so 'bun:bundle' maps to our sentinel
|
|
41
|
+
const originalResolve = Module._resolveFilename;
|
|
42
|
+
Module._resolveFilename = function (request, parent, isMain, options) {
|
|
43
|
+
if (request === 'bun:bundle') return SENTINEL;
|
|
44
|
+
return originalResolve.call(this, request, parent, isMain, options);
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shim for Bun's `bun:bundle` module, used in Node.js via tsx path aliases.
|
|
3
|
+
* Provides a no-op `feature()` function since all Bun DCE gates default to false
|
|
4
|
+
* in Node.js (the guarded code blocks have their own Node.js fallbacks).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export function feature(name: string): boolean {
|
|
8
|
+
// Allow opt-in via env var if ever needed
|
|
9
|
+
const key = `CLAUDE_CODE_FEATURE_${String(name).replace(/[^A-Za-z0-9]/g, '_').toUpperCase()}`;
|
|
10
|
+
const v = process.env[key];
|
|
11
|
+
return v === '1' || v === 'true';
|
|
12
|
+
}
|
package/entrypoints/cli.tsx
CHANGED
|
@@ -1,26 +1,6 @@
|
|
|
1
1
|
import '../macro'; // Import to set MACRO global before any other code
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
// eslint-disable-next-line custom-rules/no-top-level-side-effects
|
|
5
|
-
declare const Bun: { version: string } | undefined;
|
|
6
|
-
// eslint-disable-next-line custom-rules/no-top-level-side-effects
|
|
7
|
-
const isBun = typeof Bun !== 'undefined' && typeof Bun.version === 'string';
|
|
8
|
-
|
|
9
|
-
// Stub feature function for Node.js compatibility
|
|
10
|
-
// eslint-disable-next-line custom-rules/no-top-level-side-effects
|
|
11
|
-
function feature(name: string): boolean {
|
|
12
|
-
if (isBun) {
|
|
13
|
-
// Dynamic import for Bun - will only execute in Bun
|
|
14
|
-
try {
|
|
15
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
16
|
-
const bunBundle = require('bun:bundle');
|
|
17
|
-
return bunBundle.feature(name);
|
|
18
|
-
} catch {
|
|
19
|
-
return false;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
3
|
+
import { feature } from 'bun:bundle'
|
|
24
4
|
|
|
25
5
|
// Bugfix for corepack auto-pinning, which adds yarnpkg to peoples' package.jsons
|
|
26
6
|
// eslint-disable-next-line custom-rules/no-top-level-side-effects
|
|
@@ -59,7 +39,7 @@ async function main(): Promise<void> {
|
|
|
59
39
|
if (args.length === 1 && (args[0] === '--version' || args[0] === '-v' || args[0] === '-V')) {
|
|
60
40
|
// MACRO.VERSION is inlined at build time
|
|
61
41
|
// biome-ignore lint/suspicious/noConsole:: intentional console output
|
|
62
|
-
console.log(`xcode v3.0.
|
|
42
|
+
console.log(`xcode v3.0.8 — AI coding assistant with XMem memory`);
|
|
63
43
|
return;
|
|
64
44
|
}
|
|
65
45
|
|
package/macro.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xortex/xcode",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.8",
|
|
4
4
|
"description": "XCode - AI-powered coding assistant with XMem long-term memory. Supports Claude, Gemini, Kimi, DeepSeek via OpenRouter.",
|
|
5
5
|
"main": "main.tsx",
|
|
6
6
|
"bin": {
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
"main.tsx",
|
|
17
17
|
"macro.ts",
|
|
18
18
|
"bun-bundle-hook.js",
|
|
19
|
-
"
|
|
19
|
+
"bun-bundle-shim.ts",
|
|
20
|
+
"tsconfig.json",
|
|
20
21
|
"src/",
|
|
21
22
|
"constants/",
|
|
22
23
|
"utils/",
|
|
@@ -129,6 +130,7 @@
|
|
|
129
130
|
"terminal-link": "^5.0.0",
|
|
130
131
|
"terminal-size": "^4.0.1",
|
|
131
132
|
"tree-kill": "^1.2.2",
|
|
133
|
+
"tsx": "^4.19.4",
|
|
132
134
|
"usehooks-ts": "^3.1.1",
|
|
133
135
|
"uuid": "^13.0.0",
|
|
134
136
|
"widest-line": "^6.0.0",
|
|
@@ -143,6 +145,6 @@
|
|
|
143
145
|
"@types/lodash-es": "^4.17.12",
|
|
144
146
|
"@types/node": "^25.5.0",
|
|
145
147
|
"@types/react": "^19.2.14",
|
|
146
|
-
"
|
|
148
|
+
"esbuild": "^0.28.0"
|
|
147
149
|
}
|
|
148
150
|
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "esnext",
|
|
4
|
+
"module": "esnext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"esModuleInterop": true,
|
|
7
|
+
"strict": false,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"forceConsistentCasingInFileNames": true,
|
|
10
|
+
"baseUrl": ".",
|
|
11
|
+
"paths": {
|
|
12
|
+
"src/*": ["./*"],
|
|
13
|
+
"bun:bundle": ["./bun-bundle-shim"]
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
package/utils/bunBundleCompat.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Compatibility layer for bun:bundle module
|
|
3
|
-
* Provides feature() function for both Bun and Node.js runtimes
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
// Detect if running in Bun
|
|
7
|
-
// eslint-disable-next-line custom-rules/no-top-level-side-effects
|
|
8
|
-
declare const Bun: { version: string } | undefined;
|
|
9
|
-
// eslint-disable-next-line custom-rules/no-top-level-side-effects
|
|
10
|
-
const isBunRuntime = typeof Bun !== 'undefined' && typeof Bun.version === 'string';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Check if a feature flag is enabled.
|
|
14
|
-
* In Bun, this uses the native bun:bundle module.
|
|
15
|
-
* In Node.js, this checks environment variables.
|
|
16
|
-
*/
|
|
17
|
-
export function feature(name: string): boolean {
|
|
18
|
-
if (isBunRuntime) {
|
|
19
|
-
try {
|
|
20
|
-
// Dynamic require for Bun
|
|
21
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
22
|
-
const bunBundle = require('bun:bundle');
|
|
23
|
-
return bunBundle.feature(name);
|
|
24
|
-
} catch {
|
|
25
|
-
return false;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// In Node.js, check for equivalent environment variable
|
|
30
|
-
// Features are typically controlled via CLAUDE_CODE_* env vars
|
|
31
|
-
const envVarName = name.replace(/([A-Z])/g, '_$1').toUpperCase();
|
|
32
|
-
const envValue = process.env[`CLAUDE_CODE_${envVarName}`] || process.env[envVarName];
|
|
33
|
-
|
|
34
|
-
if (envValue === '1' || envValue === 'true') {
|
|
35
|
-
return true;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Default feature flags for Node.js
|
|
39
|
-
const defaultEnabled: Record<string, boolean> = {
|
|
40
|
-
// Add any features that should be enabled by default
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
return defaultEnabled[name] ?? false;
|
|
44
|
-
}
|