Skip to content

Environment variables ​

Pythinker Code CLI uses environment variables to control a small number of runtime behaviors — relocating the data directory, turning off telemetry, and temporarily switching models without touching the config file.

Important: API keys are not configured here

Credential variables such as PYTHINKER_API_KEY, ANTHROPIC_API_KEY, and OPENAI_API_KEY are not read automatically from shell environment variables. Running export PYTHINKER_API_KEY=xxx in the terminal does not give any provider its key — they must be written in config.toml under [providers.<name>] or the [providers.<name>.env] sub-table.

The only exception is the PYTHINKER_MODEL_* family, which is an explicit channel that does read credentials from the shell — see Define a model from environment variables.

For background, see Config overrides: provider credentials.

Core paths ​

PYTHINKER_CODE_HOME ​

Overrides the data root directory; the default is ~/.pythinker-code. Once set, the config file, sessions, logs, OAuth credentials, and all other data land under the new path:

sh
export PYTHINKER_CODE_HOME="/path/to/custom/pythinker-code"

Make sure the directory is writable. Multiple pythinker instances sharing the same PYTHINKER_CODE_HOME will share config and credential files.

For the complete data directory structure, see Data locations.

PYTHINKER_DISABLE_TELEMETRY ​

Set to 1 to turn off anonymous telemetry reporting (also accepts true, yes, y, case-insensitive):

sh
export PYTHINKER_DISABLE_TELEMETRY=1

PYTHINKER_MODEL_* family ​

Switch models temporarily without modifying config.toml — when PYTHINKER_MODEL_NAME is set, the CLI synthesizes a temporary provider in memory; the change does not persist after restart. See Define a model from environment variables.

PYTHINKER_CODE_CUSTOM_HEADERS ​

Attaches custom HTTP headers to every outbound model request — both LLM chat requests (across all provider protocols) and /models listing requests. Useful when a gateway routes by header, for example to pin a specific cluster:

sh
export PYTHINKER_CODE_CUSTOM_HEADERS=$'X-Gateway-Cluster: my-cluster\nX-Custom-Tag: debug'

The format mirrors ANTHROPIC_CUSTOM_HEADERS: newline-separated Name: Value lines. Names and values are trimmed, and lines without a colon are ignored.

Added

Added in 0.20.2.

Precedence: the Pythinker identity headers (User-Agent, X-Msh-*) and a provider's custom_headers in config.toml (see Config files) override same-named entries here. Authentication is protocol-dependent: on the pythinker, openai, and openai_responses protocols an exact Authorization entry replaces the generated bearer token, while /models listing requests keep their own authentication. A case variant such as authorization is never treated as the same name — it is combined with the real header, which can break requests. Do not use this variable for authentication or other reserved headers. Use custom_headers when headers need to differ per provider.

Provider credential key names (written in config.toml) ​

The key names below are not read directly from the shell — they are key names written inside the [providers.<name>.env] sub-table of config.toml, serving as fallback values for api_key / base_url. The CLI reads only from the config file, not from process.env.

This design lets you keep familiar key name conventions while centralizing secret management in the config file:

toml
[providers.pythinker.env]
PYTHINKER_API_KEY = "sk-xxx"
PYTHINKER_BASE_URL = "https://api.moonshot.ai/v1"

Key names per provider:

KeyApplicable providerDefault
PYTHINKER_API_KEYPythinker / PyModelNone
PYTHINKER_BASE_URLPythinker / PyModelhttps://api.moonshot.ai/v1
ANTHROPIC_API_KEYAnthropicNone
ANTHROPIC_BASE_URLAnthropicFollows Anthropic SDK default
OPENAI_API_KEYOpenAI (openai and openai_responses)None
OPENAI_BASE_URLOpenAI (openai and openai_responses)https://api.openai.com/v1
GOOGLE_API_KEYGoogle GenAI, Vertex AINone
VERTEXAI_API_KEYVertex AINone
GOOGLE_CLOUD_PROJECTVertex AINone
GOOGLE_CLOUD_LOCATIONVertex AINone

WARNING

GOOGLE_APPLICATION_CREDENTIALS (path to a service account JSON file) is the only exception that goes through the system environment variable mechanism — it is read by the Google SDK directly via the standard ADC flow, and the CLI does not participate. All other key names must be placed in the [providers.<name>.env] sub-table to take effect.

For the full provider type and field reference, see Providers and models.

Define a model from environment variables (PYTHINKER_MODEL_*) ​

