@oh-my-pi/pi-coding-agent 11.14.4 → 12.0.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,23 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [12.0.0] - 2026-02-12
6
+
7
+ ### Added
8
+
9
+ - Added `getAllServerNames()` method to MCPManager for enumerating all known servers
10
+
11
+ ### Changed
12
+
13
+ - Changed default edit mode from `patch` to `hashline` for more precise code modifications
14
+ - Changed `readHashLines` setting default from false to true to enable hash line reading by default
15
+
16
+ ### Fixed
17
+
18
+ - Fixed `omp setup` crashing with uncaught exception when no component argument provided; now shows help ([#35](https://github.com/can1357/oh-my-pi/issues/35))
19
+ - Fixed `/mcp list` showing "No MCP servers configured" when servers are loaded from discovery sources like `.claude.json`, `.cursor/mcp.json`, `.vscode/mcp.json` ([#34](https://github.com/can1357/oh-my-pi/issues/34))
20
+ - Fixed model selector sorting to show newest models first within each provider instead of alphabetical; `-latest` aliases now appear before dated versions ([#37](https://github.com/can1357/oh-my-pi/issues/37))
21
+
5
22
  ## [11.14.4] - 2026-02-12
6
23
 
7
24
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oh-my-pi/pi-coding-agent",
3
- "version": "11.14.4",
3
+ "version": "12.0.0",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "ompConfig": {
@@ -88,12 +88,12 @@
88
88
  },
89
89
  "dependencies": {
90
90
  "@mozilla/readability": "0.6.0",
91
- "@oh-my-pi/omp-stats": "11.14.4",
92
- "@oh-my-pi/pi-agent-core": "11.14.4",
93
- "@oh-my-pi/pi-ai": "11.14.4",
94
- "@oh-my-pi/pi-natives": "11.14.4",
95
- "@oh-my-pi/pi-tui": "11.14.4",
96
- "@oh-my-pi/pi-utils": "11.14.4",
91
+ "@oh-my-pi/omp-stats": "12.0.0",
92
+ "@oh-my-pi/pi-agent-core": "12.0.0",
93
+ "@oh-my-pi/pi-ai": "12.0.0",
94
+ "@oh-my-pi/pi-natives": "12.0.0",
95
+ "@oh-my-pi/pi-tui": "12.0.0",
96
+ "@oh-my-pi/pi-utils": "12.0.0",
97
97
  "@sinclair/typebox": "^0.34.48",
98
98
  "@xterm/headless": "^6.0.0",
99
99
  "ajv": "^8.17.1",
package/src/cli.ts CHANGED
@@ -6,6 +6,12 @@
6
6
  import { type CommandEntry, run } from "@oh-my-pi/pi-utils/cli";
7
7
  import { APP_NAME, VERSION } from "./config";
8
8
 
9
+ // Detect known Bun errata that cause TUI crashes (e.g. Bun.stringWidth mishandling OSC sequences).
10
+ if (Bun.stringWidth("\x1b[0m\x1b]8;;\x07") !== 0) {
11
+ process.stderr.write(`error: Bun runtime errata detected (v${Bun.version}). Please update Bun: bun upgrade\n`);
12
+ process.exit(1);
13
+ }
14
+
9
15
  process.title = APP_NAME;
10
16
 
11
17
  const commands: CommandEntry[] = [
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Install dependencies for optional features.
3
3
  */
4
- import { Args, Command, Flags } from "@oh-my-pi/pi-utils/cli";
4
+ import { Args, Command, Flags, renderCommandHelp } from "@oh-my-pi/pi-utils/cli";
5
5
  import { runSetupCommand, type SetupCommandArgs, type SetupComponent } from "../cli/setup-cli";
6
6
  import { initTheme } from "../modes/theme/theme";
7
7
 
@@ -13,7 +13,7 @@ export default class Setup extends Command {
13
13
  static args = {
14
14
  component: Args.string({
15
15
  description: "Component to install",
16
- required: true,
16
+ required: false,
17
17
  options: COMPONENTS,
18
18
  }),
19
19
  };
@@ -25,6 +25,10 @@ export default class Setup extends Command {
25
25
 
26
26
  async run(): Promise<void> {
27
27
  const { args, flags } = await this.parse(Setup);
28
+ if (!args.component) {
29
+ renderCommandHelp("omp", "setup", Setup);
30
+ return;
31
+ }
28
32
  const cmd: SetupCommandArgs = {
29
33
  component: args.component as SetupComponent,
30
34
  flags: {
@@ -32,7 +36,6 @@ export default class Setup extends Command {
32
36
  check: flags.check,
33
37
  },
34
38
  };
35
-
36
39
  await initTheme();
37
40
  await runSetupCommand(cmd);
38
41
  }
@@ -249,7 +249,7 @@ export const SETTINGS_SCHEMA = {
249
249
  },
250
250
  readHashLines: {
251
251
  type: "boolean",
252
- default: false,
252
+ default: true,
253
253
  ui: {
254
254
  tab: "config",
255
255
  label: "Read hash lines",
@@ -743,7 +743,7 @@ export const SETTINGS_SCHEMA = {
743
743
  "edit.mode": {
744
744
  type: "enum",
745
745
  values: ["replace", "patch", "hashline"] as const,
746
- default: "patch",
746
+ default: "hashline",
747
747
  ui: {
748
748
  tab: "config",
749
749
  label: "Edit mode",
@@ -343,6 +343,15 @@ export class MCPManager {
343
343
  return Array.from(this.#connections.keys());
344
344
  }
345
345
 
346
+ /**
347
+ * Get all known server names (connected, connecting, or discovered).
348
+ */
349
+ getAllServerNames(): string[] {
350
+ return Array.from(
351
+ new Set([...this.#sources.keys(), ...this.#connections.keys(), ...this.#pendingConnections.keys()]),
352
+ );
353
+ }
354
+
346
355
  /**
347
356
  * Disconnect from a specific server.
348
357
  */
@@ -203,6 +203,9 @@ export class ModelSelectorComponent extends Container {
203
203
  return i;
204
204
  };
205
205
 
206
+ const dateRe = /-(\d{8})$/;
207
+ const latestRe = /-latest$/;
208
+
206
209
  models.sort((a, b) => {
207
210
  const aKey = `${a.provider}/${a.id}`;
208
211
  const bKey = `${b.provider}/${b.id}`;
@@ -216,10 +219,33 @@ export class ModelSelectorComponent extends Container {
216
219
  const bMru = mruIndex.get(bKey) ?? Number.MAX_SAFE_INTEGER;
217
220
  if (aMru !== bMru) return aMru - bMru;
218
221
 
219
- // Finally alphabetical by provider, then id
222
+ // By provider, then recency within provider
220
223
  const providerCmp = a.provider.localeCompare(b.provider);
221
224
  if (providerCmp !== 0) return providerCmp;
222
- return a.id.localeCompare(b.id);
225
+
226
+ const aIsLatest = latestRe.test(a.id);
227
+ const bIsLatest = latestRe.test(b.id);
228
+ const aDate = a.id.match(dateRe)?.[1] ?? "";
229
+ const bDate = b.id.match(dateRe)?.[1] ?? "";
230
+
231
+ // Both have dates or latest tags — sort by recency
232
+ const aHasRecency = aIsLatest || aDate !== "";
233
+ const bHasRecency = bIsLatest || bDate !== "";
234
+
235
+ // Models with recency info come before those without
236
+ if (aHasRecency !== bHasRecency) return aHasRecency ? -1 : 1;
237
+
238
+ // If neither has recency info, fall back to alphabetical
239
+ if (!aHasRecency) return a.id.localeCompare(b.id);
240
+
241
+ // -latest always sorts first within recency group
242
+ if (aIsLatest !== bIsLatest) return aIsLatest ? -1 : 1;
243
+
244
+ // Both have dates — descending (newest first)
245
+ if (aDate && bDate) return bDate.localeCompare(aDate);
246
+
247
+ // One has date, other is latest — latest first
248
+ return aIsLatest ? -1 : bIsLatest ? 1 : a.id.localeCompare(b.id);
223
249
  });
224
250
  }
225
251
 
@@ -4,6 +4,7 @@
4
4
  * Handles /mcp subcommands for managing MCP servers.
5
5
  */
6
6
  import { Spacer, Text } from "@oh-my-pi/pi-tui";
7
+ import type { SourceMeta } from "../../capability/types";
7
8
  import { analyzeAuthError, discoverOAuthEndpoints, MCPManager } from "../../mcp";
8
9
  import { connectToServer, disconnectServer, listTools } from "../../mcp/client";
9
10
  import {
@@ -765,7 +766,20 @@ export class MCPCommandController {
765
766
  const userServers = Object.keys(userConfig.mcpServers ?? {});
766
767
  const projectServers = Object.keys(projectConfig.mcpServers ?? {});
767
768
 
768
- if (userServers.length === 0 && projectServers.length === 0) {
769
+ // Collect runtime-discovered servers not in config files
770
+ const configServerNames = new Set([...userServers, ...projectServers]);
771
+ const discoveredServers: { name: string; source: SourceMeta }[] = [];
772
+ if (this.ctx.mcpManager) {
773
+ for (const name of this.ctx.mcpManager.getAllServerNames()) {
774
+ if (configServerNames.has(name)) continue;
775
+ const source = this.ctx.mcpManager.getSource(name);
776
+ if (source) {
777
+ discoveredServers.push({ name, source });
778
+ }
779
+ }
780
+ }
781
+
782
+ if (userServers.length === 0 && projectServers.length === 0 && discoveredServers.length === 0) {
769
783
  this.#showMessage(
770
784
  [
771
785
  "",
@@ -826,6 +840,39 @@ export class MCPCommandController {
826
840
  lines.push("");
827
841
  }
828
842
 
843
+ // Show discovered servers (from .claude.json, .cursor/mcp.json, .vscode/mcp.json, etc.)
844
+ if (discoveredServers.length > 0) {
845
+ // Group by source display name + path
846
+ const bySource = new Map<string, typeof discoveredServers>();
847
+ for (const entry of discoveredServers) {
848
+ const key = `${entry.source.providerName}|${entry.source.path}`;
849
+ let group = bySource.get(key);
850
+ if (!group) {
851
+ group = [];
852
+ bySource.set(key, group);
853
+ }
854
+ group.push(entry);
855
+ }
856
+
857
+ for (const [key, entries] of bySource) {
858
+ const sepIdx = key.indexOf("|");
859
+ const providerName = key.slice(0, sepIdx);
860
+ const sourcePath = key.slice(sepIdx + 1);
861
+ const shortPath = sourcePath.replace(process.env.HOME ?? "", "~");
862
+ lines.push(theme.fg("accent", providerName) + theme.fg("muted", ` (${shortPath}):`));
863
+ for (const { name } of entries) {
864
+ const state = this.ctx.mcpManager!.getConnectionStatus(name);
865
+ const status =
866
+ state === "connected"
867
+ ? theme.fg("success", " ● connected")
868
+ : state === "connecting"
869
+ ? theme.fg("muted", " ◌ connecting")
870
+ : theme.fg("muted", " ○ not connected");
871
+ lines.push(` ${theme.fg("accent", name)}${status}`);
872
+ }
873
+ lines.push("");
874
+ }
875
+ }
829
876
  this.#showMessage(lines.join("\n"));
830
877
  } catch (error) {
831
878
  this.ctx.showError(`Failed to list servers: ${error instanceof Error ? error.message : String(error)}`);