Skip to content
源码分析手册

cli-modes-and-flags:Claude Code CLI 的启动模式和关键参数

Claude Code 的命令行界面(CLI)远超一个简单的输入框。它通过复杂的参数解析和模式切换,支撑了从“交互式结对编程”到“自动化脚本执行”的多种场景。

从定义开始

本质上,它是一个基于 commander.js 构建的命令行分发器。它负责把用户的原始 Shell 输入转化为 AppState 的初始配置,并决定最终是进入 launchRepl(交互式界面)还是 runHeadless(静默打印)。

代码里的真实逻辑

主要的命令行定义和处理逻辑位于 claude-code-opensource/src/main.tsx

1. 核心启动模式(Mode Selection)

源码通过识别不同的 Flag 来分流执行路径:

  • Interactive Mode(默认模式): 如果不带 -p 参数,系统会通过 launchRepl 启动基于 React Ink 的终端 UI。
  • One-shot Mode (-p, --print): 通过 src/cli/print.ts 中的 runHeadless 实现。它会执行推理循环,直接将结果输出到 stdout,然后进程退出。
  • Resume Mode (-r, --resume [session]): 如果指定了 -r,源码会调用 loadConversationForResume。它会去 ~/.claude/sessions/ 目录下根据 UUID 或标题模糊匹配历史记录,将其加载进当前会话。
  • Continue Mode (-c, --continue): 这是 -r 的快捷方式,直接加载“最后一次”未完成的会话。

2. 关键参数的传递路径

  • --model: 在 main.tsx 中解析后,被注入到 ToolUseContextoptions.mainLoopModel。如果用户没填,则从 getInitialSettings() 中读取默认值(通常是 claude-3-7-sonnet-latest)。
  • --max-turns: 直接映射到推理引擎的 maxTurns 约束。如果不传,源码通常不设上限或使用内置的安全水位线。
  • --output-format: 支持 textjson。这会改变输出样式,同时也影响模型收到的 System Prompt,要求其以特定结构返回数据。

3. 模式对推理循环的影响

模式决定了 PermissionMode

  • 在交互模式下,默认权限通常是 default(遇事弹窗)。
  • -p 模式下,如果用户没有显式授权,很多具有副作用的工具(如 bash)可能会因为无法弹出 TTY 确认框而报错退出。

踩坑指南

  1. 参数优先级:命令行 Flag > 环境变量 > 本地配置文件(~/.claude/settings.json)。
  2. -p 模式的阻塞性:在 -p 模式下,Claude Code 依然会运行完整的 Agentic Loop。如果模型陷入工具死循环,你必须通过 --max-turns 来强行勒马。
  3. 隐式 Resume:如果你不带参数启动,且配置了 autoResume(默认开启),Claude 会尝试自动恢复之前的上下文,这由 src/main.tsx 的初始加载逻辑控制。

接下来看什么

  • 想看 TUI 怎么渲染?查看 claude-code-opensource/src/replLauncher.tsx
  • 想看如何加载历史?查看 claude-code-opensource/src/history.ts

源码锚点

