@oh-my-pi/pi-coding-agent 5.8.0 → 6.1.0

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,33 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [6.1.0] - 2026-01-19
6
+
7
+ ### Added
8
+
9
+ - Added lspmux integration for LSP server multiplexing to reduce startup time and memory usage
10
+ - Added LSP tool proxy support for subagent workers
11
+ - Updated LSP status command to show lspmux connection state
12
+ - Added maxdepth and mindepth parameters to find function for depth-controlled file search
13
+ - Added counter function to count occurrences and sort by frequency
14
+ - Added basenames function to extract base names from paths
15
+
16
+ ### Changed
17
+
18
+ - Simplified rust-analyzer default configuration by removing custom initOptions and settings
19
+
20
+ ## [6.0.0] - 2026-01-19
21
+
22
+ ### Added
23
+
24
+ - Added Cursor and OpenAI Codex OAuth providers
25
+ - Added Windows installer bash shell auto-configuration
26
+ - Added dedicated TTSR settings tab (separated from Voice/TTS)
27
+
28
+ ### Fixed
29
+
30
+ - Fixed TTSR abbreviation expansion from TTSR to Time Traveling Stream Rules
31
+
5
32
  ## [5.8.0] - 2026-01-19
6
33
  ### Changed
7
34
 
package/README.md CHANGED
@@ -57,24 +57,23 @@ npm install -g @oh-my-pi/pi-coding-agent
57
57
 
58
58
  **Standalone binary:**
59
59
 
