#!/usr/bin/env python
import argparse
import os
import sys
from pathlib import Path
import configargparse
import shtab
from cecli import __version__
from cecli.args_formatter import (
DotEnvFormatter,
MarkdownHelpFormatter,
YamlHelpFormatter,
)
from cecli.deprecated_args import add_deprecated_mcp_args, add_deprecated_model_args
from .dump import dump # noqa: F401
# Deep-merge field sets
# These define which config keys require special merge handling for
# .cecli.conf.yml files (deep-merge instead of shallow-overwrite).
DEEP_MERGE_LIST_FIELDS: frozenset[str] = frozenset(
{
# Top-level argparse action="append" args
# These are excluded from configargparse for .cecli.conf.yml files
# (to prevent shallow overwrite), so they must be deep-merged here.
"rules",
"file",
"read",
"mcp_servers_files",
"acp_models",
"set_env",
"api_key",
"alias",
"exempt_paths",
"lint_cmd",
# Nested agent-config array fields
"skills_paths",
"skills_includelist",
"skills_excludelist",
"skills_init",
"subagent_paths",
"tools_paths",
"tools_includelist",
"tools_excludelist",
"servers_includelist",
"servers_excludelist",
"allowed_commands",
}
)
DEEP_MERGE_JSON_FIELDS: frozenset[str] = frozenset(
{
"agent_config",
"mcp_servers",
"hooks",
"model_providers",
"security_config",
"retries",
"custom",
"system_one",
"tui_config",
"workflows",
}
)
# Agent-config array fields (subset of DEEP_MERGE_LIST_FIELDS used by
# agent_coder to validate agent config before passing to sub-agents).
AGENT_CONFIG_LIST_FIELDS: frozenset[str] = frozenset(
{
"skills_paths",
"skills_includelist",
"skills_excludelist",
"skills_init",
"subagent_paths",
"tools_paths",
"tools_includelist",
"tools_excludelist",
"servers_includelist",
"servers_excludelist",
"allowed_commands",
}
)
def resolve_cecli_ignore_path(path_str, git_root=None):
path = Path(path_str)
if path.is_absolute():
return str(path)
elif git_root:
return str(Path(git_root) / path)
return str(path)
def default_env_file(git_root):
return os.path.join(git_root, ".env") if git_root else ".env"
def get_parser(default_config_files, git_root):
parser = configargparse.ArgumentParser(
description="cecli is AI pair programming in your terminal",
add_config_file_help=True,
default_config_files=default_config_files,
config_file_parser_class=configargparse.YAMLConfigFileParser,
auto_env_var_prefix="CECLI_",
)
# List of valid edit formats for argparse validation & shtab completion.
# Dynamically gather them from the registered coder classes so the list
# stays in sync if new formats are added.
from cecli.coders import EDIT_FORMAT_MAP
edit_format_choices = sorted(EDIT_FORMAT_MAP.keys())
group = parser.add_argument_group("Main model")
group.add_argument(
"files", metavar="FILE", nargs="*", help="files to edit with an LLM (optional)"
).complete = shtab.FILE
group.add_argument(
"--model",
metavar="MODEL",
default=None,
help="Specify the model to use for the main chat",
)
##########
group = parser.add_argument_group("API Keys and settings")
group.add_argument(
"--openai-api-key",
help="Specify the OpenAI API key",
)
group.add_argument(
"--anthropic-api-key",
help="Specify the Anthropic API key",
)
group.add_argument(
"--openai-api-base",
help="Specify the api base url",
)
group.add_argument(
"--set-env",
action="append",
metavar="ENV_VAR_NAME=value",
help="Set an environment variable (to control API settings, can be used multiple times)",
default=[],
)
group.add_argument(
"--api-key",
action="append",
metavar="PROVIDER=KEY",
help=(
"Set an API key for a provider (eg: --api-key provider= sets"
" PROVIDER_API_KEY=)"
),
default=[],
)
group = parser.add_argument_group("Model settings")
group.add_argument(
"--list-models",
"--models",
metavar="MODEL",
help="List known models which match the (partial) MODEL name",
)
group.add_argument(
"--configure-provider",
action="store_true",
default=False,
help="Force the interactive provider configuration wizard to run on startup",
)
group.add_argument(
"--model-settings-file",
metavar="MODEL_SETTINGS_FILE",
default=".cecli.model.settings.yml",
help="Specify a file with cecli model settings for unknown models",
).complete = shtab.FILE
group.add_argument(
"--model-metadata-file",
metavar="MODEL_METADATA_FILE",
default=".cecli.model.metadata.json",
help="Specify a file with context window and costs for unknown models",
).complete = shtab.FILE
group.add_argument(
"--alias",
action="append",
metavar="ALIAS:MODEL",
help="Add a model alias (can be used multiple times)",
)
group.add_argument(
"--model-overrides",
"--model-settings",
metavar="MODEL_OVERRIDES_JSON",
help=(
'Specify model tag overrides directly as JSON/YAML string (e.g., \'{"gpt-4o": {"high":'
' {"temperature": 0.8}}}\')'
),
default=None,
)
group.add_argument(
"--model-overrides-file",
metavar="MODEL_OVERRIDES_FILE",
default=".cecli.model.overrides.yml",
help=(
"Specify a file with model tag overrides (e.g., gpt-4o:high -> reasoning_effort: high)"
),
).complete = shtab.FILE
group.add_argument(
"--model-providers",
metavar="MODEL_PROVIDERS_JSON",
help=(
"Specify custom OpenAI-compatible model providers as a JSON/YAML string (e.g.,"
' \'{"my-provider": {"api_base": "https://...", "api_key_env": ["MY_KEY"]}}\')'
),
default=None,
)
group.add_argument(
"--reasoning-effort",
type=str,
help="Set the reasoning_effort API parameter (default: not set)",
)
group.add_argument(
"--thinking-tokens",
type=str,
help=(
"Set the thinking token budget for models that support it. Use 0 to disable. (default:"
" not set)"
),
)
group.add_argument(
"--show-thinking",
action=argparse.BooleanOptionalAction,
default=True,
help="Show reasoning content in the response (default: True)",
)
group.add_argument(
"--verify-ssl",
action=argparse.BooleanOptionalAction,
default=True,
help="Verify the SSL cert when connecting to models (default: True)",
)
group.add_argument(
"--timeout",
type=float,
default=None,
help="Timeout in seconds for API calls (default: None)",
)
group.add_argument(
"--edit-format",
"--chat-mode",
metavar="EDIT_FORMAT",
choices=edit_format_choices,
default=None,
help="Specify what edit format the LLM should use (default: agent)",
)
group.add_argument(
"--ask",
action="store_const",
dest="edit_format",
const="ask",
help="Use ask edit format for the main chat",
)
group.add_argument(
"--architect",
action="store_const",
dest="edit_format",
const="architect",
help="Use architect edit format for the main chat",
)
group.add_argument(
"--agent",
action="store_const",
dest="edit_format",
const="agent",
help="Use agent edit format for the main chat (autonomous file management)",
)
group.add_argument(
"--hashline",
action="store_const",
dest="edit_format",
const="hashline",
help="Use hashline edit format for the main chat",
)
group.add_argument(
"--auto-accept-architect",
action=argparse.BooleanOptionalAction,
default=True,
help="Enable/disable automatic acceptance of architect changes (default: True)",
)
group.add_argument(
"--weak-model",
metavar="WEAK_MODEL",
default=None,
help=(
"Specify the model to use for commit messages and chat history summarization (default"
" depends on --model)"
),
)
group.add_argument(
"--editor-model",
metavar="EDITOR_MODEL",
default=None,
help="Specify the model to use for editor tasks (default depends on --model)",
)
group.add_argument(
"--editor-edit-format",
metavar="EDITOR_EDIT_FORMAT",
choices=edit_format_choices,
default=None,
help="Specify the edit format for the editor model (default: depends on editor model)",
)
group.add_argument(
"--show-model-warnings",
action=argparse.BooleanOptionalAction,
default=True,
help="Only work with models that have meta-data available (default: True)",
)
group.add_argument(
"--check-model-accepts-settings",
action=argparse.BooleanOptionalAction,
default=True,
help=(
"Check if model accepts settings like reasoning_effort/thinking_tokens (default: True)"
),
)
group.add_argument(
"--max-chat-history-tokens",
type=int,
default=None,
help=(
"Soft limit on tokens for chat history, after which summarization begins."
" If unspecified, defaults to the model's max_chat_history_tokens."
),
)
group.add_argument(
"--max-reflections",
type=int,
default=3,
help="Maximum number of retries a model gets on malformed outputs (default: 3)",
)
group.add_argument(
"--max-tool-calls",
type=int,
default=25,
help="Maximum number of tool calls allowed per message (default: 25)",
)
group.add_argument(
"--cost-limit",
type=float,
default=None,
help="Cost limit per session, exceeding this forces prompt confirmation (default: None)",
)
group.add_argument(
"--file-diffs",
action=argparse.BooleanOptionalAction,
default=True,
help="Whether to store file diffs in context or reload files (default: True)",
)
group.add_argument(
"--retries",
metavar="RETRIES_JSON",
help=(
'Specify LLM retry configuration as a JSON/YAML string (e.g., \'{"retry_on_empty": '
'true, "retry-on-unauthorized": false, "fallbacks": {"models": {"model": '
'["backup-model"]}, "keys": {"openai": ["OPENAI_API_KEY", '
'"OPENAI_API_KEY_2"]}}}\')'
),
default=None,
)
#######
group = parser.add_argument_group("Customization Settings")
group.add_argument(
"--custom",
metavar="CUSTOM_JSON",
help=(
"Specify cecli customizations configurations (for prompts, commands, etc.) as a JSON"
" string"
),
default=None,
)
########
group = parser.add_argument_group("TUI Settings")
env_val = os.environ.get("CECLI_TUI")
if env_val is not None and env_val.lower() == "false":
tui_default = False
linear_output_default = True
else:
tui_default = None
linear_output_default = None
group.add_argument(
"--tui",
action=argparse.BooleanOptionalAction,
default=tui_default,
help="Launch Textual TUI interface",
)
group.add_argument(
"--tui-config",
metavar="TUI_CONFIG_JSON",
help="Specify TUI Mode configuration as a JSON string",
default=None,
)
#########
group = parser.add_argument_group("Workspace Settings")
group.add_argument(
"--workspaces",
type=str,
help="JSON/YAML configuration for workspace initialization",
)
group.add_argument(
"--workspace-name",
type=str,
help="Specify the workspace name to activate",
)
#########
group = parser.add_argument_group("Agent Settings")
group.add_argument(
"--agent-config",
metavar="AGENT_CONFIG_JSON",
help="Specify Agent Mode configuration as a JSON string",
default=None,
)
group.add_argument(
"--hooks",
metavar="HOOKS_CONFIG_JSON",
help="Specify hooks configuration as a JSON string",
default=None,
)
group.add_argument(
"--workflows",
metavar="WORKFLOWS_JSON",
help=(
"Specify workflows configuration as an inline JSON/YAML string (e.g.,"
' \'[{"name": "nightly", "tasks": [...]}]\') or a path to a workflows file'
),
default=None,
)
group.add_argument(
"--scheduler",
metavar="SCHEDULER_CONFIG_JSON",
help="Specify scheduler configuration as a JSON string",
default=None,
)
group.add_argument(
"--system-one",
metavar="SYSTEM_ONE_JSON",
help=(
"Specify the System One decision endpoint as a JSON/YAML string (e.g.,"
' \'{"api_base": "http://127.0.0.1:8000", "api_key_env": ["SYSTEM_ONE_API_KEY"],'
' "model_name": "von-latest"}\')'
),
default=None,
)
group.add_argument(
"--agent-model",
metavar="AGENT_MODEL",
default=None,
help="Specify the model to use for Agent mode (default depends on --model)",
)
group.add_argument(
"--auto-save",
action=argparse.BooleanOptionalAction,
default=False,
help=(
"Enable/disable automatic saving of sessions as --auto-save-session-name (default:"
" False)"
),
)
group.add_argument(
"--auto-save-session-name",
help="Specify session name for auto-save and auto-load (default: auto-save)",
default="auto-save",
)
group.add_argument(
"--auto-load",
action=argparse.BooleanOptionalAction,
default=False,
help=(
"Enable/disable automatic loading of --auto-save-session-name session on startup"
" (default: False)"
),
)
group.add_argument(
"--auto-memory",
action=argparse.BooleanOptionalAction,
default=False,
help="Enable/disable automatic fact memorization after changes and compaction (default: True)",
)
group.add_argument(
"--session-encrypt",
action=argparse.BooleanOptionalAction,
default=False,
help=(
"Encrypt saved sessions on disk (AES-256-GCM). Requires CECLI_SESSION_KEY or"
" --session-key-file (default: False)"
),
)
group.add_argument(
"--session-key-file",
metavar="SESSION_KEY_FILE",
default=None,
help=(
"File containing a urlsafe-base64 32-byte session encryption key"
" (default: use CECLI_SESSION_KEY only)"
),
).complete = shtab.FILE
group.add_argument(
"--mcp-servers",
metavar="MCP_CONFIG_JSON",
help="Specify MCP server configurations as a JSON string",
default=None,
)
group.add_argument(
"--mcp-servers-files",
metavar="MCP_CONFIG_FILES",
help="Specify a file path with MCP server configurations (can be specified multiple times)",
action="append",
default=[],
)
group.add_argument(
"--mcp-transport",
metavar="MCP_TRANSPORT",
help="Specify the transport for MCP servers (default: stdio)",
default="stdio",
choices=["stdio", "http", "sse"],
)
group.add_argument(
"--preserve-todo-list",
action="store_true",
help=(
"Deprecated: no longer needed because the todo list is saved and restored with"
" sessions. This flag has no effect and will be removed."
),
default=False,
)
group.add_argument(
"--use-enhanced-map",
action="store_true",
help="Use enhanced Repo Map that takes into account imports (default: False)",
default=True,
)
##########
group = parser.add_argument_group("Security Settings")
group.add_argument(
"--security-config",
metavar="SECURITY_CONFIG_JSON",
help=(
'Specify Security configuration as a JSON string (e.g., \'{"allowed-domains":'
' ["github.com"]}\')'
),
default=None,
)
##########
group = parser.add_argument_group("Context Compaction")
group.add_argument(
"--enable-context-compaction",
action=argparse.BooleanOptionalAction,
default=True,
help="Enable automatic compaction of chat history to conserve tokens (default: False)",
)
group.add_argument(
"--max-compaction-retries",
type=int,
default=3,
metavar="N",
choices=range(1, 11),
help="Maximum number of compactions per request before propagating the error (default: 3, min: 1, max: 10)",
)
group.add_argument(
"--context-compaction-max-tokens",
type=float,
default=None,
help=(
"The maximum number of tokens in the conversation before context compaction is"
" triggered. (default: 80%% of model's context window)"
),
)
group.add_argument(
"--context-compaction-summary-tokens",
type=int,
default=4096,
help="The target maximum number of tokens for the generated summary. (default: 4096)",
)
group.add_argument(
"--ignore-context-limit",
action=argparse.BooleanOptionalAction,
default=False,
help=(
"Ignore the model's context window limit when sending requests."
" WARNING: This may cause API errors or truncated responses. (default: False)"
),
)
##########
group = parser.add_argument_group("Cache settings")
group.add_argument(
"--cache-prompts",
action=argparse.BooleanOptionalAction,
default=False,
help="Enable caching of prompts (default: False)",
)
group.add_argument(
"--cache-keepalive-pings",
type=int,
default=0,
help="Number of times to ping at 5min intervals to keep prompt cache warm (default: 0)",
)
##########
group = parser.add_argument_group("Rate limiting")
group.add_argument(
"--tokens-per-minute",
type=int,
default=1000000,
help=(
"Set the maximum tokens sent per minute before rate limiting sleeps are"
" inserted before LLM API calls (default: 1000000, use 0 to disable)"
),
)
##########
group = parser.add_argument_group("Repomap settings")
group.add_argument(
"--map-tokens",
type=int,
default=None,
help="Suggested number of tokens to use for repo map, use 0 to disable",
)
group.add_argument(
"--map-refresh",
choices=["auto", "always", "files", "manual"],
default="auto",
help=(
"Control how often the repo map is refreshed. Options: auto, always, files, manual"
" (default: auto)"
),
)
group.add_argument(
"--map-multiplier-no-files",
type=float,
default=2,
help="Multiplier for map tokens when no files are specified (default: 2)",
)
group.add_argument(
"--map-max-line-length",
type=int,
default=100,
help=(
"Maximum line length for the repo map code. Prevents sending crazy long lines of"
" minified JS files etc. (default: 100)"
),
)
group.add_argument(
"--map-cache-dir",
metavar="MAP_CACHE_DIR",
dest="map_cache_dir",
default=".",
help=(
"Directory for the repository map cache .cecli.tags.cache.v#"
" (default: current directory)"
),
)
group.add_argument(
"--map-memory-cache",
action="store_true",
help="Store repo map in memory (default: False)",
default=False,
)
##########
group = parser.add_argument_group("History Files")
default_input_history_file = (
os.path.join(git_root, ".cecli/input.history") if git_root else ".cecli/input.history"
)
default_chat_history_file = (
os.path.join(git_root, ".cecli/chat.history") if git_root else ".cecli/chat.history"
)
group.add_argument(
"--input-history-file",
metavar="INPUT_HISTORY_FILE",
default=default_input_history_file,
help=f"Specify the chat input history file (default: {default_input_history_file})",
).complete = shtab.FILE
group.add_argument(
"--chat-history-file",
metavar="CHAT_HISTORY_FILE",
default=default_chat_history_file,
help=f"Specify the chat history file (default: {default_chat_history_file})",
).complete = shtab.FILE
#########
group = parser.add_argument_group("Input settings")
group.add_argument(
"--terminal-setup",
action=argparse.BooleanOptionalAction,
default=False,
help=(
"Auto-configure terminal emulator for shift+enter support for new lines (default:"
" False)"
),
)
group.add_argument(
"--exempt-paths",
action="append",
default=[],
help=(
"Specify a regex pattern for paths that should be exempted from file creation. "
"When /add matches a path matching any exempt pattern, it will not offer to "
"create the file. Can be used multiple times."
),
)
##########
group = parser.add_argument_group("Output settings")
group.add_argument(
"--dark-mode",
action="store_true",
help="Use colors suitable for a dark terminal background (default: False)",
default=False,
)
group.add_argument(
"--light-mode",
action="store_true",
help="Use colors suitable for a light terminal background (default: False)",
default=False,
)
group.add_argument(
"--pretty",
action=argparse.BooleanOptionalAction,
default=True,
help="Enable/disable pretty, colorized output (default: True)",
)
group.add_argument(
"--stream",
action=argparse.BooleanOptionalAction,
default=True,
help="Enable/disable streaming responses (default: True)",
)
group.add_argument(
"--spinner",
action=argparse.BooleanOptionalAction,
default=True,
help="Enable/disable the spinner while waiting for LLM responses (default: True)",
)
group.add_argument(
"--user-input-color",
default="#00cc00",
help="Set the color for user input (default: #00cc00)",
)
group.add_argument(
"--tool-output-color",
default=None,
help="Set the color for tool output (default: None)",
)
group.add_argument(
"--tool-error-color",
default="#FF2222",
help="Set the color for tool error messages (default: #FF2222)",
)
group.add_argument(
"--tool-warning-color",
default="#FFA500",
help="Set the color for tool warning messages (default: #FFA500)",
)
group.add_argument(
"--assistant-output-color",
default="#0088ff",
help="Set the color for assistant output (default: #0088ff)",
)
group.add_argument(
"--show-speed",
action="store_true",
help="Show token processing and generation speed in usage report (default: False)",
default=False,
)
group.add_argument(
"--use-reminders",
action=argparse.BooleanOptionalAction,
default=True,
help="Enable/disable injecting reminder messages (default: True)",
)
group.add_argument(
"--completion-menu-color",
metavar="COLOR",
default=None,
help="Set the color for the completion menu (default: terminal's default text color)",
)
group.add_argument(
"--completion-menu-bg-color",
metavar="COLOR",
default=None,
help=(
"Set the background color for the completion menu (default: terminal's default"
" background color)"
),
)
group.add_argument(
"--completion-menu-current-color",
metavar="COLOR",
default=None,
help=(
"Set the color for the current item in the completion menu (default: terminal's default"
" background color)"
),
)
group.add_argument(
"--completion-menu-current-bg-color",
metavar="COLOR",
default=None,
help=(
"Set the background color for the current item in the completion menu (default:"
" terminal's default text color)"
),
)
group.add_argument(
"--code-theme",
default="default",
help=(
"Set the markdown code theme (default: default, other options include monokai,"
" solarized-dark, solarized-light, or a Pygments builtin style,"
" see https://pygments.org/styles for available themes)"
),
)
group.add_argument(
"--show-diffs",
action="store_true",
help="Show diffs when committing changes (default: False)",
default=False,
)
##########
group = parser.add_argument_group("Git settings")
group.add_argument(
"--git",
action=argparse.BooleanOptionalAction,
default=True,
help="Enable/disable looking for a git repo (default: True)",
)
group.add_argument(
"--gitignore",
action=argparse.BooleanOptionalAction,
default=True,
help="Enable/disable adding .cecli* to .gitignore (default: True)",
)
group.add_argument(
"--add-gitignore-files",
action=argparse.BooleanOptionalAction,
default=False,
help="Enable/disable the addition of files listed in .gitignore to cecli's editing scope.",
)
default_cecli_ignore_file = (
os.path.join(git_root, ".cecli.ignore") if git_root else ".cecli.ignore"
)
group.add_argument(
"--cecli-ignore",
metavar="CECLI_IGNORE",
type=lambda path_str: resolve_cecli_ignore_path(path_str, git_root),
default=default_cecli_ignore_file,
help="Specify the cecli ignore file (default: .cecli.ignore in git root)",
).complete = shtab.FILE
group.add_argument(
"--subtree-only",
action="store_true",
help="Only consider files in the current subtree of the git repository",
default=False,
)
group.add_argument(
"--auto-commits",
action=argparse.BooleanOptionalAction,
default=True,
help="Enable/disable auto commit of LLM changes (default: True)",
)
group.add_argument(
"--dirty-commits",
action=argparse.BooleanOptionalAction,
default=True,
help="Enable/disable commits when repo is found dirty (default: True)",
)
group.add_argument(
"--attribute-author",
action=argparse.BooleanOptionalAction,
default=None,
help=(
"Attribute cecli code changes in the git author name (default: True). If explicitly set"
" to True, overrides --attribute-co-authored-by precedence."
),
)
group.add_argument(
"--attribute-committer",
action=argparse.BooleanOptionalAction,
default=None,
help=(
"Attribute cecli commits in the git committer name (default: True). If explicitly set"
" to True, overrides --attribute-co-authored-by precedence for cecli edits."
),
)
group.add_argument(
"--attribute-commit-message-author",
action=argparse.BooleanOptionalAction,
default=False,
help="Prefix commit messages with 'cecli: ' if cecli authored the changes (default: False)",
)
group.add_argument(
"--attribute-commit-message-committer",
action=argparse.BooleanOptionalAction,
default=False,
help="Prefix all commit messages with 'cecli: ' (default: False)",
)
group.add_argument(
"--attribute-co-authored-by",
action=argparse.BooleanOptionalAction,
default=True,
help=(
"Attribute cecli edits using the Co-authored-by trailer in the commit message"
" (default: True). If True, this takes precedence over default --attribute-author and"
" --attribute-committer behavior unless they are explicitly set to True."
),
)
group.add_argument(
"--git-commit-verify",
action=argparse.BooleanOptionalAction,
default=False,
help="Enable/disable git pre-commit hooks with --no-verify (default: False)",
)
group.add_argument(
"--commit",
action="store_true",
help="Commit all pending changes with a suitable commit message, then exit",
default=False,
)
group.add_argument(
"--commit-prompt",
metavar="PROMPT",
help="Specify a custom prompt for generating commit messages",
)
group.add_argument(
"--dry-run",
action=argparse.BooleanOptionalAction,
default=False,
help="Perform a dry run without modifying files (default: False)",
)
group.add_argument(
"--skip-sanity-check-repo",
action="store_true",
help="Skip the sanity check for the git repository (default: False)",
default=False,
)
group.add_argument(
"--watch-files",
action=argparse.BooleanOptionalAction,
default=False,
help="Enable/disable watching files for ai coding comments (default: False)",
)
group = parser.add_argument_group("Fixing and committing")
group.add_argument(
"--lint",
action="store_true",
help="Lint and fix provided files, or dirty files if none provided",
default=False,
)
group.add_argument(
"--lint-cmd",
action="append",
help=(
'Specify lint commands to run for different languages, eg: "python: flake8'
' --select=..." (can be used multiple times)'
),
default=[],
)
group.add_argument(
"--auto-lint",
action=argparse.BooleanOptionalAction,
default=True,
help="Enable/disable automatic linting after changes (default: True)",
)
group.add_argument(
"--test-cmd",
help="Specify command to run tests",
default=[],
)
group.add_argument(
"--auto-test",
action=argparse.BooleanOptionalAction,
default=False,
help="Enable/disable automatic testing after changes (default: False)",
)
group.add_argument(
"--test",
action="store_true",
help="Run tests, fix problems found and then exit",
default=False,
)
#########
group = parser.add_argument_group("Upgrading")
group.add_argument(
"--just-check-update",
action="store_true",
help="Check for updates and return status in the exit code",
default=False,
)
group.add_argument(
"--check-update",
action=argparse.BooleanOptionalAction,
help="Check for new cecli versions on launch",
default=True,
)
group.add_argument(
"--show-release-notes",
action=argparse.BooleanOptionalAction,
help="Show release notes on first run of new version (default: None, ask user)",
default=None,
)
group.add_argument(
"--upgrade",
"--update",
action="store_true",
help="Upgrade cecli to the latest version from PyPI",
default=False,
)
group.add_argument(
"--version",
action="version",
version=f"%(prog)s {__version__}",
help="Show the version number and exit",
)
##########
group = parser.add_argument_group("Modes")
group.add_argument(
"--message",
"--msg",
"-m",
metavar="COMMAND",
help=(
"Specify a single message to send the LLM, process reply then exit (disables chat mode)"
),
)
group.add_argument(
"--message-file",
"-f",
metavar="MESSAGE_FILE",
help=(
"Specify a file containing the message to send the LLM, process reply, then exit"
" (disables chat mode)"
),
).complete = shtab.FILE
group.add_argument(
"--copy-paste",
action=argparse.BooleanOptionalAction,
default=False,
help="Enable automatic copy/paste of chat between cecli and web UI (default: False)",
)
group.add_argument(
"--apply",
metavar="FILE",
help="Apply the changes from the given file instead of running the chat (debug)",
).complete = shtab.FILE
group.add_argument(
"--apply-clipboard-edits",
action="store_true",
help="Apply clipboard contents as edits using the main model's editor format",
default=False,
)
group.add_argument(
"--exit",
action="store_true",
help="Do all startup activities then exit before accepting user input (debug)",
default=False,
)
group.add_argument(
"--show-repo-map",
action="store_true",
help="Print the repo map and exit (debug)",
default=False,
)
group.add_argument(
"--show-prompts",
action="store_true",
help="Print the system prompts and exit (debug)",
default=False,
)
group.add_argument(
"--linear-output",
action=argparse.BooleanOptionalAction,
help="Run input and output sequentially instead of us simultaneous streams (default: None)",
default=linear_output_default,
)
group.add_argument(
"--debug",
action="store_true",
help="Turn on verbose debugging (default: False)",
default=False,
)
##########
group = parser.add_argument_group("Voice settings")
group.add_argument(
"--voice-format",
metavar="VOICE_FORMAT",
default="wav",
choices=["wav", "mp3", "webm"],
help="Audio format for voice recording (default: wav). webm and mp3 require ffmpeg",
)
group.add_argument(
"--voice-language",
metavar="VOICE_LANGUAGE",
default=None,
help=(
"Specify the language for voice using ISO 639-1 code "
"(default: resolve from the voice setting, then chat language)"
),
)
group.add_argument(
"--voice-input-device",
metavar="VOICE_INPUT_DEVICE",
default=None,
help="Specify the input device name for voice recording",
)
######
group = parser.add_argument_group("Other settings")
group.add_argument(
"--tweak-responses",
action="store_true",
help="Allow manual edits to model responses (default: False)",
default=False,
)
group.add_argument(
"--yes-always",
"--yes",
action="store_true",
help="Always say yes to every confirmation (not including cli commands)",
default=None,
)
group.add_argument(
"--yes-always-commands",
"--yolo",
action="store_true",
help="Always say yes to every confirmation (including cli commands)",
default=False,
)
group.add_argument(
"--disable-playwright",
action="store_true",
help="Never prompt for or attempt to install Playwright for web scraping (default: False).",
default=False,
)
group.add_argument(
"--disable-scraping",
action="store_true",
help="Disable automatic url scraping entirely web scraping (default: False).",
default=False,
)
group.add_argument(
"--file",
action="append",
metavar="FILE",
help="specify a file to edit (can be used multiple times, glob patterns supported)",
).complete = shtab.FILE
group.add_argument(
"--read",
action="append",
metavar="FILE",
help="specify a read-only file (can be used multiple times, glob patterns supported)",
).complete = shtab.FILE
group.add_argument(
"--rules",
action="append",
metavar="FILE",
help="specify a rules file (can be used multiple times, glob patterns supported)",
).complete = shtab.FILE
group.add_argument(
"--vim",
action="store_true",
help="Use VI editing mode in the terminal (default: False)",
default=False,
)
group.add_argument(
"--chat-language",
metavar="CHAT_LANGUAGE",
default=None,
help="Specify the language to use in the chat (default: None, uses system settings)",
)
group.add_argument(
"--commit-language",
metavar="COMMIT_LANGUAGE",
default=None,
help="Specify the language to use in the commit message (default: None, user language)",
)
group.add_argument(
"-v",
"--verbose",
action="store_true",
help="Enable verbose output",
default=False,
)
group.add_argument(
"--load",
metavar="LOAD_FILE",
help="Load and execute /commands from a file on launch",
).complete = shtab.FILE
group.add_argument(
"--encoding",
default="utf-8",
help="Specify the encoding for input and output (default: utf-8)",
)
group.add_argument(
"--line-endings",
choices=["platform", "lf", "crlf"],
default="platform",
help="Line endings to use when writing files (default: platform)",
)
group.add_argument(
"-c",
"--config",
is_config_file=True,
env_var="CECLI_CONFIG_FILE",
metavar="CONFIG_FILE",
help=(
"Specify the config file (default: search for .cecli.conf.yml in git root, cwd"
" or home directory)"
),
).complete = shtab.FILE
# This is a duplicate of the argument in the preparser and is a no-op by this time of
# argument parsing, but it's here so that the help is displayed as expected.
group.add_argument(
"--env-file",
metavar="ENV_FILE",
default=default_env_file(git_root),
help="Specify the .env file to load (default: .env in git root)",
).complete = shtab.FILE
group.add_argument(
"--suggest-shell-commands",
action=argparse.BooleanOptionalAction,
default=True,
help="Enable/disable suggesting shell commands (default: True)",
)
group.add_argument(
"--fancy-input",
action=argparse.BooleanOptionalAction,
default=True,
help="Enable/disable fancy input with history and completion (default: True)",
)
group.add_argument(
"--multiline",
action=argparse.BooleanOptionalAction,
default=False,
help="Enable/disable multi-line input mode with Meta-Enter to submit (default: False)",
)
group.add_argument(
"--notifications",
action=argparse.BooleanOptionalAction,
default=False,
help=(
"Enable/disable terminal bell notifications when LLM responses are ready (default:"
" False)"
),
)
group.add_argument(
"--notifications-command",
metavar="COMMAND",
default=None,
help=(
"Specify a command to run for notifications instead of the terminal bell. If not"
" specified, a default command for your OS may be used."
),
)
group.add_argument(
"--notification-bell",
action=argparse.BooleanOptionalAction,
default=False,
help=(
"Allow notification commands to produce an audible bell. When enabled, command"
" output is not suppressed so terminal bell escape sequences can ring through"
" (default: True)"
),
)
group.add_argument(
"--command-prefix",
default=None,
help="Specify a command prefix for all commands (useful for sandboxing)",
)
group.add_argument(
"--detect-urls",
action=argparse.BooleanOptionalAction,
default=True,
help="Enable/disable detection and offering to add URLs to chat (default: True)",
)
group.add_argument(
"--editor",
help="Specify which editor to use for the /editor command",
)
supported_shells_list = sorted(list(shtab.SUPPORTED_SHELLS))
group.add_argument(
"--shell-completions",
metavar="SHELL",
choices=supported_shells_list,
help=(
"Print shell completion script for the specified SHELL and exit. Supported shells:"
f" {', '.join(supported_shells_list)}. Example: cecli --shell-completions bash"
),
)
##########
group = parser.add_argument_group("Server settings")
group.add_argument(
"--server-config",
metavar="SERVER_CONFIG_JSON",
help="Specify server configuration (WebSocket and headless) as a JSON/YAML string",
default=None,
)
group.add_argument(
"--acp",
action="store_true",
default=False,
help=(
"Run as an Agent Client Protocol (ACP) agent over stdio, for editors such as Zed"
" (stdout carries the protocol)"
),
)
group.add_argument(
"--acp-models",
action="append",
metavar="PATTERN",
help=(
"Limit the ACP model picker's provider lists to models matching this pattern,"
" matched like /models (can be used multiple times)"
),
)
##########
group = parser.add_argument_group("Deprecated model settings")
# Add deprecated model shortcut arguments
add_deprecated_model_args(parser, group)
group = parser.add_argument_group("Deprecated agent settings")
add_deprecated_mcp_args(parser, group)
return parser
def get_md_help():
os.environ["COLUMNS"] = "70"
sys.argv = ["cecli"]
parser = get_parser([], None)
# This instantiates all the action.env_var values
parser.parse_known_args()
parser.formatter_class = MarkdownHelpFormatter
return argparse.ArgumentParser.format_help(parser)
def get_sample_yaml():
os.environ["COLUMNS"] = "100"
sys.argv = ["cecli"]
parser = get_parser([], None)
# This instantiates all the action.env_var values
parser.parse_known_args()
parser.formatter_class = YamlHelpFormatter
return argparse.ArgumentParser.format_help(parser)
def get_sample_dotenv():
os.environ["COLUMNS"] = "120"
sys.argv = ["cecli"]
parser = get_parser([], None)
# This instantiates all the action.env_var values
parser.parse_known_args()
parser.formatter_class = DotEnvFormatter
return argparse.ArgumentParser.format_help(parser)
def main():
if len(sys.argv) > 1:
command = sys.argv[1]
else:
command = "yaml" # Default to yaml if no command is given
if command == "md":
print(get_md_help())
elif command == "dotenv":
print(get_sample_dotenv())
elif command == "yaml":
print(get_sample_yaml())
elif command == "completion":
if len(sys.argv) > 2:
shell = sys.argv[2]
if shell not in shtab.SUPPORTED_SHELLS:
print(f"Error: Unsupported shell '{shell}'.", file=sys.stderr)
print(
f"Supported shells are: {', '.join(shtab.SUPPORTED_SHELLS)}",
file=sys.stderr,
)
sys.exit(1)
parser = get_parser([], None)
parser.prog = "cecli" # Set the program name on the parser
print(shtab.complete(parser, shell=shell))
else:
print("Error: Please specify a shell for completion.", file=sys.stderr)
print(f"Usage: python {sys.argv[0]} completion ", file=sys.stderr)
print(
f"Supported shells are: {', '.join(shtab.SUPPORTED_SHELLS)}",
file=sys.stderr,
)
sys.exit(1)
else:
# Default to YAML for any other unrecognized argument, or if 'yaml' was explicitly passed
print(get_sample_yaml())
if __name__ == "__main__":
status = main()
sys.exit(status)