📄 src/main.tsx — `program.name('claude')` CLI 定义的起点。L958-978 of 4684
tsx
    void loadPolicyLimits();
    profileCheckpoint('preAction_after_remote_settings');

    // Load settings sync (non-blocking, fail-open)
    // CLI: uploads local settings to remote (CCR download is handled by print.ts)
    if (feature('UPLOAD_USER_SETTINGS')) {
      void import('./services/settingsSync/index.js').then(m => m.uploadUserSettingsInBackground());
    }
    profileCheckpoint('preAction_after_settings_sync');
  });
  program.name('claude').description(`Claude Code - starts an interactive session by default, use -p/--print for non-interactive output`).argument('[prompt]', 'Your prompt', String)
  // Subcommands inherit helpOption via commander's copyInheritedSettings —
  // setting it once here covers mcp, plugin, auth, and all other subcommands.
  .helpOption('-h, --help', 'Display help for command').option('-d, --debug [filter]', 'Enable debug mode with optional category filtering (e.g., "api,hooks" or "!1p,!file")', (_value: string | true) => {
    // If value is provided, it will be the filter string
    // If not provided but flag is present, value will be true
    // The actual filtering is handled in debug.ts by parsing process.argv
    return true;
  }).addOption(new Option('-d2e, --debug-to-stderr', 'Enable debug mode (to stderr)').argParser(Boolean).hideHelp()).option('--debug-file <path>', 'Write debug logs to a specific file path (implicitly enables debug mode)', () => true).option('--verbose', 'Override verbose mode setting from config', () => true).option('-p, --print', 'Print response and exit (useful for pipes). Note: The workspace trust dialog is skipped when Claude is run with the -p mode. Only use this flag in directories you trust.', () => true).option('--bare', 'Minimal mode: skip hooks, LSP, plugin sync, attribution, auto-memory, background prefetches, keychain reads, and CLAUDE.md auto-discovery. Sets CLAUDE_CODE_SIMPLE=1. Anthropic auth is strictly ANTHROPIC_API_KEY or apiKeyHelper via --settings (OAuth and keychain are never read). 3P providers (Bedrock/Vertex/Foundry) use their own credentials. Skills still resolve via /skill-name. Explicitly provide context via: --system-prompt[-file], --append-system-prompt[-file], --add-dir (CLAUDE.md dirs), --mcp-config, --settings, --agents, --plugin-dir.', () => true).addOption(new Option('--init', 'Run Setup hooks with init trigger, then continue').hideHelp()).addOption(new Option('--init-only', 'Run Setup and SessionStart:startup hooks, then exit').hideHelp()).addOption(new Option('--maintenance', 'Run Setup hooks with maintenance trigger, then continue').hideHelp()).addOption(new Option('--output-format <format>', 'Output format (only works with --print): "text" (default), "json" (single result), or "stream-json" (realtime streaming)').choices(['text', 'json', 'stream-json'])).addOption(new Option('--json-schema <schema>', 'JSON Schema for structured output validation. ' + 'Example: {"type":"object","properties":{"name":{"type":"string"}},"required":["name"]}').argParser(String)).option('--include-hook-events', 'Include all hook lifecycle events in the output stream (only works with --output-format=stream-json)', () => true).option('--include-partial-messages', 'Include partial message chunks as they arrive (only works with --print and --output-format=stream-json)', () => true).addOption(new Option('--input-format <format>', 'Input format (only works with --print): "text" (default), or "stream-json" (realtime streaming input)').choices(['text', 'stream-json'])).option('--mcp-debug', '[DEPRECATED. Use --debug instead] Enable MCP debug mode (shows MCP server errors)', () => true).option('--dangerously-skip-permissions', 'Bypass all permission checks. Recommended only for sandboxes with no internet access.', () => true).option('--allow-dangerously-skip-permissions', 'Enable bypassing all permission checks as an option, without it being enabled by default. Recommended only for sandboxes with no internet access.', () => true).addOption(new Option('--thinking <mode>', 'Thinking mode: enabled (equivalent to adaptive), disabled').choices(['enabled', 'adaptive', 'disabled']).hideHelp()).addOption(new Option('--max-thinking-tokens <tokens>', '[DEPRECATED. Use --thinking instead for newer models] Maximum number of thinking tokens (only works with --print)').argParser(Number).hideHelp()).addOption(new Option('--max-turns <turns>', 'Maximum number of agentic turns in non-interactive mode. This will early exit the conversation after the specified number of turns. (only works with --print)').argParser(Number).hideHelp()).addOption(new Option('--max-budget-usd <amount>', 'Maximum dollar amount to spend on API calls (only works with --print)').argParser(value => {
    const amount = Number(value);
    if (isNaN(amount) || amount <= 0) {
📄 src/cli/print.ts — `runHeadless` 处理 `-p` 逻辑的地方。L983-1003 of 5595
typescript
  sdkMcpConfigs: Record<string, McpSdkServerConfig>,
  getAppState: () => AppState,
  setAppState: (f: (prev: AppState) => AppState) => void,
  agents: AgentDefinition[],
  options: {
    verbose: boolean | undefined
    jsonSchema: Record<string, unknown> | undefined
    permissionPromptToolName: string | undefined
    allowedTools: string[] | undefined
    thinkingConfig: ThinkingConfig | undefined
    maxTurns: number | undefined
    maxBudgetUsd: number | undefined
    taskBudget: { total: number } | undefined
    systemPrompt: string | undefined
    appendSystemPrompt: string | undefined
    userSpecifiedModel: string | undefined
    fallbackModel: string | undefined
    replayUserMessages?: boolean | undefined
    includePartialMessages?: boolean | undefined
    enableAuthStatus?: boolean | undefined
    agent?: string | undefined

基于 Claude Code v2.1.88 开源快照的深度分析