Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ bun run health
# Check unused exports
bun run check:unused

bun run typecheck

# Remote Control Server
bun run rcs

Expand Down
62 changes: 21 additions & 41 deletions build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,27 @@ for (const file of files) {
}
}

// Also patch unguarded globalThis.Bun destructuring from third-party deps
// (e.g. @anthropic-ai/sandbox-runtime) so Node.js doesn't crash at import time.
let bunPatched = 0
const BUN_DESTRUCTURE = /var \{([^}]+)\} = globalThis\.Bun;?/g
const BUN_DESTRUCTURE_SAFE = 'var {$1} = typeof globalThis.Bun !== "undefined" ? globalThis.Bun : {};'
for (const file of files) {
if (!file.endsWith('.js')) continue
const filePath = join(outdir, file)
const content = await readFile(filePath, 'utf-8')
if (BUN_DESTRUCTURE.test(content)) {
await writeFile(
filePath,
content.replace(BUN_DESTRUCTURE, BUN_DESTRUCTURE_SAFE),
)
bunPatched++
}
}
BUN_DESTRUCTURE.lastIndex = 0

console.log(
`Bundled ${result.outputs.length} files to ${outdir}/ (patched ${patched} for Node.js compat)`,
`Bundled ${result.outputs.length} files to ${outdir}/ (patched ${patched} for import.meta.require, ${bunPatched} for Bun destructure)`,
)

// Step 4: Copy native .node addon files (audio-capture)
Expand Down Expand Up @@ -119,46 +138,7 @@ const cliNode = join(outdir, 'cli-node.js')

await writeFile(cliBun, '#!/usr/bin/env bun\nimport "./cli.js"\n')

// Node.js entry needs a Bun API polyfill because Bun.build({ target: 'bun' })
// emits globalThis.Bun references (e.g. Bun.$ shell tag in computer-use-input,
// Bun.which in chunk-ys6smqg9) that crash at import time under plain Node.js.
const NODE_BUN_POLYFILL = `#!/usr/bin/env node
// Bun API polyfill for Node.js runtime
if (typeof globalThis.Bun === "undefined") {
const { execFileSync } = await import("child_process");
const { resolve, delimiter } = await import("path");
const { accessSync, constants: { X_OK } } = await import("fs");
function which(bin) {
const isWin = process.platform === "win32";
const pathExt = isWin ? (process.env.PATHEXT || ".EXE").split(";") : [""];
for (const dir of (process.env.PATH || "").split(delimiter)) {
for (const ext of pathExt) {
const candidate = resolve(dir, bin + ext);
try { accessSync(candidate, X_OK); return candidate; } catch {}
}
}
return null;
}
// Bun.$ is the shell template tag (e.g. $\`osascript ...\`). Only used by
// computer-use-input/darwin — stub it so the top-level destructuring
// \`var { $ } = globalThis.Bun\` doesn't crash.
function $(parts, ...args) {
throw new Error("Bun.$ shell API is not available in Node.js. Use Bun runtime for this feature.");
}
function hash(data, seed) {
let h = ((seed || 0) ^ 0x811c9dc5) >>> 0;
for (let i = 0; i < data.length; i++) {
h ^= data.charCodeAt(i);
h = Math.imul(h, 0x01000193) >>> 0;
}
return h;
}
globalThis.Bun = { which, $, hash };
}
import "./cli.js"
`
await writeFile(cliNode, NODE_BUN_POLYFILL)
// NOTE: when new Bun-specific globals appear in bundled output, add them here.
await writeFile(cliNode, '#!/usr/bin/env node\nimport "./cli.js"\n')

