@oh-my-pi/pi-coding-agent 11.0.2 → 11.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,40 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [11.0.3] - 2026-02-05
6
+ ### Added
7
+
8
+ - Added new subcommands to help text: `commit` for AI-assisted git commits, `stats` for AI usage statistics dashboard, and `jupyter` for managing the shared Jupyter gateway
9
+ - Added `grep` subcommand to help text for testing the grep tool
10
+ - Added `browser` tool documentation for browser automation using Puppeteer
11
+ - Added `todo_write` tool documentation for managing todo and task lists
12
+ - Added documentation for additional LLM provider API keys (Groq, Cerebras, xAI, OpenRouter, Mistral, z.ai, MiniMax, OpenCode, Cursor, Vercel AI Gateway) in environment variables reference
13
+ - Added documentation for cloud provider configuration (AWS Bedrock, Google Vertex AI) in environment variables reference
14
+ - Added documentation for search provider API keys (Perplexity, Anthropic Search) in environment variables reference
15
+ - Added documentation for model override environment variables (`PI_SMOL_MODEL`, `PI_SLOW_MODEL`, `PI_PLAN_MODEL`) in CLI help text
16
+ - Added comprehensive environment variables reference documentation at `docs/environment-variables.md` covering API keys, configuration, debugging, and testing variables
17
+ - Added theme system with 44 customizable color tokens, two built-in themes (dark/light), and auto-detection based on terminal background
18
+ - Added `/theme` command to interactively select and switch between themes
19
+ - Added support for custom themes in `~/.pi/agent/themes/*.json` with live editing - changes apply immediately when files are saved
20
+ - Added `userMessageText` theme token for customizing user message text color
21
+ - Added `toolTitle` and `toolOutput` theme tokens for separate coloring of tool execution box titles and output
22
+
23
+ ### Changed
24
+
25
+ - Updated help text to reflect expanded tool availability - default now enables all tools instead of just read, bash, edit, write
26
+ - Updated available tools list in help documentation to include python, notebook, task, fetch, web_search, browser, and ask
27
+ - Simplified main description in help text from 'AI coding assistant with read, bash, edit, write tools' to 'AI coding assistant'
28
+ - Updated `--tools` option documentation to clarify default behavior and list all available tools
29
+ - Changed all environment variable access from `process.env` to `Bun.env` throughout the codebase for Bun runtime compatibility
30
+ - Updated documentation to reference `Bun.env` instead of `process.env` in examples and comments
31
+
32
+ ### Fixed
33
+
34
+ - Fixed `Text` component to properly implement `invalidate()` method, ensuring theme changes apply correctly to all UI elements
35
+ - Fixed `TruncatedText` component to properly pad all lines to exactly match the specified width, preventing rendering artifacts
36
+ - Fixed `TruncatedText` component to stop at the first newline and only display the first line
37
+ - Fixed invalid or malformed themes to fall back gracefully to dark theme instead of crashing the application
38
+
5
39
  ## [11.0.2] - 2026-02-05
6
40
 
7
41
  ### Fixed
