/** * @license * Copyright 2025 Vybestack LLC * SPDX-License-Identifier: Apache-2.0 */ /** * Declarative yargs option definitions for the LLxprt CLI. * This module contains only static data — no command wiring, no runtime logic. */ import { OutputFormat } from '@vybestack/llxprt-code-core'; import type { Options } from 'yargs'; /** * Normalizes the `--debug` flag value. * * `--debug` accepts an optional comma-separated namespace list (e.g. * `--debug llxprt:core:*`). Yargs types the option as a string so the optional * value can be captured, but a bare `--debug` must still resolve to boolean * `true`, and explicit false-like values (`--debug=false`, `0`, `no`, `off`) * must resolve to boolean `false`. Any other string is preserved as the * namespace specifier and consumed downstream by the bootstrap debug parser. */ export function coerceDebugFlag( value: string | boolean | undefined, ): string | boolean | undefined { if (value === undefined) { return undefined; } if (value === '' || value === true) { return true; } if ( typeof value === 'string' && ['false', '0', 'no', 'off'].includes(value.trim().toLowerCase()) ) { return false; } return value; } /** * Options registered on the root command scope (accessible before subcommand dispatch). * These duplicate a subset of the inner-command options intentionally so that * `--provider`, `--key`, etc. are parseable during bootstrap arg scanning. */ export const rootOptions: Record = { telemetry: { type: 'boolean', description: 'Enable local telemetry output. By default output is written to the console; use --telemetry-outfile to write to a file.', }, 'telemetry-log-prompts': { type: 'boolean', description: 'Enable or disable logging of user prompts for telemetry. Overrides settings files.', }, 'telemetry-outfile': { type: 'string', description: 'Redirect all telemetry output to the specified file.', }, checkpointing: { alias: 'c', type: 'boolean', description: 'Enables checkpointing of file edits', default: false, }, 'experimental-acp': { type: 'boolean', description: 'Starts the agent in ACP mode', }, 'experimental-ui': { type: 'boolean', description: 'Use experimental terminal UI (requires bun and @vybestack/llxprt-ui)', }, 'allowed-mcp-server-names': { type: 'array', string: true, description: 'Allowed MCP server names', }, extensions: { alias: 'e', type: 'array', string: true, description: 'A list of extensions to use. If not provided, all extensions are used.', }, 'list-extensions': { alias: 'l', type: 'boolean', description: 'List all available extensions and exit.', }, provider: { type: 'string', description: 'The provider to use.', // Don't set default here, handle it in loadCliConfig }, 'ide-mode': { type: 'string', choices: ['enable', 'disable'], description: 'Enable or disable IDE mode', }, key: { type: 'string', description: 'API key for the current provider', }, keyfile: { type: 'string', description: 'Path to file containing API key for the current provider', }, 'key-name': { type: 'string', description: 'Load a named API key from the keyring (same as /key load )', }, baseurl: { type: 'string', description: 'Base URL for the current provider', }, proxy: { type: 'string', description: 'Proxy for gemini client, like schema://user:password@host:port', }, 'include-directories': { type: 'array', string: true, description: 'Additional directories to include in the workspace (comma-separated or multiple --include-directories)', coerce: (dirs: string[]) => dirs.flatMap((dir) => dir.split(',').map((d) => d.trim())), }, set: { type: 'array', string: true, description: 'Set an ephemeral setting via key=value (can be repeated)', coerce: (entries: unknown[]) => entries.map((entry) => { if (typeof entry !== 'string') { throw new Error( `Invalid value for --set: ${String(entry)}. Expected key=value string.`, ); } return entry; }), }, 'profile-load': { type: 'string', description: 'Load a saved profile configuration on startup', }, profile: { type: 'string', description: 'Inline JSON profile configuration (alternative to --profile-load for CI/CD)', }, 'load-memory-from-include-directories': { type: 'boolean', description: 'If true, when refreshing memory, LLXPRT.md files should be loaded from all directories that are added. If false, LLXPRT.md files should only be loaded from the primary working directory.', }, debug: { alias: 'd', type: 'string', coerce: coerceDebugFlag, description: 'Run in debug mode? (Optional: specify comma-separated namespaces, e.g., llxprt:core:*,llxprt:openai:*)', }, // Hidden internal-only ROOT option. A memory or sandbox direct-replacement // relaunch transports an env-origin bootstrap path here (without restoring // LLXPRT_JSP_BOOTSTRAP_FILE to the environment). Root scope so it is accepted // for both the launch command and subcommands (mcp, hooks, etc.). Never // user-facing; absent from help. 'jsp-bootstrap-internal-env-path': { type: 'string', hidden: true, }, }; /** * Options registered inside the default command handler (`$0 [promptWords...]`). * These are the primary CLI flags available to end users. */ export const innerCommandOptions: Record = { model: { alias: 'm', type: 'string', description: `Model`, // Don't set default here, handle it in loadCliConfig }, prompt: { alias: 'p', type: 'string', description: 'Prompt. Appended to input on stdin (if any).', }, 'prompt-interactive': { alias: 'i', type: 'string', description: 'Execute the provided prompt and continue in interactive mode', }, 'output-format': { type: 'string', choices: [OutputFormat.TEXT, OutputFormat.JSON, OutputFormat.STREAM_JSON], description: 'Output format for non-interactive mode (text, json, or stream-json).', }, quiet: { alias: 'q', type: 'boolean', description: 'Suppress tool calls, tool results, and intermediate text. Only the final response is written to stdout (non-interactive mode).', default: false, }, sandbox: { alias: 's', type: 'boolean', description: 'Run in sandbox?', }, 'sandbox-image': { type: 'string', description: 'Sandbox image URI.', }, 'sandbox-engine': { type: 'string', choices: ['auto', 'docker', 'podman', 'sandbox-exec', 'none'], description: 'Sandbox engine (auto|docker|podman|sandbox-exec|none).', }, 'sandbox-profile-load': { type: 'string', description: 'Load a sandbox profile from the sandboxes directory in your LLxprt config directory (/sandboxes/.json)', }, 'jsp-bootstrap': { type: 'string', description: 'Path to a JSP observation bootstrap file (preferred over the deprecated LLXPRT_JSP_BOOTSTRAP_FILE environment variable).', }, debug: { alias: 'd', type: 'string', coerce: coerceDebugFlag, description: 'Run in debug mode? (Optional: specify comma-separated namespaces, e.g., llxprt:core:*,llxprt:openai:*)', }, 'show-memory-usage': { type: 'boolean', description: 'Show memory usage in status bar', default: false, }, yolo: { alias: 'y', type: 'boolean', description: 'Automatically accept all actions (aka YOLO mode, see https://www.youtube.com/watch?v=xvFZjo5PgG0 for more details)?', default: false, }, 'approval-mode': { type: 'string', choices: ['default', 'auto_edit', 'yolo'], description: 'Set the approval mode: default (prompt for approval), auto_edit (auto-approve edit tools), yolo (auto-approve all tools)', }, telemetry: { type: 'boolean', description: 'Enable local telemetry output. By default output is written to the console; use --telemetry-outfile to write to a file.', }, 'telemetry-log-prompts': { type: 'boolean', description: 'Enable or disable logging of user prompts for telemetry. Overrides settings files.', }, 'telemetry-outfile': { type: 'string', description: 'Redirect all telemetry output to the specified file.', }, checkpointing: { alias: 'c', type: 'boolean', description: 'Enables checkpointing of file edits', default: false, }, 'experimental-acp': { type: 'boolean', description: 'Starts the agent in ACP mode', }, 'experimental-ui': { type: 'boolean', description: 'Use experimental terminal UI (requires bun and @vybestack/llxprt-ui)', }, 'allowed-mcp-server-names': { type: 'array', string: true, description: 'Allowed MCP server names', coerce: (mcpServerNames: string[]) => mcpServerNames.flatMap((mcpServerName) => mcpServerName.split(',').map((m) => m.trim()), ), }, 'allowed-tools': { type: 'array', string: true, description: 'Tools that are allowed to run without confirmation', coerce: (tools: string[]) => tools.flatMap((tool) => tool.split(',').map((t) => t.trim())), }, extensions: { alias: 'e', type: 'array', string: true, nargs: 1, description: 'A list of extensions to use. If not provided, all extensions are used.', coerce: (extensions: string[]) => extensions.flatMap((extension) => extension.split(',').map((e) => e.trim()), ), }, 'list-extensions': { alias: 'l', type: 'boolean', description: 'List all available extensions and exit.', }, proxy: { type: 'string', description: 'Proxy for LLxprt client, like schema://user:password@host:port', }, 'include-directories': { type: 'array', string: true, description: 'Additional directories to include in the workspace (comma-separated or multiple --include-directories)', coerce: (dirs: string[]) => dirs.flatMap((dir) => dir.split(',').map((d) => d.trim())), }, 'screen-reader': { type: 'boolean', description: 'Enable screen reader mode for accessibility.', }, 'session-summary': { type: 'string', description: 'File to write session summary to.', }, dumponerror: { type: 'boolean', description: 'Dump request body to the dumps directory in your LLxprt cache directory on API errors.', default: false, }, continue: { alias: 'C', type: 'string', skipValidation: true, description: 'Resume a previous session. Bare --continue resumes the most recent. --continue resumes a specific session.', coerce: (value: string): string => { if (value === '') { return value; } return value; }, }, 'list-sessions': { type: 'boolean', description: 'List recorded sessions for the current project.', default: false, }, 'delete-session': { type: 'string', description: 'Delete a recorded session by ID, prefix, or 1-based index.', }, nobrowser: { type: 'boolean', description: 'Skip browser OAuth flow, use manual code entry', default: false, }, 'image-input': { alias: 'I', type: 'array', string: true, description: 'Input image path for image editing mode. Repeatable up to 5 times (preserves order, no comma split).', }, 'image-output': { alias: 'O', type: 'string', description: 'Output image path (.png) for direct image generation/editing mode.', }, 'image-prompt': { alias: 'P', type: 'string', description: 'Prompt for direct image generation/editing mode. Requires --image-output.', }, }; /** Options that are deprecated and should show deprecation warnings. */ export const deprecatedOptions = [ { key: 'telemetry', message: 'Use settings.json instead. This flag will be removed in a future version.', }, { key: 'telemetry-log-prompts', message: 'Use settings.json instead. This flag will be removed in a future version.', }, { key: 'telemetry-outfile', message: 'Use settings.json instead. This flag will be removed in a future version.', }, { key: 'show-memory-usage', message: 'Use settings.json instead. This flag will be removed in a future version.', }, { key: 'sandbox-image', message: 'Use settings.json instead. This flag will be removed in a future version.', }, { key: 'proxy', message: 'Use settings.json instead. This flag will be removed in a future version.', }, { key: 'checkpointing', message: 'Use settings.json instead. This flag will be removed in a future version.', }, ] as const;