diff --git a/backend/app.ts b/backend/app.ts index 7f4c2c1b..9be24740 100644 --- a/backend/app.ts +++ b/backend/app.ts @@ -24,6 +24,7 @@ export interface AppConfig { debugMode: boolean; staticPath: string; cliPath: string; // Actual CLI script path detected by validateClaudeCli + claudeArgs: string[]; // Additional CLI arguments to pass to claude-code } export function createApp( @@ -52,6 +53,7 @@ export function createApp( debugMode: config.debugMode, runtime, cliPath: config.cliPath, + claudeArgs: config.claudeArgs, }), ); diff --git a/backend/cli/args.ts b/backend/cli/args.ts index 1b9d75e1..0bdc1891 100644 --- a/backend/cli/args.ts +++ b/backend/cli/args.ts @@ -13,6 +13,7 @@ export interface ParsedArgs { port: number; host: string; claudePath?: string; + claudeArgs: string[]; } export function parseCliArgs(): ParsedArgs { @@ -48,7 +49,12 @@ export function parseCliArgs(): ParsedArgs { "--claude-path ", "Path to claude executable (overrides automatic detection)", ) - .option("-d, --debug", "Enable debug mode", false); + .option("-d, --debug", "Enable debug mode", false) + .option( + "--claude-arg ", + "Pass additional arguments to claude-code CLI (can be used multiple times)", + [], + ); // Parse arguments - Commander.js v14 handles this automatically program.parse(getArgs(), { from: "user" }); @@ -63,5 +69,6 @@ export function parseCliArgs(): ParsedArgs { port: options.port, host: options.host, claudePath: options.claudePath, + claudeArgs: options.claudeArg || [], }; } diff --git a/backend/cli/deno.ts b/backend/cli/deno.ts index df367530..853181ac 100644 --- a/backend/cli/deno.ts +++ b/backend/cli/deno.ts @@ -35,6 +35,7 @@ async function main(runtime: DenoRuntime) { debugMode: args.debug, staticPath, cliPath: cliPath, + claudeArgs: args.claudeArgs, }); // Start server (only show this message when everything is ready) diff --git a/backend/cli/node.ts b/backend/cli/node.ts index 7690a8e2..b0b0c219 100644 --- a/backend/cli/node.ts +++ b/backend/cli/node.ts @@ -40,6 +40,7 @@ async function main(runtime: NodeRuntime) { debugMode: args.debug, staticPath, cliPath, + claudeArgs: args.claudeArgs, }); // Start server (only show this message when everything is ready) diff --git a/backend/handlers/chat.ts b/backend/handlers/chat.ts index b0d283e0..55080512 100644 --- a/backend/handlers/chat.ts +++ b/backend/handlers/chat.ts @@ -13,6 +13,7 @@ import { logger } from "../utils/logger.ts"; * @param allowedTools - Optional array of allowed tool names * @param workingDirectory - Optional working directory for Claude execution * @param permissionMode - Optional permission mode for Claude execution + * @param claudeArgs - Optional additional CLI arguments to pass to claude-code * @returns AsyncGenerator yielding StreamResponse objects */ async function* executeClaudeCommand( @@ -24,6 +25,7 @@ async function* executeClaudeCommand( allowedTools?: string[], workingDirectory?: string, permissionMode?: PermissionMode, + claudeArgs?: string[], ): AsyncGenerator { let abortController: AbortController; @@ -44,7 +46,7 @@ async function* executeClaudeCommand( options: { abortController, executable: "node" as const, - executableArgs: [], + executableArgs: claudeArgs || [], pathToClaudeCodeExecutable: cliPath, ...(sessionId ? { resume: sessionId } : {}), ...(allowedTools ? { allowedTools } : {}), @@ -94,13 +96,19 @@ export async function handleChatRequest( requestAbortControllers: Map, ) { const chatRequest: ChatRequest = await c.req.json(); - const { cliPath } = c.var.config; + const { cliPath, claudeArgs: globalClaudeArgs } = c.var.config; logger.chat.debug( "Received chat request {*}", chatRequest as unknown as Record, ); + // Merge global claudeArgs with request-specific claudeArgs (if any) + const mergedClaudeArgs = [ + ...(globalClaudeArgs || []), + ...(chatRequest.claudeArgs || []), + ]; + const stream = new ReadableStream({ async start(controller) { try { @@ -113,6 +121,7 @@ export async function handleChatRequest( chatRequest.allowedTools, chatRequest.workingDirectory, chatRequest.permissionMode, + mergedClaudeArgs, )) { const data = JSON.stringify(chunk) + "\n"; controller.enqueue(new TextEncoder().encode(data)); diff --git a/backend/types.ts b/backend/types.ts index 1434ba5c..89d39d78 100644 --- a/backend/types.ts +++ b/backend/types.ts @@ -9,5 +9,6 @@ export interface AppConfig { debugMode: boolean; runtime: Runtime; cliPath: string; // Path to actual CLI script detected by validateClaudeCli + claudeArgs: string[]; // Additional CLI arguments to pass to claude-code // Future configuration options can be added here } diff --git a/docs/CLAUDE_ARGS_USAGE.md b/docs/CLAUDE_ARGS_USAGE.md new file mode 100644 index 00000000..6a6d7fb1 --- /dev/null +++ b/docs/CLAUDE_ARGS_USAGE.md @@ -0,0 +1,190 @@ +# Claude Code Web UI - CLI Argument Passthrough + +## Overview + +This feature allows you to pass through any claude-code CLI arguments when starting claude-code-webui, including `--dangerously-skip-permissions` and others. + +## Usage + +### 1. Global Configuration (Recommended) + +Pass arguments through `--claude-arg` when starting the web UI: + +```bash +# Single argument +node backend/cli/node.js --claude-arg --dangerously-skip-permissions + +# Multiple arguments +node backend/cli/node.js \ + --claude-arg --dangerously-skip-permissions \ + --claude-arg --debug \ + --claude-arg --permission-mode=bypassPermissions + +# Arguments with values +node backend/cli/node.js \ + --claude-arg --model=sonnet \ + --claude-arg --allowed-tools=Bash,Edit,Read +``` + +### 2. Argument Merging + +- **Global Arguments**: Passed via `--claude-arg` at startup, applied to all requests +- **Request-Specific Arguments**: Passed via `claudeArgs` field in API requests, applied only to current request + +Argument merging logic: + +``` +Final Arguments = [Global Arguments, ..., Request-Specific Arguments, ...] +``` + +## Implementation Details + +### 1. CLI Argument Parsing (backend/cli/args.ts) + +```typescript +interface ParsedArgs { + debug: boolean; + port: number; + host: string; + claudePath?: string; + claudeArgs: string[]; // New field +} + +program.option( + "--claude-arg ", + "Pass additional arguments to claude-code CLI (can be used multiple times)", + [], +); +``` + +### 2. Type Definitions (shared/types.ts) + +```typescript +export interface ChatRequest { + message: string; + sessionId?: string; + requestId: string; + allowedTools?: string[]; + workingDirectory?: string; + permissionMode?: "default" | "plan" | "acceptEdits"; + claudeArgs?: string[]; // New field +} +``` + +### 3. Configuration Propagation (backend/handlers/chat.ts) + +```typescript +// Merge global and request-specific arguments +const mergedClaudeArgs = [ + ...(globalClaudeArgs || []), + ...(chatRequest.claudeArgs || []), +]; + +// Pass to claude-code CLI +for await (const sdkMessage of query({ + prompt: processedMessage, + options: { + abortController, + executable: "node" as const, + executableArgs: mergedClaudeArgs, // Use merged arguments + pathToClaudeCodeExecutable: cliPath, + // ... other options + }, +})) { + // ... +} +``` + +## Supported Parameter Examples + +Based on `claude --help` output, the following parameters are supported: + +### Permission-related + +- `--dangerously-skip-permissions` - Skip all permission checks +- `--allow-dangerously-skip-permissions` - Enable bypassing all permission checks as an option +- `--permission-mode ` - Permission mode (acceptEdits, bypassPermissions, default, dontAsk, plan) + +### Tool-related + +- `--allowed-tools ` - Comma or space-separated list of tool names to allow +- `--disallowed-tools ` - Comma or space-separated list of tool names to deny +- `--tools ` - Specify the list of available tools from the built-in set + +### Model-related + +- `--model ` - Model for the current session (sonnet, opus, etc.) +- `--fallback-model ` - Enable automatic fallback to specified model when default model is overloaded + +### MCP-related + +- `--mcp-config ` - Load MCP servers from JSON files or strings +- `--strict-mcp-config` - Only use MCP servers from --mcp-config, ignoring all other MCP configurations + +### Debug-related + +- `--debug [filter]` - Enable debug mode with optional category filtering +- `--verbose` - Override verbose mode setting from config + +### Other + +- `--system-prompt ` - System prompt to use for the session +- `--append-system-prompt ` - Append a system prompt to the default system prompt +- `--settings ` - Path to a settings JSON file or a JSON string to load additional settings from +- `--add-dir ` - Additional directories to allow tool access to + +## API Usage Examples + +### 1. Frontend Integration + +If the frontend needs to support these arguments, you can add a settings interface: + +```typescript +const chatRequest: ChatRequest = { + message: "Hello Claude", + requestId: generateId(), + sessionId: "session-123", + allowedTools: ["Bash", "Edit"], + permissionMode: "default", + claudeArgs: ["--dangerously-skip-permissions"], // Request-specific arguments +}; + +fetch("/api/chat", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(chatRequest), +}); +``` + +### 2. Environment Variable Configuration + +You can also consider setting default arguments through environment variables: + +```bash +export CLAUDE_WEBUI_DEFAULT_ARGS="--dangerously-skip-permissions --debug" +node backend/cli/node.js +``` + +## Security Considerations + +1. **`--dangerously-skip-permissions`**: This parameter bypasses all permission checks and should only be used in sandboxed or trusted environments +2. **Tool Restrictions**: It's recommended to always explicitly specify `--allowed-tools` to limit available tools +3. **Directory Access**: Only add necessary directories when using `--add-dir` + +## Troubleshooting + +### 1. Arguments Not Taking Effect + +Check if the startup logs contain the passed-through arguments: + +```bash +node backend/cli/node.js --claude-arg --dangerously-skip-permissions --debug +``` + +### 2. Type Errors + +Ensure that frontend and backend type definitions are consistent, especially the `ChatRequest` interface. + +### 3. Parameter Conflicts + +Some parameters may conflict with each other, such as `--allowed-tools` and `--disallowed-tools`. Please refer to the claude-code official documentation. diff --git a/shared/types.ts b/shared/types.ts index 51b88793..b2012760 100644 --- a/shared/types.ts +++ b/shared/types.ts @@ -11,6 +11,7 @@ export interface ChatRequest { allowedTools?: string[]; workingDirectory?: string; permissionMode?: "default" | "plan" | "acceptEdits"; + claudeArgs?: string[]; } export interface AbortRequest {