// Make both executable
const { chmodSync } = await import('fs')
Expand Down
3 changes: 2 additions & 1 deletion bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 11 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "claude-code-best",
"version": "1.3.6",
"version": "1.3.7",
"description": "Reverse-engineered Anthropic Claude Code CLI — interactive AI coding assistant in the terminal",
"type": "module",
"author": "claude-code-best <[email protected]>",
Expand Down Expand Up @@ -52,17 +52,14 @@
"health": "bun run scripts/health-check.ts",
"postinstall": "node scripts/postinstall.cjs && node scripts/setup-chrome-mcp.mjs",
"docs:dev": "npx mintlify dev",
"typecheck": "tsc --noEmit",
"rcs": "bun run scripts/rcs.ts"
},
"dependencies": {
"ws": "^8.20.0",
"@claude-code-best/mcp-chrome-bridge": "^2.0.7"
},
"devDependencies": {
"@types/he": "^1.2.3",
"@langfuse/otel": "^5.1.0",
"@langfuse/tracing": "^5.1.0",
"@types/lodash-es": "^4.17.12",
"@alcalzone/ansi-tokenize": "^0.3.0",
"@ant/claude-for-chrome-mcp": "workspace:*",
"@ant/computer-use-input": "workspace:*",
Expand All @@ -76,18 +73,20 @@
"@anthropic-ai/sdk": "^0.80.0",
"@anthropic-ai/vertex-sdk": "^0.14.4",
"@anthropic/ink": "workspace:*",
"@claude-code-best/builtin-tools": "workspace:*",
"@claude-code-best/agent-tools": "workspace:*",
"@claude-code-best/mcp-client": "workspace:*",
"@aws-sdk/client-bedrock": "^3.1020.0",
"@aws-sdk/client-bedrock-runtime": "^3.1020.0",
"@aws-sdk/client-sts": "^3.1020.0",
"@aws-sdk/credential-provider-node": "^3.972.28",
"@aws-sdk/credential-providers": "^3.1020.0",
"@azure/identity": "^4.13.1",
"@biomejs/biome": "^2.4.10",
"@claude-code-best/agent-tools": "workspace:*",
"@claude-code-best/builtin-tools": "workspace:*",
"@claude-code-best/mcp-client": "workspace:*",
"@commander-js/extra-typings": "^14.0.0",
"@growthbook/growthbook": "^1.6.5",
"@langfuse/otel": "^5.1.0",
"@langfuse/tracing": "^5.1.0",
"@modelcontextprotocol/sdk": "^1.29.0",
"@opentelemetry/api": "^1.9.1",
"@opentelemetry/api-logs": "^0.214.0",
Expand All @@ -110,8 +109,11 @@
"@sentry/node": "^10.47.0",
"@smithy/core": "^3.23.13",
"@smithy/node-http-handler": "^4.5.1",
"@types/bun": "^1.3.11",
"@types/bun": "^1.3.12",
"@types/cacache": "^20.0.1",
"@types/he": "^1.2.3",
"@types/lodash-es": "^4.17.12",
"@types/node": "^25.6.0",
"@types/picomatch": "^4.0.3",
"@types/plist": "^3.0.5",
"@types/proper-lockfile": "^4.1.4",
Expand Down
5 changes: 5 additions & 0 deletions packages/@ant/claude-for-chrome-mcp/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": "../../../tsconfig.base.json",
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist"]
}
38 changes: 20 additions & 18 deletions packages/@ant/computer-use-input/src/backends/darwin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
* mouse and keyboard via CoreGraphics events and System Events.
*/

import { $ } from 'bun'
import { execFile, execFileSync } from 'child_process'
import { promisify } from 'util'
import type { FrontmostAppInfo, InputBackend } from '../types.js'

const execFileAsync = promisify(execFile)