60
- Download from [GitHub Releases](https://github.com/badlogic/pi-mono/releases):
60
+ Download from [GitHub Releases](https://github.com/can1357/oh-my-pi/releases):
61
61
 
62
- | Platform | Archive |
62
+ | Platform | Binary |
63
63
  | ------------------- | ------------------------ |
64
- | macOS Apple Silicon | `omp-darwin-arm64.tar.gz` |
65
- | macOS Intel | `omp-darwin-x64.tar.gz` |
66
- | Linux x64 | `omp-linux-x64.tar.gz` |
67
- | Linux ARM64 | `omp-linux-arm64.tar.gz` |
68
- | Windows x64 | `omp-windows-x64.zip` |
64
+ | macOS Apple Silicon | `omp-darwin-arm64` |
65
+ | macOS Intel | `omp-darwin-x64` |
66
+ | Linux x64 | `omp-linux-x64` |
67
+ | Linux ARM64 | `omp-linux-arm64` |
68
+ | Windows x64 | `omp-windows-x64.exe` |
69
69
 
70
70
  ```bash
71
71
  # macOS/Linux
72
- tar -xzf omp-darwin-arm64.tar.gz
73
- ./omp
72
+ chmod +x omp-darwin-arm64
73
+ ./omp-darwin-arm64
74
74
 
75
75
  # Windows
76
- unzip omp-windows-x64.zip
77
- omp.exe
76
+ omp-windows-x64.exe
78
77
  ```
79
78
 
80
79
  **macOS note:** The binary is unsigned. If blocked, run: `xattr -c ./omp`
@@ -133,24 +132,7 @@ return config
133
132
 
134
133
  ### API Keys & OAuth
135
134
 
136
- **Option 1: Auth file** (recommended)
137
-
138
- Add API keys to `~/.omp/agent/auth.json`:
139
-
140
- ```json
141
- {
142
- "anthropic": [
143
- { "type": "api_key", "key": "sk-ant-..." },
144
- { "type": "api_key", "key": "sk-ant-..." }
145
- ],
146
- "openai": { "type": "api_key", "key": "sk-..." },
147
- "google": { "type": "api_key", "key": "..." }
148
- }
149
-
150
- If a provider has multiple credentials, new sessions round robin across them and stay sticky per session.
151
- ```
152
-
153
- **Option 2: Environment variables**
135
+ **Option 1: Environment variables** (recommended)
154
136
 
155
137
  | Provider | Auth Key | Environment Variable |
156
138
  | ---------- | ------------ | -------------------- |
@@ -164,16 +146,16 @@ If a provider has multiple credentials, new sessions round robin across them and
164
146
  | OpenRouter | `openrouter` | `OPENROUTER_API_KEY` |
165
147
  | ZAI | `zai` | `ZAI_API_KEY` |
166
148
 
167
- Auth file keys take priority over environment variables.
168
-
169
- **OAuth Providers:**
149
+ **Option 2: OAuth**
170
150
 
171
151
  Use `/login` to authenticate with subscription-based or free-tier providers:
172
152
 
173
153
  | Provider | Models | Cost |
174
154
  | -------------------------- | ----------------------------------------------- | --------------------- |
175
155
  | Anthropic (Claude Pro/Max) | Claude models via your subscription | Subscription |
156
+ | Cursor | Claude, GPT-4o via Cursor Pro subscription | Subscription |
176
157
  | GitHub Copilot | GPT-4o, Claude, Gemini via Copilot subscription | Subscription |
158
+ | OpenAI Codex | o3, o4-mini via ChatGPT Plus/Pro subscription | Subscription |
177
159
  | Google Gemini CLI | Gemini 2.0/2.5 models | Free (Google account) |
178
160
  | Google Antigravity | Gemini 3, Claude, GPT-OSS | Free (Google account) |
179
161
 
@@ -182,7 +164,7 @@ omp
182
164
  /login # Select provider, authorize in browser
183
165
  ```
184
166
 
185
- **Note:** `/login` replaces any existing API keys for that provider with OAuth credentials in `auth.json`. If OAuth credentials already exist, `/login` appends another entry.
167
+ **Note:** `/login` replaces any existing API keys for that provider with OAuth credentials. If OAuth credentials already exist, `/login` appends another entry.
186
168
 
187
169
  **GitHub Copilot notes:**
188
170
 
@@ -196,7 +178,7 @@ omp
196
178
  - Both are free with any Google account, subject to rate limits
197
179
  - Paid Cloud Code Assist subscriptions: set `GOOGLE_CLOUD_PROJECT` or `GOOGLE_CLOUD_PROJECT_ID` env var to your project ID
198
180
 
199
- Credentials stored in `~/.omp/agent/auth.json`. Use `/logout` to clear.
181
+ Credentials stored in `~/.omp/agent/agent.db`. Use `/logout` to clear.
200
182
 
201
183
  ### Quick Start
202
184
 
@@ -281,10 +263,10 @@ The agent reads, writes, and edits files, and executes commands via bash.
281
263
  | Ctrl+D | Exit (when editor is empty) |
282
264
  | Ctrl+Z | Suspend to background (use `fg` in shell to resume) |
283
265
  | Shift+Tab | Cycle thinking level |
284
- | Ctrl+P / Shift+Ctrl+P | Cycle models forward/backward (scoped by `--models`) |
266
+ | Ctrl+P / Shift+Ctrl+P | Cycle role models (slow/default/smol) |
285
267
  | Ctrl+L | Open model selector |
286
268
  | Ctrl+O | Toggle tool output expansion |
287
- | Ctrl+T | Toggle thinking block visibility |
269
+ | Ctrl+T | Toggle todo list expansion |
288
270
  | Ctrl+G | Edit message in external editor (`$VISUAL` or `$EDITOR`) |
289
271
 
290
272
  ### Bash Mode
@@ -489,7 +471,7 @@ Add custom models (Ollama, vLLM, LM Studio, etc.) via `~/.omp/agent/models.json`
489
471
  }
490
472
  ```
491
473
 
492
- **Supported APIs:** `openai-completions`, `openai-responses`, `anthropic-messages`, `google-generative-ai`
474
+ **Supported APIs:** `openai-completions`, `openai-responses`, `openai-codex-responses`, `anthropic-messages`, `google-generative-ai`, `google-vertex`
493
475
 
494
476
  **API key resolution:** The `apiKey` field is checked as environment variable name first, then used as literal value.
495
477
 
@@ -523,7 +505,6 @@ Add custom models (Ollama, vLLM, LM Studio, etc.) via `~/.omp/agent/models.json`
523
505
  | `supportsStore` | Whether provider supports `store` field |
524
506
  | `supportsDeveloperRole` | Use `developer` vs `system` role |
525
507
  | `supportsReasoningEffort` | Support for `reasoning_effort` parameter |
526
- | `supportsUsageInStreaming` | Whether provider supports `stream_options: { include_usage: true }`. Default: `true` |
527
508
  | `maxTokensField` | Use `max_completion_tokens` or `max_tokens` |
528
509
 
529
510
  **Live reload:** The file reloads each time you open `/model`. Edit during session; no restart needed.
@@ -552,8 +533,9 @@ Global `~/.omp/agent/settings.json` stores persistent preferences:
552
533
  ```json
553
534
  {
554
535
  "theme": "dark",
555
- "defaultProvider": "anthropic",
556
- "defaultModel": "claude-sonnet-4-20250514",
536
+ "modelRoles": {
537
+ "default": "anthropic/claude-sonnet-4-20250514"
538
+ },
557
539
  "defaultThinkingLevel": "medium",
558
540
  "enabledModels": ["anthropic/*", "*gpt*", "gemini-2.5-pro:high"],
559
541
  "queueMode": "one-at-a-time",
@@ -575,22 +557,19 @@ Global `~/.omp/agent/settings.json` stores persistent preferences:
575
557
  },
576
558
  "terminal": {
577
559
  "showImages": true
578
- },
579
- "hooks": ["/path/to/hook.ts"],
580
- "customTools": ["/path/to/tool.ts"]
560
+ }
581
561
  }
582
562
  ```
583
563
 
584
564
  | Setting | Description | Default |
585
565
  | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | --------------- |
586
566
  | `theme` | Color theme name | auto-detected |
587
- | `defaultProvider` | Default model provider | - |
588
- | `defaultModel` | Default model ID | - |
567
+ | `modelRoles` | Model assignments by role (e.g., `{"default": "anthropic/claude-sonnet-4-20250514", "slow": "...", "smol": "..."}`) | - |
589
568
  | `defaultThinkingLevel` | Thinking level: `off`, `minimal`, `low`, `medium`, `high`, `xhigh` | - |
590
569
  | `enabledModels` | Model patterns for cycling. Supports glob patterns (`github-copilot/*`, `*sonnet*`) and fuzzy matching. Same as `--models` CLI flag | - |
591
570
  | `queueMode` | Message queue mode: `all` or `one-at-a-time` | `one-at-a-time` |
592
571
  | `shellPath` | Custom bash path (Windows) | auto-detected |
593
- | `hideThinkingBlock` | Hide thinking blocks in output (Ctrl+T to toggle) | `false` |
572
+ | `hideThinkingBlock` | Hide thinking blocks in output | `false` |
594
573
  | `collapseChangelog` | Show condensed changelog after update | `false` |
595
574
  | `compaction.enabled` | Enable auto-compaction | `true` |
596
575
  | `compaction.reserveTokens` | Tokens to reserve before compaction triggers | `16384` |
@@ -600,8 +579,6 @@ Global `~/.omp/agent/settings.json` stores persistent preferences:
600
579
  | `retry.maxRetries` | Maximum retry attempts | `3` |
601
580
  | `retry.baseDelayMs` | Base delay for exponential backoff | `2000` |
602
581
  | `terminal.showImages` | Render images inline (supported terminals) | `true` |
603
- | `hooks` | Additional hook file paths | `[]` |
604
- | `customTools` | Additional custom tool file paths | `[]` |
605
582
 
606
583
  ---
607
584
 
@@ -685,10 +662,10 @@ A skill provides specialized workflows, setup instructions, helper scripts, and
685
662
 
686
663
  **Skill locations:**
687
664
 
688
- - Omp user: `~/.omp/agent/skills/**/SKILL.md` (recursive)
689
- - Omp project: `.omp/skills/**/SKILL.md` (recursive)
665
+ - Omp user: `~/.omp/agent/skills/*/SKILL.md`
666
+ - Omp project: `.omp/skills/*/SKILL.md`
690
667
  - Claude Code: `~/.claude/skills/*/SKILL.md` and `.claude/skills/*/SKILL.md`
691
- - Codex CLI: `~/.codex/skills/**/SKILL.md` (recursive)
668
+ - Codex CLI: `~/.codex/skills/*/SKILL.md`
692
669
 
693
670
  **Format:**
694
671
 
@@ -733,8 +710,8 @@ Hooks are TypeScript modules that extend omp's behavior by subscribing to lifecy
733
710
 
734
711
  **Hook locations:**
735
712
 
736
- - Global: `~/.omp/agent/hooks/*.ts`
737
- - Project: `.omp/hooks/*.ts`
713
+ - Global: `~/.omp/agent/hooks/pre/*.ts`, `~/.omp/agent/hooks/post/*.ts`
714
+ - Project: `.omp/hooks/pre/*.ts`, `.omp/hooks/post/*.ts`
738
715
  - CLI: `--hook <path>` (for debugging)
739
716
 
740
717
  **Quick example** (permission gate):
@@ -849,7 +826,7 @@ omp [options] [@files...] [messages...]
849
826
 
850
827
  | Option | Description |
851
828
  | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
852
- | `--provider <name>` | Provider: `anthropic`, `openai`, `google`, `mistral`, `xai`, `groq`, `cerebras`, `openrouter`, `zai`, `github-copilot`, `google-gemini-cli`, `google-antigravity`, or custom |
829
+ | `--provider <name>` | Provider: `anthropic`, `openai`, `google`, `mistral`, `xai`, `groq`, `cerebras`, `openrouter`, `zai`, `cursor`, `github-copilot`, `openai-codex`, `google-gemini-cli`, `google-antigravity`, or custom |
853
830
  | `--model <id>` | Model ID |
854
831
  | `--api-key <key>` | API key (overrides environment) |
855
832
  | `--system-prompt <text\|file>` | Custom system prompt (text or file path) |
@@ -861,14 +838,16 @@ omp [options] [@files...] [messages...]
861
838
  | `--session-dir <dir>` | Directory for session storage and lookup |
862
839
  | `--continue`, `-c` | Continue most recent session |
863
840
  | `--resume`, `-r` | Select session to resume |
864
- | `--models <patterns>` | Comma-separated patterns for Ctrl+P cycling. Supports glob patterns (e.g., `anthropic/*`, `*sonnet*:high`) and fuzzy matching (e.g., `sonnet,haiku:low`) |
841
+ | `--models <patterns>` | Comma-separated patterns for model role cycling. Supports glob patterns (e.g., `anthropic/*`, `*sonnet*:high`) and fuzzy matching (e.g., `sonnet,haiku:low`) |
865
842
  | `--no-tools` | Disable all built-in tools |
866
- | `--tools <tools>` | Comma-separated tool list (default: `read,bash,edit,write`) |
867
- | `--thinking <level>` | Thinking level: `off`, `minimal`, `low`, `medium`, `high` |
843
+ | `--tools <tools>` | Restrict to comma-separated tool list (default: all tools enabled) |
844
+ | `--thinking <level>` | Thinking level: `off`, `minimal`, `low`, `medium`, `high`, `xhigh` |
868
845
  | `--extension <path>`, `-e` | Load an extension file (can be used multiple times) |
869
846
  | `--no-extensions` | Disable extension discovery (explicit `-e` paths still work) |
870
847
  | `--no-skills` | Disable skills discovery and loading |
871
848
  | `--skills <patterns>` | Comma-separated glob patterns to filter skills (e.g., `git-*,docker`) |
849
+ | `--no-lsp` | Disable LSP integration |
850
+ | `--hook <path>` | Load a hook file (for debugging) |
872
851
  | `--export <file> [output]` | Export session to HTML |
873
852
  | `--help`, `-h` | Show help |
874
853
  | `--version`, `-v` | Show version |
@@ -930,34 +909,40 @@ omp --export session.jsonl output.html
930
909
  | Variable | Description |
931
910
  |----------|-------------|
932
911
  | `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, etc. | API keys for providers (see [API Keys & OAuth](#api-keys--oauth)) |
933
- | `PI_CODING_AGENT_DIR` | Override the agent config directory (default: `~/.omp/agent`) |
934
- | `PI_SKIP_VERSION_CHECK` | Skip new version check at startup (useful for Nix or other package manager installs) |
912
+ | `OMP_CODING_AGENT_DIR` | Override the agent config directory (default: `~/.omp/agent`) |
935
913
  | `VISUAL`, `EDITOR` | External editor for Ctrl+G (e.g., `vim`, `code --wait`) |
936
914
 
937
915
  ---
938
916
 
939
917
  ## Tools
940
918
 
941
- ### Default Tools
919
+ All tools are enabled by default. Use `--tools <list>` to restrict to a subset.
942
920
 
943
- | Tool | Description |
944
- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
945
- | `read` | Read file contents. Images sent as attachments. Text: first 2000 lines, lines truncated at 2000 chars. Use offset/limit for large files. |
946
- | `write` | Write/overwrite file. Creates parent directories. |
947
- | `edit` | Replace exact text in file. Must match exactly including whitespace. Fails if text appears multiple times or not found. |
948
- | `bash` | Execute command. Returns stdout/stderr. Optional `timeout` parameter. |
921
+ ### Core Tools
949
922
 
950
- ### Read-Only Tools
923
+ | Tool | Description |
924
+ | ------- | --------------------------------------------------------------------------------------------------------------- |
925
+ | `read` | Read file contents. Images sent as attachments. Text: first 2000 lines. Use offset/limit for large files. |
926
+ | `write` | Write/overwrite file. Creates parent directories. |
927
+ | `edit` | Replace text in file with fuzzy whitespace matching. Fails if text appears multiple times or not found. |
928
+ | `bash` | Execute command. Returns stdout/stderr. Optional `timeout` parameter. |
929
+ | `grep` | Search file contents (regex or literal). Respects `.gitignore`. |
930
+ | `find` | Search for files by glob pattern. Respects `.gitignore`. |
931
+ | `ls` | List directory contents. Includes dotfiles. |
951
932
 
952
- Available via `--tools` flag:
933
+ ### Additional Built-in Tools
953
934
 
954
- | Tool | Description |
955
- | ------ | --------------------------------------------------------------- |
956
- | `grep` | Search file contents (regex or literal). Respects `.gitignore`. |
957
- | `find` | Search for files by glob pattern. Respects `.gitignore`. |
958
- | `ls` | List directory contents. Includes dotfiles. |
935
+ | Tool | Description |
936
+ | ------------- | ----------------------------------------------------- |
937
+ | `task` | Spawn sub-agents for complex multi-step tasks |
938
+ | `lsp` | Language Server Protocol queries (go-to-definition, references, hover) |
939
+ | `todo_write` | Track task progress during sessions |
940
+ | `web_search` | Search the web |
941
+ | `web_fetch` | Fetch and render URLs |
942
+ | `python` | Execute Python code in IPython kernel |
943
+ | `notebook` | Edit Jupyter notebook cells |
959
944
 
960
- Example: `--tools read,grep,find,ls` for code review without modification.
945
+ Example: `--tools read,grep,find,ls` for read-only code review.
961
946
 
962
947
  For adding new tools, see [Custom Tools](#custom-tools) in the Configuration section.
963
948
 
@@ -972,8 +957,8 @@ For embedding omp in Node.js/TypeScript applications, use the SDK:
972
957
  ```typescript
973
958
  import { createAgentSession, discoverAuthStorage, discoverModels, SessionManager } from "@oh-my-pi/pi-coding-agent";
974
959
 
975
- const authStorage = discoverAuthStorage();
976
- const modelRegistry = discoverModels(authStorage);
960
+ const authStorage = await discoverAuthStorage();
961
+ const modelRegistry = await discoverModels(authStorage);
977
962
 
978
963
  const { session } = await createAgentSession({
979
964
  sessionManager: SessionManager.inMemory(),
@@ -1035,21 +1020,15 @@ Works with both session files and streaming event logs from `--mode json`.
1035
1020
 
1036
1021
  ## Philosophy
1037
1022
 
1038
- Omp is opinionated about what it won't do. These are intentional design decisions to minimize context bloat and avoid anti-patterns.
1039
-
1040
- **No MCP.** Build CLI tools with READMEs (see [Skills](#skills)). The agent reads them on demand. [Would you like to know more?](https://mariozechner.at/posts/2025-11-02-what-if-you-dont-need-mcp/)
1041
-
1042
- **No sub-agents.** Spawn omp instances via tmux, or [build your own sub-agent tool](examples/custom-tools/subagent/) with [custom tools](#custom-tools). Full observability and steerability.
1043
-
1044
- **No permission popups.** Security theater. Run in a container or build your own with [Hooks](#hooks).
1023
+ Omp is a fork of [Pi](https://github.com/badlogic/pi) by [Mario Zechner](https://github.com/badlogic). Pi is intentionally minimal—no MCP, no sub-agents, no built-in todos. Omp is the opposite: batteries included.
1045
1024
 
1046
- **No plan mode.** Gather context in one session, write plans to file, start fresh for implementation.
1025
+ **Yin to Pi's Yang.** Same foundation, different philosophy. Pi strips away; omp adds on. Both are valid approaches—pick what fits your workflow.
1047
1026
 
1048
- **No built-in to-dos.** They confuse models. Use a TODO.md file, or [build your own](examples/custom-tools/todo/) with [custom tools](#custom-tools).
1027
+ **Full toolset by default.** Sub-agents, MCP, LSP, web search, Python execution, todo tracking—all enabled out of the box. Use `--tools` to restrict when needed.
1049
1028
 
1050
- **No background bash.** Use tmux. Full observability, direct interaction.
1029
+ **Agent orchestration built-in.** The Task tool spawns specialized sub-agents (explore, plan, reviewer, task) for complex multi-step work. Parallelism and delegation, not just chat.
1051
1030
 
1052
- Read the [blog post](https://mariozechner.at/posts/2025-11-30-pi-coding-agent/) for the full rationale.
1031
+ **Multiple extension points.** [Skills](#skills) for on-demand capabilities, [Hooks](#hooks) for lifecycle control, [Custom Tools](#custom-tools) for new abilities, MCP for existing integrations.
1053
1032
 
1054
1033
  ---
1055
1034
 
@@ -1074,10 +1053,10 @@ Change `name`, `configDir`, and `bin` field for your fork. Affects CLI banner, c
1074
1053
 
1075
1054
  Three execution modes: npm install, standalone binary, tsx from source.
1076
1055
 
1077
- **Always use `src/paths.ts`** for package assets:
1056
+ **Always use `src/config.ts`** for package assets:
1078
1057
 
1079
1058
  ```typescript
1080
- import { getPackageDir, getThemeDir } from "./paths.js";
1059
+ import { getPackageDir } from "./config.js";
1081
1060
  ```
1082
1061
 
1083
1062
  Never use `__dirname` directly for package assets.
@@ -1096,5 +1075,5 @@ MIT
1096
1075
 
1097
1076
  ## See Also
1098
1077
 
1099
- - [@oh-my-pi/omp-ai](https://www.npmjs.com/package/@oh-my-pi/omp-ai): Core LLM toolkit
1100
- - [@oh-my-pi/omp-agent](https://www.npmjs.com/package/@oh-my-pi/omp-agent): Agent framework
1078
+ - [@oh-my-pi/pi-ai](https://www.npmjs.com/package/@oh-my-pi/pi-ai): Core LLM toolkit
1079
+ - [@oh-my-pi/pi-agent-core](https://www.npmjs.com/package/@oh-my-pi/pi-agent-core): Agent framework
package/docs/sdk.md CHANGED
@@ -20,8 +20,8 @@ See [examples/sdk/](../examples/sdk/) for working examples from minimal to full
20
20
  import { createAgentSession, discoverAuthStorage, discoverModels, SessionManager } from "@oh-my-pi/pi-coding-agent";
21
21
 
22
22
  // Set up credential storage and model registry
23
- const authStorage = discoverAuthStorage();
24
- const modelRegistry = discoverModels(authStorage);
23
+ const authStorage = await discoverAuthStorage();
24
+ const modelRegistry = await discoverModels(authStorage);
25
25
 
26
26
  const { session } = await createAgentSession({
27
27
  sessionManager: SessionManager.inMemory(),
@@ -251,8 +251,8 @@ const { session } = await createAgentSession({
251
251
  import { getModel } from "@oh-my-pi/pi-ai";
252
252
  import { discoverAuthStorage, discoverModels } from "@oh-my-pi/pi-coding-agent";
253
253
 
254
- const authStorage = discoverAuthStorage();
255
- const modelRegistry = discoverModels(authStorage);
254
+ const authStorage = await discoverAuthStorage();
255
+ const modelRegistry = await discoverModels(authStorage);
256
256
 
257
257
  // Find specific built-in model (doesn't check if API key exists)
258
258
  const opus = getModel("anthropic", "claude-opus-4-5");
@@ -301,8 +301,8 @@ API key resolution priority (handled by AuthStorage):
301
301
  import { AuthStorage, ModelRegistry, discoverAuthStorage, discoverModels } from "@oh-my-pi/pi-coding-agent";
302
302
 
303
303
  // Default: uses ~/.omp/agent/auth.json and ~/.omp/agent/models.json
304
- const authStorage = discoverAuthStorage();
305
- const modelRegistry = discoverModels(authStorage);
304
+ const authStorage = await discoverAuthStorage();
305
+ const modelRegistry = await discoverModels(authStorage);
306
306
 
307
307
  const { session } = await createAgentSession({
308
308
  sessionManager: SessionManager.inMemory(),
@@ -742,8 +742,8 @@ import {
742
742
  } from "@oh-my-pi/pi-coding-agent";
743
743
 
744
744
  // Auth and Models
745
- const authStorage = discoverAuthStorage(); // ~/.omp/agent/auth.json
746
- const modelRegistry = discoverModels(authStorage); // + ~/.omp/agent/models.json
745
+ const authStorage = await discoverAuthStorage(); // ~/.omp/agent/agent.db
746
+ const modelRegistry = await discoverModels(authStorage); // + ~/.omp/agent/models.json
747
747
  const allModels = modelRegistry.getAll(); // All models (built-in + custom)
748
748
  const available = await modelRegistry.getAvailable(); // Only models with API keys
749
749
  const model = modelRegistry.find("provider", "id"); // Find specific model
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oh-my-pi/pi-coding-agent",
3
- "version": "5.8.0",
3
+ "version": "6.1.0",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "ompConfig": {
@@ -34,17 +34,16 @@
34
34
  "clean": "rm -rf dist",
35
35
  "build": "tsgo -p tsconfig.build.json && chmod +x dist/cli.js",
36
36
  "build:binary": "bun run generate-wasm-b64 && bun build --compile ./src/cli.ts --outfile dist/omp",
37
- "generate-wasm-b64": "bun scripts/generate-wasm-b64.ts",
38
37
  "generate-template": "bun scripts/generate-template.ts",
39
- "postinstall": "[ -f scripts/generate-wasm-b64.ts ] && bun run generate-wasm-b64 && bun run generate-template || true",
38
+ "postinstall": "bun run generate-template || true",
40
39
  "test": "bun test",
41
40
  "prepublishOnly": "bun run generate-template && bun run clean && bun run build"
42
41
  },
43
42
  "dependencies": {
44
- "@oh-my-pi/pi-agent-core": "5.8.0",
45
- "@oh-my-pi/pi-ai": "5.8.0",
46
- "@oh-my-pi/pi-git-tool": "5.8.0",
47
- "@oh-my-pi/pi-tui": "5.8.0",
43
+ "@oh-my-pi/pi-agent-core": "6.1.0",
44
+ "@oh-my-pi/pi-ai": "6.1.0",
45
+ "@oh-my-pi/pi-git-tool": "6.1.0",
46
+ "@oh-my-pi/pi-tui": "6.1.0",
48
47
  "@openai/agents": "^0.3.7",
49
48
  "@sinclair/typebox": "^0.34.46",
50
49
  "ajv": "^8.17.1",
@@ -191,14 +191,26 @@ if "__omp_prelude_loaded__" not in globals():
191
191
  limit: int = 1000,
192
192
  hidden: bool = False,
193
193
  sort_by_mtime: bool = False,
194
+ maxdepth: int | None = None,
195
+ mindepth: int | None = None,
194
196
  ) -> list[Path]:
195
- """Recursive glob find. Respects .gitignore."""
196
- p = Path(path)
197
+ """Recursive glob find. Respects .gitignore.
198
+
199
+ maxdepth/mindepth are relative to path (0 = path itself, 1 = direct children).
200
+ """
201
+ p = Path(path).resolve()
202
+ base_depth = len(p.parts)
197
203
  ignore_patterns = _load_gitignore_patterns(p)
198
204
  matches: list[Path] = []
199
205
  for m in p.rglob(pattern):
200
206
  if len(matches) >= limit:
201
207
  break
208
+ # Check depth constraints
209
+ rel_depth = len(m.resolve().parts) - base_depth
210
+ if maxdepth is not None and rel_depth > maxdepth:
211
+ continue
212
+ if mindepth is not None and rel_depth < mindepth:
213
+ continue
202
214
  # Skip hidden files unless requested
203
215
  if not hidden and any(part.startswith(".") for part in m.parts):
204
216
  continue
@@ -485,6 +497,30 @@ if "__omp_prelude_loaded__" not in globals():
485
497
  return groups
486
498
  return "\n".join(line for _, line in groups)
487
499
 
500
+ @_category("Text")
501
+ def counter(
502
+ items: str | list,
503
+ *,
504
+ limit: int | None = None,
505
+ reverse: bool = True,
506
+ ) -> list[tuple[int, str]]:
507
+ """Count occurrences and sort by frequency. Like sort | uniq -c | sort -rn.
508
+
509
+ items: text (splits into lines) or list of strings
510
+ reverse: True for descending (most common first), False for ascending
511
+ Returns: [(count, item), ...] sorted by count
512
+ """
513
+ from collections import Counter
514
+ if isinstance(items, str):
515
+ items = items.splitlines()
516
+ counts = Counter(items)
517
+ sorted_items = sorted(counts.items(), key=lambda x: (x[1], x[0]), reverse=reverse)
518
+ if limit is not None:
519
+ sorted_items = sorted_items[:limit]
520
+ result = [(count, item) for item, count in sorted_items]
521
+ _emit_status("counter", unique=len(counts), total=sum(counts.values()), top=result[:10])
522
+ return result
523
+
488
524
  @_category("Text")
489
525
  def cols(text: str, *indices: int, sep: str | None = None) -> str:
490
526
  """Extract columns from text (0-indexed). Like cut."""
@@ -497,6 +533,13 @@ if "__omp_prelude_loaded__" not in globals():
497
533
  _emit_status("cols", lines=len(result_lines), columns=list(indices))
498
534
  return out
499
535
 
536
+ @_category("Navigation")
537
+ def basenames(paths: list[str | Path]) -> list[str]:
538
+ """Extract basename from each path. Like: sed 's|.*/||'."""
539
+ names = [Path(p).name for p in paths]
540
+ _emit_status("basenames", count=len(names), sample=names[:10])
541
+ return names
542
+
500
543
  @_category("Navigation")
501
544
  def tree(path: str | Path = ".", *, max_depth: int = 3, show_hidden: bool = False) -> str:
502
545
  """Return directory tree."""
@@ -1,6 +1,7 @@
1
1
  import * as fs from "node:fs";
2
2
  import { logger } from "../../logger";
3
3
  import { applyWorkspaceEdit } from "./edits";
4
+ import { getLspmuxCommand, isLspmuxSupported } from "./lspmux";
4
5
  import type {
5
6
  Diagnostic,
6
7
  LspClient,
@@ -400,13 +401,20 @@ export async function getOrCreateClient(config: ServerConfig, cwd: string, initT
400
401
 
401
402
  // Create new client with lock
402
403
  const clientPromise = (async () => {
403
- const args = config.args ?? [];
404
- const command = config.resolvedCommand ?? config.command;
404
+ const baseCommand = config.resolvedCommand ?? config.command;
405
+ const baseArgs = config.args ?? [];
406
+
407
+ // Wrap with lspmux if available and supported
408
+ const { command, args, env } = isLspmuxSupported(baseCommand)
409
+ ? await getLspmuxCommand(baseCommand, baseArgs)
410
+ : { command: baseCommand, args: baseArgs };
411
+
405
412
  const proc = Bun.spawn([command, ...args], {
406
413
  cwd,
407
414
  stdin: "pipe",
408
415
  stdout: "pipe",
409
416
  stderr: "pipe",
417
+ env: env ? { ...process.env, ...env } : undefined,
410
418
  });
411
419
 
412
420
  const client: LspClient = {
@@ -4,17 +4,8 @@
4
4
  "args": [],
5
5
  "fileTypes": [".rs"],
6
6
  "rootMarkers": ["Cargo.toml", "rust-analyzer.toml"],
7
- "initOptions": {
8
- "checkOnSave": { "command": "clippy" },
9
- "cargo": { "allFeatures": true },
10
- "procMacro": { "enable": true }
11
- },
12
- "settings": {
13
- "rust-analyzer": {
14
- "diagnostics": { "enable": true },
15
- "inlayHints": { "enable": true }
16
- }
17
- },
7
+ "initOptions": {},
8
+ "settings": {},
18
9
  "capabilities": {
19
10
  "flycheck": true,
20
11
  "ssr": true,
@@ -25,6 +25,7 @@ import {
25
25
  import { getLinterClient } from "./clients";
26
26
  import { getServersForFile, hasCapability, type LspConfig, loadConfig } from "./config";
27
27
  import { applyTextEditsToString, applyWorkspaceEdit } from "./edits";
28
+ import { detectLspmux } from "./lspmux";
28
29
  import { renderCall, renderResult } from "./render";
29
30
  import * as rustAnalyzer from "./rust-analyzer";
30
31
  import {
@@ -798,10 +799,19 @@ export function createLspTool(session: ToolSession): AgentTool<typeof lspSchema,
798
799
  // Status action doesn't need a file
799
800
  if (action === "status") {
800
801
  const servers = Object.keys(config.servers);
801
- const output =
802
+ const lspmuxState = await detectLspmux();
803
+ const lspmuxStatus = lspmuxState.available
804
+ ? lspmuxState.running
805
+ ? "lspmux: active (multiplexing enabled)"
806
+ : "lspmux: installed but server not running"
807
+ : "";
808
+
809
+ const serverStatus =
802
810
  servers.length > 0
803
811
  ? `Active language servers: ${servers.join(", ")}`
804
812
  : "No language servers configured for this project";
813
+
814
+ const output = lspmuxStatus ? `${serverStatus}\n${lspmuxStatus}` : serverStatus;
805
815
  return {
806
816
  content: [{ type: "text", text: output }],
807
817
  details: { action, success: true },