package/README.md CHANGED
@@ -368,7 +368,7 @@ When disabled, neither case triggers automatic compaction (use `/compact` manual
368
368
  **Environment Variables (`env`):**
369
369
 
370
370
  - Automatically sets environment variables when the application starts
371
- - Only sets variables that aren't already present in `process.env`
371
+ - Only sets variables that aren't already present in `Bun.env`
372
372
  - Supports any environment variable, not just API keys
373
373
  - Order of precedence: existing env vars > settings.json env vars > auth.json env vars
374
374
 
@@ -0,0 +1,257 @@
1
+ # Environment Variables Reference
2
+
3
+ This document lists all environment variables used by the coding agent.
4
+
5
+ ## API Keys
6
+
7
+ ### Multi-Provider Support
8
+
9
+ | Variable | Description | Default |
10
+ |----------|-------------|---------|
11
+ | `OPENAI_API_KEY` | OpenAI API key for GPT models | - |
12
+ | `ANTHROPIC_API_KEY` | Anthropic API key for Claude models | - |
13
+ | `ANTHROPIC_OAUTH_TOKEN` | Anthropic OAuth token (takes precedence over `ANTHROPIC_API_KEY`) | - |
14
+ | `GOOGLE_API_KEY` | Google Gemini API key | - |
15
+ | `COPILOT_GITHUB_TOKEN` | GitHub Copilot personal access token | `$GH_TOKEN` or `$GITHUB_TOKEN` |
16
+ | `GH_TOKEN` | GitHub CLI token (fallback for Copilot) | - |
17
+ | `GITHUB_TOKEN` | GitHub token (fallback for Copilot and API access) | - |
18
+ | `EXA_API_KEY` | Exa search API key | - |
19
+ | `GROQ_API_KEY` | Groq API key for Llama and other models | - |
20
+ | `CEREBRAS_API_KEY` | Cerebras API key | - |
21
+ | `XAI_API_KEY` | xAI API key for Grok models | - |
22
+ | `OPENROUTER_API_KEY` | OpenRouter aggregated models API key | - |
23
+ | `MISTRAL_API_KEY` | Mistral AI API key | - |
24
+ | `ZAI_API_KEY` | z.ai API key (ZhipuAI/GLM models) | - |
25
+ | `MINIMAX_API_KEY` | MiniMax API key | - |
26
+ | `OPENCODE_API_KEY` | OpenCode API key | - |
27
+ | `CURSOR_ACCESS_TOKEN` | Cursor AI access token | - |
28
+ | `AI_GATEWAY_API_KEY` | Vercel AI Gateway API key | - |
29
+ | `PERPLEXITY_API_KEY` | Perplexity search API key | - |
30
+
31
+ ### Provider-Specific Configuration
32
+
33
+ #### AWS Bedrock
34
+
35
+ | Variable | Description | Default |
36
+ |----------|-------------|---------|
37
+ | `AWS_REGION` | AWS region for Bedrock | `$AWS_DEFAULT_REGION` or `us-east-1` |
38
+ | `AWS_DEFAULT_REGION` | AWS default region | - |
39
+ | `AWS_PROFILE` | AWS CLI profile name | - |
40
+ | `AWS_ACCESS_KEY_ID` | AWS access key ID | - |
41
+ | `AWS_SECRET_ACCESS_KEY` | AWS secret access key | - |
42
+ | `AWS_BEARER_TOKEN_BEDROCK` | AWS bearer token for Bedrock | - |
43
+ | `AWS_CONTAINER_CREDENTIALS_RELATIVE_URI` | ECS container credentials URI | - |
44
+ | `AWS_CONTAINER_CREDENTIALS_FULL_URI` | ECS container credentials full URI | - |
45
+ | `AWS_WEB_IDENTITY_TOKEN_FILE` | Web identity token file path | - |
46
+ | `AWS_ROLE_ARN` | IAM role ARN for web identity | - |
47
+
48
+ #### Azure OpenAI
49
+
50
+ | Variable | Description | Default |
51
+ |----------|-------------|---------|
52
+ | `AZURE_OPENAI_API_KEY` | Azure OpenAI API key (required) | - |
53
+ | `AZURE_OPENAI_API_VERSION` | Azure OpenAI API version | `2024-10-01-preview` |
54
+ | `AZURE_OPENAI_BASE_URL` | Azure OpenAI base URL | Constructed from resource name |
55
+ | `AZURE_OPENAI_RESOURCE_NAME` | Azure OpenAI resource name | - |
56
+ | `AZURE_OPENAI_DEPLOYMENT_NAME_MAP` | JSON map of model IDs to deployment names | `{}` |
57
+
58
+ #### Google Cloud (Vertex AI)
59
+
60
+ | Variable | Description | Default |
61
+ |----------|-------------|---------|
62
+ | `GOOGLE_CLOUD_PROJECT` | Google Cloud project ID (required for Vertex AI) | `$GCLOUD_PROJECT` |
63
+ | `GCLOUD_PROJECT` | Google Cloud project ID (alternative) | - |
64
+ | `GOOGLE_CLOUD_PROJECT_ID` | Google Cloud project ID (used during OAuth discovery) | `$GOOGLE_CLOUD_PROJECT` |
65
+ | `GOOGLE_CLOUD_LOCATION` | Google Cloud location (required for Vertex AI) | - |
66
+ | `GOOGLE_APPLICATION_CREDENTIALS` | Path to Google service account JSON | - |
67
+
68
+ #### Anthropic Search & Custom Base URLs
69
+
70
+ | Variable | Description | Default |
71
+ |----------|-------------|---------|
72
+ | `ANTHROPIC_BASE_URL` | Custom Anthropic API base URL | - |
73
+ | `ANTHROPIC_SEARCH_API_KEY` | API key for Anthropic search (separate from main API) | - |
74
+ | `ANTHROPIC_SEARCH_BASE_URL` | Custom base URL for Anthropic search | - |
75
+ | `ANTHROPIC_SEARCH_MODEL` | Model to use for web search | `claude-sonnet-4` |
76
+
77
+ #### Kimi
78
+
79
+ | Variable | Description | Default |
80
+ |----------|-------------|---------|
81
+ | `KIMI_CODE_BASE_URL` | Kimi Code API base URL | `https://kimi.moonshot.cn` |
82
+ | `KIMI_CODE_OAUTH_HOST` | Kimi Code OAuth host | `$KIMI_OAUTH_HOST` or `https://kimi.moonshot.cn` |
83
+ | `KIMI_OAUTH_HOST` | Kimi OAuth host (fallback) | - |
84
+
85
+ ## Model Configuration
86
+
87
+ ### Model Role Overrides
88
+
89
+ Override model roles via environment variables (ephemeral, not persisted):
90
+
91
+ | Variable | Description | CLI Flag |
92
+ |----------|-------------|----------|
93
+ | `PI_SMOL_MODEL` | Fast model for lightweight tasks | `--smol` |
94
+ | `PI_SLOW_MODEL` | Reasoning model for thorough analysis | `--slow` |
95
+ | `PI_PLAN_MODEL` | Model for architectural planning | `--plan` |
96
+
97
+ ## Agent Configuration
98
+
99
+ ### Core Settings
100
+
101
+ | Variable | Description | Default |
102
+ |----------|-------------|---------|
103
+ | `PI_CODING_AGENT_DIR` | Directory for agent data (sessions, auth, cache) | `~/.omp/agent` |
104
+ | `PI_SUBPROCESS_CMD` | Custom command for spawning subagents | Auto-detected |
105
+ | `PI_NO_TITLE` | Disable automatic session title generation | `false` |
106
+ | `NULL_PROMPT` | Use empty system prompt (testing) | `false` |
107
+ | `PI_BLOCKED_AGENT` | Override agent type in task tool | - |
108
+
109
+ ### Caching
110
+
111
+ | Variable | Description | Default |
112
+ |----------|-------------|---------|
113
+ | `PI_CACHE_RETENTION` | Prompt cache retention (`long` = 24h for OpenAI) | - |
114
+
115
+ ## Python Configuration
116
+
117
+ ### Python Kernel
118
+
119
+ | Variable | Description | Default |
120
+ |----------|-------------|---------|
121
+ | `VIRTUAL_ENV` | Python virtual environment path | Auto-detected (`.venv` or `venv`) |
122
+ | `PI_PY` | Python tool mode (`per-session`, `per-call`, `off`) | `per-session` |
123
+ | `PI_PYTHON_SKIP_CHECK` | Skip Python availability check (testing) | `false` |
124
+
125
+ ### External Python Gateway
126
+
127
+ | Variable | Description | Default |
128
+ |----------|-------------|---------|
129
+ | `PI_PYTHON_GATEWAY_URL` | External Python gateway URL | - |
130
+ | `PI_PYTHON_GATEWAY_TOKEN` | Authentication token for gateway | - |
131
+
132
+ ### Debugging
133
+
134
+ | Variable | Description | Default |
135
+ |----------|-------------|---------|
136
+ | `PI_PYTHON_IPC_TRACE` | Trace Python IPC messages (`1` = enabled) | `false` |
137
+
138
+ ## Task & Subagent Configuration
139
+
140
+ | Variable | Description | Default |
141
+ |----------|-------------|---------|
142
+ | `PI_TASK_MAX_OUTPUT_BYTES` | Maximum output bytes per subagent | `500000` |
143
+ | `PI_TASK_MAX_OUTPUT_LINES` | Maximum output lines per subagent | `5000` |
144
+
145
+ ## TUI & Terminal Configuration
146
+
147
+ ### Terminal Capabilities
148
+
149
+ | Variable | Description | Auto-Detected |
150
+ |----------|-------------|---------------|
151
+ | `COLORTERM` | Terminal color support (`truecolor`, `24bit`) | Yes |
152
+ | `COLORFGBG` | Terminal foreground/background colors | Yes |
153
+ | `TERM` | Terminal type | Yes |
154
+ | `TERM_PROGRAM` | Terminal program name | Yes |
155
+ | `TERM_PROGRAM_VERSION` | Terminal program version | Yes |
156
+ | `TERMINAL_EMULATOR` | Terminal emulator name | Yes |
157
+ | `WT_SESSION` | Windows Terminal session ID | Yes |
158
+
159
+ ### TUI Behavior
160
+
161
+ | Variable | Description | Default |
162
+ |----------|-------------|---------|
163
+ | `PI_NOTIFICATIONS` | Desktop notifications (`off`, `0`, `false` = disabled) | Enabled |
164
+ | `PI_TUI_WRITE_LOG` | Log all TUI write operations to file | - |
165
+ | `PI_HARDWARE_CURSOR` | Show hardware cursor (`1` = enabled) | `false` |
166
+
167
+ ## Bash & Shell Configuration
168
+
169
+ ### Shell Detection
170
+
171
+ | Variable | Description | Auto-Detected |
172
+ |----------|-------------|---------------|
173
+ | `SHELL` | User's default shell | Yes (Unix) |
174
+ | `ComSpec` | Command processor | Yes (Windows) |
175
+
176
+ ### Editor
177
+
178
+ | Variable | Description | Fallback |
179
+ |----------|-------------|----------|
180
+ | `VISUAL` | Visual editor for external editing (Ctrl+G) | `$EDITOR` |
181
+ | `EDITOR` | Default text editor | - |
182
+
183
+ ### Bash Tool Behavior
184
+
185
+ | Variable | Description | Default |
186
+ |----------|-------------|---------|
187
+ | `PI_BASH_NO_CI` | Don't set `CI=true` in bash environment | `$CLAUDE_BASH_NO_CI` |
188
+ | `CLAUDE_BASH_NO_CI` | Legacy name for `PI_BASH_NO_CI` | - |
189
+ | `PI_BASH_NO_LOGIN` | Don't use login shell for bash | `$CLAUDE_BASH_NO_LOGIN` |
190
+ | `CLAUDE_BASH_NO_LOGIN` | Legacy name for `PI_BASH_NO_LOGIN` | - |
191
+ | `PI_SHELL_PREFIX` | Prefix for bash commands | `$CLAUDE_CODE_SHELL_PREFIX` |
192
+ | `CLAUDE_CODE_SHELL_PREFIX` | Legacy name for `PI_SHELL_PREFIX` | - |
193
+
194
+ ## Desktop Environment Detection
195
+
196
+ These are auto-detected for system prompt context:
197
+
198
+ | Variable | Purpose |
199
+ |----------|---------|
200
+ | `KDE_FULL_SESSION` | Detect KDE desktop |
201
+ | `XDG_CURRENT_DESKTOP` | Current desktop environment |
202
+ | `DESKTOP_SESSION` | Desktop session name |
203
+ | `XDG_SESSION_DESKTOP` | XDG desktop session |
204
+ | `GDMSESSION` | GDM session type |
205
+ | `WINDOWMANAGER` | Window manager name |
206
+ | `XDG_CONFIG_HOME` | User config directory |
207
+ | `APPDATA` | Windows app data directory |
208
+ | `HOME` | User home directory |
209
+
210
+ ## LSP Configuration
211
+
212
+ | Variable | Description | Default |
213
+ |----------|-------------|---------|
214
+ | `PI_DISABLE_LSPMUX` | Disable lspmux integration (`1` = disabled) | `false` |
215
+
216
+ ## Debugging & Development
217
+
218
+ ### General Debugging
219
+
220
+ | Variable | Description | Default |
221
+ |----------|-------------|---------|
222
+ | `DEBUG` | Enable debug logging | `false` |
223
+ | `PI_DEV` | Development mode (verbose native addon loading) | `false` |
224
+ | `PI_TIMING` | Log tool factory and operation timings (`1` = enabled) | `false` |
225
+
226
+ ### Startup Debugging
227
+
228
+ | Variable | Description | Default |
229
+ |----------|-------------|---------|
230
+ | `PI_DEBUG_STARTUP` | Print startup stage timings to stderr | `false` |
231
+
232
+ ### Provider-Specific Debugging
233
+
234
+ | Variable | Description | Default |
235
+ |----------|-------------|---------|
236
+ | `DEBUG_CURSOR` | Cursor provider debug logging (`1` = basic, `2` or `verbose` = detailed) | `false` |
237
+ | `DEBUG_CURSOR_LOG` | Path to write Cursor debug log file | - |
238
+ | `PI_CODEX_DEBUG` | OpenAI Codex debug logging (`1` or `true` = enabled) | `false` |
239
+
240
+ ## Commit Message Generation
241
+
242
+ | Variable | Description | Default |
243
+ |----------|-------------|---------|
244
+ | `PI_COMMIT_TEST_FALLBACK` | Force fallback commit generation (testing) | `false` |
245
+ | `PI_COMMIT_NO_FALLBACK` | Disable fallback commit generation | `false` |
246
+ | `PI_COMMIT_MAP_REDUCE` | Enable/disable map-reduce for large diffs (`false` = disabled) | Enabled |
247
+
248
+ ## Testing & CI
249
+
250
+ These are auto-detected but documented for completeness:
251
+
252
+ | Variable | Description |
253
+ |----------|-------------|
254
+ | `BUN_ENV` | Bun environment (`test` skips certain checks) |
255
+ | `NODE_ENV` | Node environment (`test` skips certain checks) |
256
+ | `E2E` | Enable end-to-end tests (`1` or `true`) |
257
+ | `PI_NO_LOCAL_LLM` | Skip local LLM tests (Ollama, LM Studio) |
package/docs/sdk.md CHANGED
@@ -87,7 +87,7 @@ interface AgentSession {
87
87
  prompt(text: string, options?: PromptOptions): Promise<void>;
88
88
  sendUserMessage(
89
89
  content: string | (TextContent | ImageContent)[],
90
- options?: { deliverAs?: "steer" | "followUp" },
90
+ options?: { deliverAs?: "steer" | "followUp" }
91
91
  ): Promise<void>;
92
92
  steer(text: string): void;
93
93
  followUp(text: string): void;
@@ -106,7 +106,7 @@ interface AgentSession {
106
106
  setThinkingLevel(level: ThinkingLevel): void;
107
107
  cycleModel(direction?: "forward" | "backward"): Promise<ModelCycleResult | undefined>;
108
108
  cycleRoleModels(
109
- direction?: "forward" | "backward",
109
+ direction?: "forward" | "backward"
110
110
  ): Promise<{ model: Model; thinkingLevel: ThinkingLevel; role: ModelRole } | undefined>;
111
111
  cycleThinkingLevel(): ThinkingLevel | undefined;
112
112
 
@@ -141,7 +141,7 @@ interface AgentSession {
141
141
  // Compaction
142
142
  compact(
143
143
  customInstructions?: string,
144
- options?: { onComplete?: (result: CompactionResult) => void; onError?: (error: Error) => void },
144
+ options?: { onComplete?: (result: CompactionResult) => void; onError?: (error: Error) => void }
145
145
  ): Promise<CompactionResult>;
146
146
  abortCompaction(): void;
147
147
 
@@ -491,11 +491,7 @@ const { session } = await createAgentSession({
491
491
  Extensions intercept agent events and can register custom tools/commands. Hooks remain for legacy compatibility.
492
492
 
493
493
  ```typescript
494
- import {
495
- createAgentSession,
496
- discoverExtensions,
497
- type ExtensionFactory,
498
- } from "@oh-my-pi/pi-coding-agent";
494
+ import { createAgentSession, discoverExtensions, type ExtensionFactory } from "@oh-my-pi/pi-coding-agent";
499
495
 
500
496
  // Inline extension
501
497
  const loggingExtension: ExtensionFactory = (api) => {
@@ -878,8 +874,8 @@ import systemPrompt from "./SYSTEM.md" with { type: "text" };
878
874
  const authStorage = await discoverAuthStorage();
879
875
 
880
876
  // Runtime API key override (not persisted)
881
- if (process.env.MY_KEY) {
882
- authStorage.setRuntimeApiKey("anthropic", process.env.MY_KEY);
877
+ if (Bun.env.MY_KEY) {
878
+ authStorage.setRuntimeApiKey("anthropic", Bun.env.MY_KEY);
883
879
  }
884
880
 
885
881
  // Model registry
@@ -78,7 +78,7 @@ const { session } = await createAgentSession({
78
78
 
79
79
  // Full control
80
80
  const customAuth = new AuthStorage("/my/app/auth.json");
81
- customAuth.setRuntimeApiKey("anthropic", process.env.MY_KEY!);
81
+ customAuth.setRuntimeApiKey("anthropic", Bun.env.MY_KEY!);
82
82
  const customRegistry = new ModelRegistry(customAuth);
83
83
 
84
84
  const { session } = await createAgentSession({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oh-my-pi/pi-coding-agent",
3
- "version": "11.0.2",
3
+ "version": "11.0.3",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "ompConfig": {
@@ -80,12 +80,12 @@
80
80
  },
81
81
  "dependencies": {
82
82
  "@mozilla/readability": "0.6.0",
83
- "@oh-my-pi/omp-stats": "11.0.2",
84
- "@oh-my-pi/pi-agent-core": "11.0.2",
85
- "@oh-my-pi/pi-ai": "11.0.2",
86
- "@oh-my-pi/pi-natives": "11.0.2",
87
- "@oh-my-pi/pi-tui": "11.0.2",
88
- "@oh-my-pi/pi-utils": "11.0.2",
83
+ "@oh-my-pi/omp-stats": "11.0.3",
84
+ "@oh-my-pi/pi-agent-core": "11.0.3",
85
+ "@oh-my-pi/pi-ai": "11.0.3",
86
+ "@oh-my-pi/pi-natives": "11.0.3",
87
+ "@oh-my-pi/pi-tui": "11.0.3",
88
+ "@oh-my-pi/pi-utils": "11.0.3",
89
89
  "@openai/agents": "^0.4.5",
90
90
  "@sinclair/typebox": "^0.34.48",
91
91
  "ajv": "^8.17.1",
package/src/cli/args.ts CHANGED
@@ -181,7 +181,7 @@ export function parseArgs(args: string[], extensionFlags?: Map<string, { type: "
181
181
  }
182
182
 
183
183
  export function printHelp(): void {
184
- console.log(`${chalk.bold(APP_NAME)} - AI coding assistant with read, bash, edit, write tools
184
+ console.log(`${chalk.bold(APP_NAME)} - AI coding assistant
185
185
 
186
186
  ${chalk.bold("Usage:")}
187
187
  ${APP_NAME} [options] [@files...] [messages...]
@@ -191,7 +191,11 @@ ${chalk.bold("Subcommands:")}
191
191
  update Check for and install updates
192
192
  config Manage configuration settings
193
193
  setup Install dependencies for optional features
194
+ commit AI-assisted git commits
195
+ stats AI usage statistics dashboard
196
+ jupyter Manage the shared Jupyter gateway
194
197
  shell Interactive shell console (brush-core test)
198
+ grep Test grep tool
195
199
 
196
200
  ${chalk.bold("Options:")}
197
201
  --model <pattern> Model to use (fuzzy match: "opus", "gpt-5.2", or "p-openai/gpt-5.2")
@@ -213,8 +217,9 @@ ${chalk.bold("Options:")}
213
217
  Supports globs (anthropic/*, *sonnet*) and fuzzy matching
214
218
  --no-tools Disable all built-in tools
215
219
  --no-lsp Disable LSP tools, formatting, and diagnostics
216
- --tools <tools> Comma-separated list of tools to enable (default: read,bash,edit,write)
217
- Available: read, bash, edit, write, grep, find, ls
220
+ --tools <tools> Comma-separated list of tools to enable (default: all)
221
+ Available: read, bash, edit, write, grep, find, lsp,
222
+ python, notebook, task, fetch, web_search, browser, ask
218
223
  --thinking <level> Set thinking level: off, minimal, low, medium, high, xhigh
219
224
  --hook <path> Load a hook/extension file (can be used multiple times)
220
225
  --extension, -e <path> Load an extension file (can be used multiple times)
@@ -261,33 +266,51 @@ ${chalk.bold("Examples:")}
261
266
  ${APP_NAME} --thinking high "Solve this complex problem"
262
267
 
263
268
  # Read-only mode (no file modifications possible)
264
- ${APP_NAME} --tools read,grep,find,ls -p "Review the code in src/"
269
+ ${APP_NAME} --tools read,grep,find -p "Review the code in src/"
265
270
 
266
271
  # Export a session file to HTML
267
272
  ${APP_NAME} --export ~/${CONFIG_DIR_NAME}/agent/sessions/--path--/session.jsonl
268
273
  ${APP_NAME} --export session.jsonl output.html
269
274
 
270
275
  ${chalk.bold("Environment Variables:")}
271
- ${chalk.dim("# Model providers")}
272
- ANTHROPIC_API_KEY - Anthropic Claude API key
273
- ANTHROPIC_OAUTH_TOKEN - Anthropic OAuth token (alternative to API key)
274
- OPENAI_API_KEY - OpenAI GPT API key
275
- GEMINI_API_KEY - Google Gemini API key
276
- GROQ_API_KEY - Groq API key
277
- CEREBRAS_API_KEY - Cerebras API key
278
- XAI_API_KEY - xAI Grok API key
279
- OPENROUTER_API_KEY - OpenRouter API key
280
- MISTRAL_API_KEY - Mistral API key
281
- ZAI_API_KEY - ZAI API key
282
- GITHUB_TOKEN - GitHub Copilot models (or GH_TOKEN, COPILOT_GITHUB_TOKEN)
276
+ ${chalk.dim("# Core Providers")}
277
+ ANTHROPIC_API_KEY - Anthropic Claude models
278
+ ANTHROPIC_OAUTH_TOKEN - Anthropic OAuth (takes precedence over API key)
279
+ OPENAI_API_KEY - OpenAI GPT models
280
+ GEMINI_API_KEY - Google Gemini models
281
+ GITHUB_TOKEN - GitHub Copilot (or GH_TOKEN, COPILOT_GITHUB_TOKEN)
283
282
 
284
- ${chalk.dim("# Web search providers")}
285
- EXA_API_KEY - Exa search API key
286
- PERPLEXITY_API_KEY - Perplexity search API key
283
+ ${chalk.dim("# Additional LLM Providers")}
284
+ AZURE_OPENAI_API_KEY - Azure OpenAI models
285
+ GROQ_API_KEY - Groq models
286
+ CEREBRAS_API_KEY - Cerebras models
287
+ XAI_API_KEY - xAI Grok models
288
+ OPENROUTER_API_KEY - OpenRouter aggregated models
289
+ MISTRAL_API_KEY - Mistral models
290
+ ZAI_API_KEY - z.ai models (ZhipuAI/GLM)
291
+ MINIMAX_API_KEY - MiniMax models
292
+ OPENCODE_API_KEY - OpenCode models
293
+ CURSOR_ACCESS_TOKEN - Cursor AI models
294
+ AI_GATEWAY_API_KEY - Vercel AI Gateway
295
+
296
+ ${chalk.dim("# Cloud Providers")}
297
+ AWS_PROFILE - AWS Bedrock (or AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY)
298
+ GOOGLE_CLOUD_PROJECT - Google Vertex AI (requires GOOGLE_CLOUD_LOCATION)
299
+ GOOGLE_APPLICATION_CREDENTIALS - Service account for Vertex AI
300
+
301
+ ${chalk.dim("# Search & Tools")}
302
+ EXA_API_KEY - Exa web search
303
+ PERPLEXITY_API_KEY - Perplexity web search
304
+ ANTHROPIC_SEARCH_API_KEY - Anthropic search provider
287
305
 
288
306
  ${chalk.dim("# Configuration")}
289
- PI_CODING_AGENT_DIR - Session storage directory (default: ~/${CONFIG_DIR_NAME}/agent)
307
+ PI_CODING_AGENT_DIR - Session storage directory (default: ~/${CONFIG_DIR_NAME}/agent)
308
+ PI_SMOL_MODEL - Override smol/fast model (see --smol)
309
+ PI_SLOW_MODEL - Override slow/reasoning model (see --slow)
310
+ PI_PLAN_MODEL - Override planning model (see --plan)
290
311
 
312
+ For complete environment variable reference, see:
313
+ ${chalk.dim("docs/environment-variables.md")}
291
314
  ${chalk.bold("Available Tools (all enabled by default):")}
292
315
  read - Read file contents
293
316
  bash - Execute bash commands
@@ -295,11 +318,12 @@ ${chalk.bold("Available Tools (all enabled by default):")}
295
318
  write - Write files (creates/overwrites)
296
319
  grep - Search file contents
297
320
  find - Find files by glob pattern
298
- ls - List directory contents
299
321
  lsp - Language server protocol (code intelligence)
300
322
  python - Execute Python code (requires: ${APP_NAME} setup python)
301
323
  notebook - Edit Jupyter notebooks
324
+ browser - Browser automation (Puppeteer)
302
325
  task - Launch sub-agents for parallel tasks
326
+ todo_write - Manage todo/task lists
303
327
  fetch - Fetch and process URLs
304
328
  web_search - Search the web
305
329
  ask - Ask user questions (interactive mode only)
@@ -150,7 +150,7 @@ function emptyCustomModelsResult(error?: string): CustomModelsResult {
150
150
  * Checks environment variable first, then treats as literal.
151
151
  */
152
152
  function resolveApiKeyConfig(keyConfig: string): string | undefined {
153
- const envValue = process.env[keyConfig];
153
+ const envValue = Bun.env[keyConfig];
154
154
  if (envValue) return envValue;
155
155
  return keyConfig;
156
156
  }
@@ -418,7 +418,7 @@ export class Settings {
418
418
  prev[role] = modelId;
419
419
  }
420
420
  }
421
- this.set("modelRoles", prev);
421
+ this.override("modelRoles", prev);
422
422
  }
423
423
 
424
424
  /**
@@ -86,7 +86,7 @@ export async function createReportBundle(options: ReportBundleOptions): Promise<
86
86
  files.push("system.json");
87
87
 
88
88
  // Sanitized environment
89
- data["env.json"] = JSON.stringify(sanitizeEnv(process.env as Record<string, string>), null, 2);
89
+ data["env.json"] = JSON.stringify(sanitizeEnv(Bun.env as Record<string, string>), null, 2);
90
90
  files.push("env.json");
91
91
 
92
92
  // Settings/config
@@ -28,8 +28,8 @@ export async function collectSystemInfo(): Promise<SystemInfo> {
28
28
  const cpuModel = cpus[0]?.model ?? "Unknown CPU";
29
29
 
30
30
  // Try to get shell from environment
31
- const shell = process.env.SHELL ?? process.env.ComSpec ?? "unknown";
32
- const terminal = process.env.TERM_PROGRAM ?? process.env.TERM ?? undefined;
31
+ const shell = Bun.env.SHELL ?? Bun.env.ComSpec ?? "unknown";
32
+ const terminal = Bun.env.TERM_PROGRAM ?? Bun.env.TERM ?? undefined;
33
33
 
34
34
  return {
35
35
  os: `${os.type()} ${os.release()} (${os.platform()})`,
@@ -160,7 +160,7 @@ function extractMCPServersFromToml(toml: Record<string, unknown>): Record<string
160
160
  const env: Record<string, string> = { ...config.env };
161
161
  if (config.env_vars) {
162
162
  for (const varName of config.env_vars) {
163
- const value = process.env[varName];
163
+ const value = Bun.env[varName];
164
164
  if (value !== undefined) {
165
165
  env[varName] = value;
166
166
  }
@@ -174,14 +174,14 @@ function extractMCPServersFromToml(toml: Record<string, unknown>): Record<string
174
174
  const headers: Record<string, string> = { ...config.http_headers };
175
175
  if (config.env_http_headers) {
176
176
  for (const [headerName, envVarName] of Object.entries(config.env_http_headers)) {
177
- const value = process.env[envVarName];
177
+ const value = Bun.env[envVarName];
178
178
  if (value !== undefined) {
179
179
  headers[headerName] = value;
180
180
  }
181
181
  }
182
182
  }
183
183
  if (config.bearer_token_env_var) {
184
- const token = process.env[config.bearer_token_env_var];
184
+ const token = Bun.env[config.bearer_token_env_var];
185
185
  if (token) {
186
186
  headers.Authorization = `Bearer ${token}`;
187
187
  }
@@ -294,7 +294,7 @@ export async function loadSkillsFromDir(
294
294
  */
295
295
  export function expandEnvVars(value: string, extraEnv?: Record<string, string>): string {
296
296
  return value.replace(/\$\{([^}:]+)(?::-([^}]*))?\}/g, (_, varName: string, defaultValue?: string) => {
297
- const envValue = extraEnv?.[varName] ?? process.env[varName];
297
+ const envValue = extraEnv?.[varName] ?? Bun.env[varName];
298
298
  if (envValue !== undefined) return envValue;
299
299
  if (defaultValue !== undefined) return defaultValue;
300
300
  return `\${${varName}}`;
@@ -18,9 +18,9 @@ import type {
18
18
  MCPToolWrapperConfig,
19
19
  } from "./types";
20
20
 
21
- /** Find EXA_API_KEY from process.env or .env files */
21
+ /** Find EXA_API_KEY from Bun.env or .env files */
22
22
  export async function findApiKey(): Promise<string | null> {
23
- // Check process.env first
23
+ // Check Bun.env first
24
24
  if ($env.EXA_API_KEY) {
25
25
  return $env.EXA_API_KEY;
26
26
  }
@@ -29,7 +29,7 @@ export async function runDoctorChecks(): Promise<DoctorCheck[]> {
29
29
  ];
30
30
 
31
31
  for (const key of apiKeys) {
32
- const hasKey = !!process.env[key.name];
32
+ const hasKey = !!Bun.env[key.name];
33
33
  checks.push({
34
34
  name: key.name,
35
35
  status: hasKey ? "ok" : "warning",
@@ -225,7 +225,7 @@ export async function warmPythonEnvironment(
225
225
  useSharedGateway?: boolean,
226
226
  sessionFile?: string,
227
227
  ): Promise<{ ok: boolean; reason?: string; docs: PreludeHelper[] }> {
228
- const isTestEnv = process.env.BUN_ENV === "test" || process.env.NODE_ENV === "test";
228
+ const isTestEnv = Bun.env.BUN_ENV === "test" || Bun.env.NODE_ENV === "test";
229
229
  let cacheState: PreludeCacheState | null = null;
230
230
  try {
231
231
  debugStartup("warmPython:ensureKernel:start");
package/src/ipy/kernel.ts CHANGED
@@ -108,7 +108,7 @@ export interface PythonKernelAvailability {
108
108
  }
109
109
 
110
110
  export async function checkPythonKernelAvailability(cwd: string): Promise<PythonKernelAvailability> {
111
- if (process.env.BUN_ENV === "test" || process.env.NODE_ENV === "test" || $env.PI_PYTHON_SKIP_CHECK === "1") {
111
+ if (Bun.env.BUN_ENV === "test" || Bun.env.NODE_ENV === "test" || $env.PI_PYTHON_SKIP_CHECK === "1") {
112
112
  return { ok: true };
113
113
  }
114
114
 
package/src/lsp/client.ts CHANGED
@@ -412,7 +412,7 @@ export async function getOrCreateClient(config: ServerConfig, cwd: string, initT
412
412
  const proc = ptree.spawn([command, ...args], {
413
413
  cwd,
414
414
  stdin: "pipe",
415
- env: env ? { ...process.env, ...env } : undefined,
415
+ env: env ? { ...Bun.env, ...env } : undefined,
416
416
  });
417
417
 
418
418
  const client: LspClient = {
package/src/lsp/lspmux.ts CHANGED
@@ -66,11 +66,11 @@ function getConfigPath(): string {
66
66
  const home = os.homedir();
67
67
  switch (os.platform()) {
68
68
  case "win32":
69
- return path.join(process.env.APPDATA ?? path.join(home, "AppData", "Roaming"), "lspmux", "config.toml");
69
+ return path.join(Bun.env.APPDATA ?? path.join(home, "AppData", "Roaming"), "lspmux", "config.toml");
70
70
  case "darwin":
71
71
  return path.join(home, "Library", "Application Support", "lspmux", "config.toml");
72
72
  default:
73
- return path.join(process.env.XDG_CONFIG_HOME ?? path.join(home, ".config"), "lspmux", "config.toml");
73
+ return path.join(Bun.env.XDG_CONFIG_HOME ?? path.join(home, ".config"), "lspmux", "config.toml");
74
74
  }
75
75
  }
76
76
 
package/src/main.ts CHANGED
@@ -657,11 +657,11 @@ export async function main(args: string[]) {
657
657
  const slowModel = parsed.slow ?? $env.PI_SLOW_MODEL;
658
658
  const planModel = parsed.plan ?? $env.PI_PLAN_MODEL;
659
659
  if (smolModel || slowModel || planModel) {
660
- const currentRoles = settings.get("modelRoles") as Record<string, string>;
661
- if (smolModel) currentRoles.smol = smolModel;
662
- if (slowModel) currentRoles.slow = slowModel;
663
- if (planModel) currentRoles.plan = planModel;
664
- settings.override("modelRoles", currentRoles);
660
+ settings.overrideModelRoles({
661
+ smol: smolModel,
662
+ slow: slowModel,
663
+ plan: planModel,
664
+ });
665
665
  }
666
666
 
667
667
  await initTheme(settings.get("theme"), isInteractive, settings.get("symbolPreset"), settings.get("colorBlindMode"));
@@ -47,7 +47,7 @@ export class StdioTransport implements MCPTransport {
47
47
 
48
48
  const args = this.config.args ?? [];
49
49
  const env = {
50
- ...process.env,
50
+ ...Bun.env,
51
51
  ...this.config.env,
52
52
  };
53
53
 
@@ -114,7 +114,7 @@ export class RpcClient {
114
114
 
115
115
  this.process = ptree.spawn(["bun", cliPath, ...args], {
116
116
  cwd: this.options.cwd,
117
- env: { ...process.env, ...this.options.env },
117
+ env: { ...Bun.env, ...this.options.env },
118
118
  stdin: "pipe",
119
119
  });
120
120
 
@@ -1053,15 +1053,15 @@ type ColorMode = "truecolor" | "256color";
1053
1053
  // ============================================================================
1054
1054
 
1055
1055
  function detectColorMode(): ColorMode {
1056
- const colorterm = process.env.COLORTERM;
1056
+ const colorterm = Bun.env.COLORTERM;
1057
1057
  if (colorterm === "truecolor" || colorterm === "24bit") {
1058
1058
  return "truecolor";
1059
1059
  }
1060
1060
  // Windows Terminal supports truecolor
1061
- if (process.env.WT_SESSION) {
1061
+ if (Bun.env.WT_SESSION) {
1062
1062
  return "truecolor";
1063
1063
  }
1064
- const term = process.env.TERM || "";
1064
+ const term = Bun.env.TERM || "";
1065
1065
  // Only fall back to 256color for truly limited terminals
1066
1066
  if (term === "dumb" || term === "" || term === "linux") {
1067
1067
  return "256color";
@@ -1706,7 +1706,7 @@ export async function getThemeByName(name: string): Promise<Theme | undefined> {
1706
1706
  }
1707
1707
 
1708
1708
  function detectTerminalBackground(): "dark" | "light" {
1709
- const colorfgbg = process.env.COLORFGBG || "";
1709
+ const colorfgbg = Bun.env.COLORFGBG || "";
1710
1710
  if (colorfgbg) {
1711
1711
  const parts = colorfgbg.split(";");
1712
1712
  if (parts.length >= 2) {
@@ -218,7 +218,7 @@ export class EditTool implements AgentTool<TInput> {
218
218
  PI_EDIT_FUZZY: editFuzzy = "auto",
219
219
  PI_EDIT_FUZZY_THRESHOLD: editFuzzyThreshold = "auto",
220
220
  PI_EDIT_VARIANT: envEditVariant = "auto",
221
- } = process.env;
221
+ } = Bun.env;
222
222
  this.envEditVariant = envEditVariant;
223
223
 
224
224
  if (envEditVariant !== "replace" && envEditVariant !== "patch" && envEditVariant !== "auto") {
package/src/sdk.ts CHANGED
@@ -17,7 +17,7 @@
17
17
  * // Full control
18
18
  * const session = await createAgentSession({
19
19
  * model: myModel,
20
- * getApiKey: async () => process.env.MY_KEY,
20
+ * getApiKey: async () => Bun.env.MY_KEY,
21
21
  * toolNames: ["read", "bash", "edit", "write"], // Filter tools
22
22
  * extensions: [],
23
23
  * skills: [],
@@ -566,7 +566,7 @@ function createCustomToolsExtension(tools: CustomTool[]): ExtensionFactory {
566
566
  * // Full control
567
567
  * const { session } = await createAgentSession({
568
568
  * model: myModel,
569
- * getApiKey: async () => process.env.MY_KEY,
569
+ * getApiKey: async () => Bun.env.MY_KEY,
570
570
  * systemPrompt: 'You are helpful.',
571
571
  * tools: codingTools({ cwd: process.cwd() }),
572
572
  * skills: [],
@@ -814,7 +814,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
814
814
 
815
815
  // If we extracted Exa API keys from MCP configs and EXA_API_KEY isn't set, use the first one
816
816
  if (mcpResult.exaApiKeys.length > 0 && !$env.EXA_API_KEY) {
817
- process.env.EXA_API_KEY = mcpResult.exaApiKeys[0];
817
+ Bun.env.EXA_API_KEY = mcpResult.exaApiKeys[0];
818
818
  }
819
819
 
820
820
  // Log MCP errors
@@ -193,15 +193,15 @@ async function getGpuModel(): Promise<string | null> {
193
193
  }
194
194
 
195
195
  function getTerminalName(): string {
196
- const termProgram = process.env.TERM_PROGRAM;
197
- const termProgramVersion = process.env.TERM_PROGRAM_VERSION;
196
+ const termProgram = Bun.env.TERM_PROGRAM;
197
+ const termProgramVersion = Bun.env.TERM_PROGRAM_VERSION;
198
198
  if (termProgram) {
199
199
  return termProgramVersion ? `${termProgram} ${termProgramVersion}` : termProgram;
200
200
  }
201
201
 
202
- if (process.env.WT_SESSION) return "Windows Terminal";
202
+ if (Bun.env.WT_SESSION) return "Windows Terminal";
203
203
 
204
- const term = firstNonEmpty(process.env.TERM, process.env.COLORTERM, process.env.TERMINAL_EMULATOR);
204
+ const term = firstNonEmpty(Bun.env.TERM, Bun.env.COLORTERM, Bun.env.TERMINAL_EMULATOR);
205
205
  return term ?? "unknown";
206
206
  }
207
207
 
@@ -216,12 +216,12 @@ function normalizeDesktopValue(value: string): string {
216
216
  }
217
217
 
218
218
  function getDesktopEnvironment(): string {
219
- if (process.env.KDE_FULL_SESSION === "true") return "KDE";
219
+ if (Bun.env.KDE_FULL_SESSION === "true") return "KDE";
220
220
  const raw = firstNonEmpty(
221
- process.env.XDG_CURRENT_DESKTOP,
222
- process.env.DESKTOP_SESSION,
223
- process.env.XDG_SESSION_DESKTOP,
224
- process.env.GDMSESSION,
221
+ Bun.env.XDG_CURRENT_DESKTOP,
222
+ Bun.env.DESKTOP_SESSION,
223
+ Bun.env.XDG_SESSION_DESKTOP,
224
+ Bun.env.GDMSESSION,
225
225
  );
226
226
  return raw ? normalizeDesktopValue(raw) : "unknown";
227
227
  }
@@ -252,10 +252,10 @@ function matchKnownWindowManager(value: string): string | null {
252
252
  }
253
253
 
254
254
  function getWindowManager(): string {
255
- const explicit = firstNonEmpty(process.env.WINDOWMANAGER);
255
+ const explicit = firstNonEmpty(Bun.env.WINDOWMANAGER);
256
256
  if (explicit) return explicit;
257
257
 
258
- const desktop = firstNonEmpty(process.env.XDG_CURRENT_DESKTOP, process.env.DESKTOP_SESSION);
258
+ const desktop = firstNonEmpty(Bun.env.XDG_CURRENT_DESKTOP, Bun.env.DESKTOP_SESSION);
259
259
  if (desktop) {
260
260
  const matched = matchKnownWindowManager(desktop);
261
261
  if (matched) return matched;
@@ -239,7 +239,7 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
239
239
  !skipPythonPreflight &&
240
240
  pythonMode !== "bash-only" &&
241
241
  (requestedTools === undefined || requestedTools.includes("python"));
242
- const isTestEnv = process.env.BUN_ENV === "test" || process.env.NODE_ENV === "test";
242
+ const isTestEnv = Bun.env.BUN_ENV === "test" || Bun.env.NODE_ENV === "test";
243
243
  const skipPythonWarm = isTestEnv || $env.PI_PYTHON_SKIP_CHECK === "1";
244
244
  if (shouldCheckPython) {
245
245
  const availability = await checkPythonKernelAvailability(session.cwd);