const KEY_MAP: Record<string, number> = {
return: 36, enter: 36, tab: 48, space: 49, delete: 51, backspace: 51,
escape: 53, esc: 53,
Expand All @@ -25,13 +28,17 @@ const MODIFIER_MAP: Record<string, string> = {
}

async function osascript(script: string): Promise<string> {
const result = await $`osascript -e ${script}`.quiet().nothrow().text()
return result.trim()
const { stdout } = await execFileAsync('osascript', ['-e', script], {
encoding: 'utf-8',
})
return stdout.trim()
}

async function jxa(script: string): Promise<string> {
const result = await $`osascript -l JavaScript -e ${script}`.quiet().nothrow().text()
return result.trim()
const { stdout } = await execFileAsync('osascript', ['-l', 'JavaScript', '-e', script], {
encoding: 'utf-8',
})
return stdout.trim()
}

function buildMouseJxa(eventType: string, x: number, y: number, btn: number, clickState?: number): string {
Expand Down Expand Up @@ -115,19 +122,14 @@ export const typeText: InputBackend['typeText'] = async (text) => {

export const getFrontmostAppInfo: InputBackend['getFrontmostAppInfo'] = () => {
try {
const result = Bun.spawnSync({
cmd: ['osascript', '-e', `
tell application "System Events"
set frontApp to first application process whose frontmost is true
set appName to name of frontApp
set bundleId to bundle identifier of frontApp
return bundleId & "|" & appName
end tell
`],
stdout: 'pipe',
stderr: 'pipe',
})
const output = new TextDecoder().decode(result.stdout).trim()
const output = execFileSync('osascript', ['-e', `
tell application "System Events"
set frontApp to first application process whose frontmost is true
set appName to name of frontApp
set bundleId to bundle identifier of frontApp
return bundleId & "|" & appName
end tell
`], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] }).trim()
if (!output || !output.includes('|')) return null
const [bundleId, appName] = output.split('|', 2)
return { bundleId: bundleId!, appName: appName! }
Expand Down
5 changes: 5 additions & 0 deletions packages/@ant/computer-use-input/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": "../../../tsconfig.base.json",
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist"]
}
5 changes: 5 additions & 0 deletions packages/@ant/computer-use-mcp/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": "../../../tsconfig.base.json",
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist"]
}
5 changes: 5 additions & 0 deletions packages/@ant/computer-use-swift/src/backends/darwin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,4 +274,9 @@ export const screenshot: ScreenshotAPI = {
if (displayId !== undefined) args.push('-D', String(displayId))
return captureScreenToBase64(args)
},

captureWindowTarget(_titleOrHwnd: string | number): ScreenshotResult | null {
// Window capture not supported on macOS via this backend
return null
},
}
5 changes: 5 additions & 0 deletions packages/@ant/computer-use-swift/src/backends/linux.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,4 +275,9 @@ export const screenshot: ScreenshotAPI = {
return { base64: '', width: 0, height: 0 }
}
},

captureWindowTarget(_titleOrHwnd: string | number): ScreenshotResult | null {
// Window capture not supported on Linux via this backend
return null
},
}
1 change: 1 addition & 0 deletions packages/@ant/computer-use-swift/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export interface ScreenshotAPI {
x: number, y: number, w: number, h: number,
outW: number, outH: number, quality: number, displayId?: number,
): Promise<ScreenshotResult>
captureWindowTarget(titleOrHwnd: string | number): ScreenshotResult | null
}

export interface SwiftBackend {
Expand Down
5 changes: 5 additions & 0 deletions packages/@ant/computer-use-swift/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": "../../../tsconfig.json",
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist"]
}
5 changes: 5 additions & 0 deletions packages/@ant/ink/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": "../../../tsconfig.base.json",
"include": ["src/**/*.ts", "src/**/*.tsx"],
"exclude": ["node_modules", "dist"]
}
4 changes: 2 additions & 2 deletions packages/agent-tools/src/__tests__/compat.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, test } from 'bun:test'
import type { CoreTool, Tool, Tools, AnyObject, ToolResult, ValidationResult, PermissionResult } from '@claude-code-best/agent-tools'
import type { Tool as HostTool } from '../../src/Tool.js'
import type { Tool as HostTool } from '../../../../src/Tool.js'

