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: 1 addition & 1 deletion packages/code/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"dev": "bun --watch run src/cli.ts",
"build": "echo 'Build handled by root build:npm'",
"typecheck": "tsc -p tsconfig.json --noEmit",
"test": "bun test ./src"
"test": "bun test ./test"
},
"dependencies": {
"@pleaseai/code-format": "workspace:*",
Expand Down
150 changes: 0 additions & 150 deletions packages/code/src/__tests__/cli.test.ts

This file was deleted.

52 changes: 14 additions & 38 deletions packages/code/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import process from 'node:process'
import { Format } from '@pleaseai/code-format'
import pkg from '../package.json'
import { runLSPDiagnostics } from './hooks/lsp'
import { parseArgs } from './utils'

const VERSION = pkg.version

Expand All @@ -31,38 +32,6 @@ interface HookInput {
tool_use_id: string
}

interface ParsedArgs {
command: string
args: string[]
flags: Record<string, string | boolean>
}

function parseArgs(argv: string[]): ParsedArgs {
const args = argv.slice(2)
const flags: Record<string, string | boolean> = {}
const positional: string[] = []

for (let i = 0; i < args.length; i++) {
const arg = args[i]!
if (arg.startsWith('--')) {
const [key, value] = arg.slice(2).split('=')
flags[key!] = value ?? true
}
else if (arg.startsWith('-')) {
flags[arg.slice(1)] = true
}
else {
positional.push(arg)
}
}

return {
command: positional[0] ?? 'help',
args: positional.slice(1),
flags,
}
}

async function readStdinJson(): Promise<HookInput> {
const chunks: Buffer[] = []
for await (const chunk of Bun.stdin.stream()) {
Expand All @@ -77,8 +46,9 @@ async function readStdinJson(): Promise<HookInput> {
try {
return JSON.parse(text)
}
catch {
throw new Error('Invalid JSON input from stdin')
catch (error: unknown) {
const message = error instanceof Error ? error.message : 'unknown parse error'
throw new Error(`Invalid JSON input from stdin: ${message}`)
}
}

Expand Down Expand Up @@ -157,6 +127,16 @@ Environment:
async function main(): Promise<void> {
const { command, args, flags } = parseArgs(process.argv)

// Check for version/help flags first (before command routing)
if (flags.v || flags.version) {
versionCommand()
return
}
if (flags.h || flags.help) {
helpCommand()
return
}

const projectDir
= (flags.project as string)
?? process.env.CODE_PROJECT_PATH
Expand Down Expand Up @@ -208,14 +188,10 @@ async function main(): Promise<void> {
}

case 'version':
case '-v':
case '--version':
versionCommand()
break

case 'help':
case '-h':
case '--help':
helpCommand()
break

Expand Down
16 changes: 13 additions & 3 deletions packages/code/src/launcher/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ const PACKAGE_NAME = 'code'
/**
* Detect if running on musl libc (Alpine Linux, etc.)
* Based on oxc-project's detection approach
*
* Uses multiple detection methods:
* 1. Check /usr/bin/ldd content for "musl"
* 2. Check process.report for glibc version (Node 12+)
* 3. Run ldd --version and check output
*
* All methods use fallback behavior - if one fails (expected or unexpected),
* the next method is tried. Common expected errors: ENOENT (file not found).
* Unexpected errors (EACCES, ENOMEM) are silently ignored with fallback.
*/
function isMusl(): boolean {
// Method 1: Check /usr/bin/ldd for musl
Expand All @@ -30,7 +39,8 @@ function isMusl(): boolean {
}
}
catch {
// File doesn't exist or can't be read
// Expected: ENOENT (file not found) on non-Linux or some distros
// Unexpected errors (EACCES, ENOMEM, etc.) also fall through to next method
}

// Method 2: Check process.report for glibc version (Node 12+)
Expand All @@ -41,7 +51,7 @@ function isMusl(): boolean {
}
}
catch {
// process.report not available
// Expected: process.report not available in all environments
}

// Method 3: Run ldd --version and check output
Expand All @@ -52,7 +62,7 @@ function isMusl(): boolean {
}
}
catch {
// ldd not available or failed
// Expected: ldd not available on non-Linux systems
}

return false
Expand Down
54 changes: 54 additions & 0 deletions packages/code/src/utils/args.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* CLI argument parsing utilities
*/

export interface ParsedArgs {
command: string
args: string[]
flags: Record<string, string | boolean>
}

/**
* Parse command-line arguments into structured format.
*
* Supports:
* - --flag=value (equals syntax)
* - --flag (boolean flag)
* - -f (short boolean flag)
* - Positional arguments
*
* @param argv - Process argv array (first 2 elements are node/bun path and script path)
* @returns Parsed arguments with command, positional args, and flags
*/
export function parseArgs(argv: string[]): ParsedArgs {
const args = argv.slice(2)
const flags: Record<string, string | boolean> = {}
const positional: string[] = []

for (let i = 0; i < args.length; i++) {
const arg = args[i]!
if (arg.startsWith('--')) {
const eqIndex = arg.indexOf('=')
if (eqIndex > -1) {
const key = arg.slice(2, eqIndex)
const value = arg.slice(eqIndex + 1)
flags[key] = value
}
else {
flags[arg.slice(2)] = true
}
}
else if (arg.startsWith('-')) {
flags[arg.slice(1)] = true
}
else {
positional.push(arg)
}
}

return {
command: positional[0] ?? 'help',
args: positional.slice(1),
flags,
}
}
6 changes: 6 additions & 0 deletions packages/code/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* Utility modules for @pleaseai/code
*/

export { parseArgs, type ParsedArgs } from './args'
export { getPotentialBinaryPaths, getTarget, isMusl } from './platform'
Loading