Want to switch models for testing without touching config.toml? When PYTHINKER_MODEL_NAME is set, the CLI synthesizes a temporary provider and model alias from the PYTHINKER_MODEL_* variables in memory — nothing is written back to the config file. These variables take priority over default_model in config.toml, but the -m <alias> option at startup still has the highest priority.

sh
export PYTHINKER_MODEL_NAME="test-model"
export PYTHINKER_MODEL_API_KEY="YOUR_API_KEY"
export PYTHINKER_MODEL_BASE_URL="https://api.example.com/v1"
export PYTHINKER_MODEL_MAX_CONTEXT_SIZE="262144"
export PYTHINKER_MODEL_CAPABILITIES="image_in,thinking"
pythinker

Complete variable list:

VariableRequiredPurposeDefault
PYTHINKER_MODEL_NAMEYes (also the enable switch)Model id sent to the API—
PYTHINKER_MODEL_API_KEYYesAPI key—
PYTHINKER_MODEL_PROVIDER_TYPENoProvider type: pythinker, anthropic, openaipythinker
PYTHINKER_MODEL_BASE_URLNoAPI base URLEach type has its own default
PYTHINKER_MODEL_MAX_CONTEXT_SIZENoMaximum context length (tokens)262144 (256 K)
PYTHINKER_MODEL_CAPABILITIESNoComma-separated capability tags, unioned with auto-detected capabilitiesimage_in,thinking
PYTHINKER_MODEL_DISPLAY_NAMENoName shown in /modelFalls back to PYTHINKER_MODEL_NAME
PYTHINKER_MODEL_MAX_OUTPUT_SIZENoPer-request output cap (anthropic only); when set, overrides the built-in Claude ceilingModel default
PYTHINKER_MODEL_REASONING_KEYNoReasoning field name override (openai only)Auto-detected
PYTHINKER_MODEL_THINKING_EFFORTNoThinking effort level: low/medium/high/xhigh/max—
PYTHINKER_MODEL_ADAPTIVE_THINKINGNoForce adaptive thinking on or off (anthropic only)Inferred from model name

If PYTHINKER_MODEL_NAME is set but a required variable is missing, startup fails immediately with a clear error message.

Runtime switches ​

Switches that control the behavior of subsystems such as telemetry, background tasks, and the plugin marketplace:

VariablePurposeValid values
PYTHINKER_DISABLE_TELEMETRYDisable anonymous telemetry reporting1, true, yes, y (case-insensitive)
PYTHINKER_CODE_PASSWORDSet a parallel auth credential for the pythinker web local server, valid alongside the bearer token; recommended when binding the server beyond loopback — see Local server and APIAny non-empty string; when unset, only the token is valid
PYTHINKER_CODE_BACKGROUND_KEEP_ALIVE_ON_EXITWhether to keep background tasks when the session closes; takes higher priority than config.toml. The default is to stop them on exitTruthy: 1/true/yes/on; falsy: 0/false/no/off
PYTHINKER_CODE_BACKGROUND_MAX_RUNNING_TASKSCap on concurrently running background tasks; takes higher priority than [background] max_running_tasks in config.toml (unset means no cap)Positive integer; invalid values are ignored
PYTHINKER_CODE_BACKGROUND_BASH_TASK_TIMEOUT_SDefault timeout (seconds) for background Bash tasks, also used to re-arm foreground commands moved to the background; takes higher priority than [task] bash_task_timeout_s (0 means no timeout)Non-negative integer; invalid values are ignored
PYTHINKER_CODE_BACKGROUND_PRINT_BACKGROUND_MODEWhat pythinker -p does while background tasks are still pending after the main turn; takes higher priority than [task] print_background_modeexit, drain, or steer; invalid values are ignored
PYTHINKER_CODE_BACKGROUND_PRINT_WAIT_CEILING_SWall-clock ceiling (seconds) for the print-mode drain/steer wait; takes higher priority than [task] print_wait_ceiling_sPositive integer; invalid values are ignored
PYTHINKER_CODE_BACKGROUND_PRINT_MAX_TURNSMaximum number of new turns triggered by background-task completions in print mode; takes higher priority than [task] print_max_turnsPositive integer; invalid values are ignored
PYTHINKER_IMAGE_MAX_EDGE_PXLongest-edge ceiling (px) for image compression; takes higher priority than [image] max_edge_px in config.toml (default 2000)Positive integer; invalid values are ignored
PYTHINKER_IMAGE_READ_BYTE_BUDGETPer-image byte budget for model-initiated image reads (ReadMediaFile default reads); takes higher priority than [image] read_byte_budget in config.toml (default 262144, i.e. 256 KB)Positive integer; invalid values are ignored
PYTHINKER_CODE_PLUGIN_MARKETPLACE_URLOverride the plugin marketplace JSON loaded by /plugins; useful for dev loopback servers, staging CDN files, or alternate marketplace directoriesUnset (no default catalog; unset means only built-in entries are shown); accepts http://, file:// URLs, and local paths
PYTHINKER_CODE_AGENT_DYNAMIC_WORKFLOW_MAX_CONCURRENCYCap how many AgentDynamicWorkflow subagents run concurrently during the initial ramp; takes higher priority than [dynamic_workflow] max_concurrency in config.toml (unset means no cap)Positive integer; invalid values fail fast
PYTHINKER_CODE_AGENT_DYNAMIC_WORKFLOW_TIMEOUT_MSMaximum wall-clock time (ms) for one AgentDynamicWorkflow subagent; takes higher priority than [dynamic_workflow] timeout_ms in config.toml (default 7200000, or 2 hours)Non-negative integer (0 means no timeout); invalid values fall back to the config or default
PYTHINKER_SUBAGENT_TIMEOUT_MSMaximum wall-clock time (ms) a single Agent subagent may run, and the same limit for tower workers and reviewers; takes higher priority than [subagent] timeout_ms in config.toml (default 7200000, i.e. 2 hours)Non-negative integer (0 means no timeout); invalid values fall back to the config or default
PYTHINKER_CODE_IDENTITY_NAMEDisplay name the agent calls itself in the system prompt; takes higher priority than [identity] name in config.toml and is never written back to itAny non-empty string; blank values read as unset
PYTHINKER_CODE_IDENTITY_SLUGProtocol identifier for the User-Agent product token sent to third-party providers and the MCP client name; takes higher priority than [identity] slug. Derived from the name when unsetAny non-empty string; normalized to lowercase with non-alphanumeric runs folded to -
PYTHINKER_CODE_BUILTIN_PRODUCT_SKILLSWhether the built-in skills documenting Pythinker Code itself are offered to the model; takes higher priority than builtin_product_skills in config.toml (default enabled)Truthy: 1/true/yes/on; falsy: 0/false/no/off
PYTHINKER_CODE_REPEAT_BREAKERWhether repeating the same tool call many times in a row injects reminders and eventually force-stops the turn. Unset keeps this onTruthy: 1/true/yes/on; falsy: 0/false/no/off; any other value is ignored
PYTHINKER_CODE_TUI_FULL_SCREENControl the fullscreen TUI with a fixed prompt dock, scrollable transcript, mouse text selection, clickable links, transcript search, and a clickable jump-to-bottom control. Fullscreen is enabled by default0 restores the legacy inline UI; unset or any other value keeps fullscreen enabled
PYTHINKER_CODE_DANGEROUS_COMMAND_GUARDOverride [permission].dangerous_command_guard. The guard asks before dangerous or unanalyzable Bash commands in interactive modes and blocks them in Auto modetrue or false; default true
PYTHINKER_CODE_PERMISSION_MODE_REMINDERStop injecting the automatic permission-mode reminder (the context note that explains Auto mode) into the model context; set to a false value or an empty value to disableTruthy keeps the reminder enabled; falsy: 0/false/no/off disables it; an empty value disables it
PYTHINKER_CODE_EXPERIMENTAL_SUBAGENT_FORKEnable the experimental fork parameter on the Agent and AgentDynamicWorkflow tools, letting the model start a subagent with a snapshot of the calling agent's conversation history instead of an empty context; the master PYTHINKER_CODE_EXPERIMENTAL_FLAG=1 also enables itTruthy: 1/true/yes/on; falsy: 0/false/no/off
PYTHINKER_CODE_EXPERIMENTAL_TOOL_SELECTExperimental on-demand tool loading: tools of MCP servers marked deferred: true stay out of the top-level tool list and are loaded via select_tools; also requires the model to declare the dynamically_loaded_tools capability — see MCPTruthy: 1/true/yes/on; falsy: 0/false/no/off
PYTHINKER_CODE_EXPERIMENTAL_TOWEREnable the experimental /tower command for workspace-wide subagent coordination; the master PYTHINKER_CODE_EXPERIMENTAL_FLAG=1 also enables itTruthy: 1/true/yes/on; falsy: 0/false/no/off
PYTHINKER_CODE_WATCHAttach filesystem watchers that reload config and workspace files; higher priority than [watch] enabled (default false)Truthy: 1/true/yes/on; falsy: 0/false/no/off
PYTHINKER_CODE_SEARCH_WORKERRun the global search index in a dedicated worker thread; takes higher priority than [database] search in config.toml (default true)Truthy: 1/true/yes/on; falsy: 0/false/no/off
PYTHINKER_CODE_PERSISTENCE_MINIDB_READMODELUse the minidb-backed read model for session indexing; takes higher priority than [database] base in config.toml (default true)Truthy: 1/true/yes/on; falsy: 0/false/no/off
PYTHINKER_MCP_STARTUP_TIMEOUT_MSGlobal default connection timeout (ms) for all MCP servers; takes higher priority than [mcp] startup_timeout_ms in config.toml, but a per-server startupTimeoutMs in mcp.json still wins (default 30000)Integer from 1 to 2147483647; invalid values are ignored
PYTHINKER_MCP_TOOL_TIMEOUT_MSGlobal default single tool-call timeout (ms) for all MCP servers; takes higher priority than [mcp] tool_timeout_ms in config.toml, but a per-server toolTimeoutMs in mcp.json still wins (default 60000)Integer from 1 to 2147483647; invalid values are ignored
PYTHINKER_LOOP_MAX_STEPS_PER_TURNMaximum Agent steps per turn; takes higher priority than [loop_control] max_steps_per_turn in config.toml (unset or 0 means unlimited)Non-negative integer; invalid values are ignored
PYTHINKER_LOOP_MAX_ATTEMPTS_PER_STEPMaximum total attempts for a failing step (including the initial attempt); takes higher priority than [loop_control] max_attempts_per_step in config.toml (default 10). The deprecated PYTHINKER_LOOP_MAX_RETRIES_PER_STEP is still honored with a warning when this variable is unsetNon-negative integer; invalid values are ignored
PYTHINKER_CODE_INFINITE_RETRYRetry every failed LLM request indefinitely — turn steps and background operations such as compaction alike — instead of failing the task; waits use exponential backoff (capped at 32 s) and honor the server's Retry-After header, and aborting still cancels immediately. Intended for long-running unattended evaluations against endpoints that may fail temporarilyTruthy: 1/true/yes/on; falsy: 0/false/no/off
PYTHINKER_TOKEN_COUNTING_STRATEGYWhich context token count is reported externally (the context-size display); takes higher priority than [token_counting] strategy in config.toml (default measured+estimated)measured+estimated, measured, estimated (case-insensitive); invalid values are ignored
PYTHINKER_WEB_SEARCH_BASE_URLAPI URL of the web search (WebSearch) service; takes higher priority than [services.pymodel_search] base_url in config.toml, and enables the service without that config section. Persisted credentials and custom headers are not forwarded to an env-selected endpointNon-blank string; blank values are ignored
PYTHINKER_WEB_SEARCH_API_KEYAPI key of the web search (WebSearch) service; replaces both the configured API key and OAuth credential when setNon-blank string; blank values are ignored
PYTHINKER_WEB_FETCH_BASE_URLAPI URL of the web fetch (FetchURL) service; takes higher priority than [services.pymodel_fetch] base_url. Persisted credentials and custom headers are not forwarded to an env-selected endpointNon-blank string; blank values are ignored
PYTHINKER_WEB_FETCH_API_KEYAPI key of the web fetch (FetchURL) service; replaces both the configured API key and OAuth credential when setNon-blank string; blank values are ignored
PYTHINKER_CODE_EXPERIMENTAL_FLAGEnable all registered experimental features for this process; a per-feature PYTHINKER_CODE_EXPERIMENTAL_<NAME> variable or an explicit entry in the [experimental] section of config.toml takes precedence over it; it does not select the agent engine1, true, yes, on
PYTHINKER_CODE_LEGACY_FLAGUse the legacy agent-core engine for pythinker, pythinker -p, pythinker export, and pythinker provider; these commands use agent-core-v2 by default1, true, yes, on
PYTHINKER_SHELL_PATHOverride the Git Bash path on Windows (used when auto-detection fails)Absolute path
PYTHINKER_MODEL_MAX_COMPLETION_TOKENSHard cap on max_completion_tokens per LLM step; applies to the pythinker provider onlyPositive integer; 0 or negative disables clamping
PYTHINKER_MODEL_TEMPERATURESampling temperature for every request; applies to the pythinker provider only (global — independent of PYTHINKER_MODEL_NAME)Number, e.g. 0.3
PYTHINKER_MODEL_TOP_PNucleus-sampling top_p for every request; applies to the pythinker provider only (global)Number, e.g. 0.95
PYTHINKER_MODEL_THINKING_EFFORTForce a specific thinking effort on the wire (thinking.effort), bypassing the model's declared support_efforts; applies to the pythinker provider only, and only while Thinking is onAn effort value, e.g. max
PYTHINKER_MODEL_THINKING_KEEPPreserved-thinking passthrough; on pythinker sent as thinking.keep, on anthropic (Claude and Pythinker's Anthropic-compatible mode) sent as a context_management clear_thinking_20251015 edit (enabling keep routes Anthropic requests to the beta Messages API); overrides [thinking] keep (which defaults to "all"); only injected while Thinking is onA value the API accepts, e.g. all; an off-value (false/0/no/off/none/null) disables it
PYTHINKER_CODE_NO_AUTO_UPDATEFully disable the update preflight — no check, background install, or prompt. Legacy alias PYTHINKER_CLI_NO_AUTO_UPDATE is also honoredTruthy: 1/true/yes/on
PYTHINKER_DISABLE_CRONDisable the scheduled-task tool (CronCreate rejects new schedules; existing tasks do not fire)1 to disable

The PYTHINKER_CODE_INFINITE_RETRY, PYTHINKER_CODE_IDENTITY_*, and PYTHINKER_CODE_BUILTIN_PRODUCT_SKILLS variables are read by the default agent-core-v2 engine. The legacy pythinker / pythinker -p path selected with PYTHINKER_CODE_LEGACY_FLAG=1 ignores them.

Diagnostic logs ​

These variables control log level and file rotation, read once at process startup:

VariablePurposeDefault
PYTHINKER_LOG_LEVELLog level: off, error, warn, info, debuginfo
PYTHINKER_LOG_GLOBAL_MAX_BYTESMaximum bytes per global log file6291456 (6 MB)
PYTHINKER_LOG_GLOBAL_FILESNumber of global log files to retain5
PYTHINKER_LOG_SESSION_MAX_BYTESMaximum bytes per session log file5242880 (5 MB)
PYTHINKER_LOG_SESSION_FILESNumber of session log files to retain3

System environment variables ​

The CLI also reads several standard system variables to detect the runtime environment; it does not modify them:

  • HOME: used to resolve the default data path
  • VISUAL, EDITOR: external editor command (VISUAL takes precedence)
  • PATH: used to locate dependencies such as rg, fd, fdfind, and git; on Windows, Git Bash detection checks each git.exe found on PATH, including package-manager shims such as Scoop
  • NO_COLOR, FORCE_COLOR: control color output (following the no-color.org convention)
  • CI: when non-empty and not "0", disables theme detection and falls back to the dark theme
  • TERM_PROGRAM, TERM, TMUX: detect terminal features and notification support
  • DISPLAY, WAYLAND_DISPLAY, XDG_SESSION_TYPE: detect Linux graphical sessions (for clipboard and image features)
  • WSL_DISTRO_NAME, WSLENV: detect WSL for the clipboard PowerShell bridge
  • LOCALAPPDATA: used on Windows as a fallback when probing for the Git Bash installation path

HTTP proxy ​

Pythinker Code honors the standard proxy environment variables for all outbound traffic — model API calls, MCP servers, web tools, telemetry, sign-in, and update checks:

  • HTTP_PROXY / http_proxy: proxy for http:// requests
  • HTTPS_PROXY / https_proxy: proxy for https:// requests
  • ALL_PROXY / all_proxy: fallback proxy used when the scheme-specific variable is unset; this is where a SOCKS proxy is usually set
  • NO_PROXY / no_proxy: comma-separated hosts that bypass the proxy

Both HTTP(S) and SOCKS proxies are supported. A SOCKS proxy is recognized by its scheme — socks5://, socks5h://, socks4://, or socks:// (an alias for socks5://) — and is typically set via ALL_PROXY (the form used by tools like Clash and V2RayN). An HTTP(S) proxy takes precedence over ALL_PROXY for HTTP/HTTPS traffic.

The proxy is applied only when one of these variables is set; otherwise connections are made directly. Loopback hosts (localhost, 127.0.0.1, ::1) always bypass the proxy, so a local server such as a localhost MCP server keeps working when a proxy is configured — add your own internal hosts to NO_PROXY to exempt them too.

Stdio MCP servers that run as Node child processes honor HTTP_PROXY / HTTPS_PROXY / NO_PROXY automatically when the child's Node version supports NODE_USE_ENV_PROXY (Node ≥ 22.21 or ≥ 24.5); SOCKS proxying applies to Pythinker Code's own traffic only.

Next steps ​