describe('agent-tools compatibility', () => {
test('CoreTool structural compatibility with host Tool', () => {
Expand All @@ -27,7 +27,7 @@ describe('agent-tools compatibility', () => {
}

// This assignment should work if HostTool structurally extends CoreTool
const coreTool: CoreTool = mockHostTool as CoreTool
const coreTool: CoreTool = mockHostTool as unknown as CoreTool
expect(coreTool.name).toBe('test')
expect(coreTool.isEnabled()).toBe(true)
})
Expand Down
5 changes: 5 additions & 0 deletions packages/agent-tools/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": "../../tsconfig.base.json",
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist"]
}
5 changes: 5 additions & 0 deletions packages/audio-capture-napi/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": "../../tsconfig.base.json",
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist"]
}
5 changes: 5 additions & 0 deletions packages/builtin-tools/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": "../../tsconfig.json",
"include": ["src/**/*.ts", "src/**/*.tsx"],
"exclude": ["node_modules", "dist"]
}
12 changes: 6 additions & 6 deletions packages/color-diff-napi/src/__tests__/color-diff.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,18 +72,18 @@ describe("detectColorMode", () => {

describe("detectLanguage", () => {
test("detects language from file extension", () => {
expect(detectLanguage("index.ts")).toBe("ts");
expect(detectLanguage("main.py")).toBe("py");
expect(detectLanguage("style.css")).toBe("css");
expect(detectLanguage("index.ts", null)).toBe("ts");
expect(detectLanguage("main.py", null)).toBe("py");
expect(detectLanguage("style.css", null)).toBe("css");
});

test("detects language from known filenames", () => {
expect(detectLanguage("Makefile")).toBe("makefile");
expect(detectLanguage("Dockerfile")).toBe("dockerfile");
expect(detectLanguage("Makefile", null)).toBe("makefile");
expect(detectLanguage("Dockerfile", null)).toBe("dockerfile");
});

test("returns null for unknown extensions", () => {
expect(detectLanguage("file.xyz123")).toBeNull();
expect(detectLanguage("file.xyz123", null)).toBeNull();
});
});

Expand Down
5 changes: 5 additions & 0 deletions packages/color-diff-napi/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": "../../tsconfig.base.json",
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist"]
}
5 changes: 5 additions & 0 deletions packages/image-processor-napi/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": "../../tsconfig.base.json",
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist"]
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ describe('InProcessTransport', () => {
let received: JSONRPCMessage | null = null
client.onmessage = (msg) => { received = msg }

await server.send({ jsonrpc: '2.0', result: 42, id: 1 })
await server.send({ jsonrpc: '2.0', result: 42, id: 1 } as any)

await new Promise(resolve => setTimeout(resolve, 10))

Expand Down
6 changes: 3 additions & 3 deletions packages/mcp-client/src/__tests__/discovery.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ describe('discoverTools', () => {
expect(tool.name).toBe('mcp__my-server__search')
expect(tool.mcpInfo).toEqual({ serverName: 'my-server', toolName: 'search' })
expect(tool.isMcp).toBe(true)
expect(tool.isReadOnly()).toBe(true)
expect(tool.userFacingName()).toBe('Search Items')
expect(await tool.description()).toBe('Search for items')
expect(tool.isReadOnly({} as any)).toBe(true)
expect(tool.userFacingName(undefined)).toBe('Search Items')
expect(await tool.description({} as any, { isNonInteractiveSession: false, toolPermissionContext: {}, tools: [] })).toBe('Search for items')
})

test('respects skipPrefix option', async () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/mcp-client/src/__tests__/manager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ describe('createMcpManager', () => {

const result = await manager.connect('test-server', { command: 'npx', args: [] })
expect(result.type).toBe('connected')
expect(connectedEvent).toBe('test-server')
expect(connectedEvent as unknown as string).toBe('test-server')
})

test('disconnect calls cleanup and emits disconnected', async () => {
Expand Down
5 changes: 5 additions & 0 deletions packages/mcp-client/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": "../../tsconfig.base.json",
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist"]
}
5 changes: 5 additions & 0 deletions packages/modifiers-napi/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": "../../tsconfig.base.json",
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist"]
}
Loading
Loading