@oh-my-pi/pi-coding-agent 9.0.0 → 9.1.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oh-my-pi/pi-coding-agent",
3
- "version": "9.0.0",
3
+ "version": "9.1.1",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "ompConfig": {
@@ -79,12 +79,12 @@
79
79
  "test": "bun test"
80
80
  },
81
81
  "dependencies": {
82
- "@oh-my-pi/omp-stats": "9.0.0",
83
- "@oh-my-pi/pi-agent-core": "9.0.0",
84
- "@oh-my-pi/pi-ai": "9.0.0",
85
- "@oh-my-pi/pi-natives": "9.0.0",
86
- "@oh-my-pi/pi-tui": "9.0.0",
87
- "@oh-my-pi/pi-utils": "9.0.0",
82
+ "@oh-my-pi/omp-stats": "9.1.1",
83
+ "@oh-my-pi/pi-agent-core": "9.1.1",
84
+ "@oh-my-pi/pi-ai": "9.1.1",
85
+ "@oh-my-pi/pi-natives": "9.1.1",
86
+ "@oh-my-pi/pi-tui": "9.1.1",
87
+ "@oh-my-pi/pi-utils": "9.1.1",
88
88
  "@openai/agents": "^0.4.4",
89
89
  "@sinclair/typebox": "^0.34.48",
90
90
  "ajv": "^8.17.1",
@@ -5,6 +5,7 @@ import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
5
5
  import { type Api, type KnownProvider, type Model, modelsAreEqual } from "@oh-my-pi/pi-ai";
6
6
  import chalk from "chalk";
7
7
  import { isValidThinkingLevel } from "../cli/args";
8
+ import { fuzzyMatch } from "../utils/fuzzy";
8
9
  import type { ModelRegistry } from "./model-registry";
9
10
 
10
11
  /** Default model IDs for each known provider */
@@ -163,6 +164,32 @@ function tryMatchModel(
163
164
  if (providerMatch) {
164
165
  return providerMatch;
165
166
  }
167
+
168
+ const providerModels = availableModels.filter(m => m.provider.toLowerCase() === provider.toLowerCase());
169
+ if (providerModels.length > 0) {
170
+ const scored = providerModels
171
+ .map(model => ({ model, match: fuzzyMatch(modelId, model.id) }))
172
+ .filter(entry => entry.match.matches);
173
+ if (scored.length > 0) {
174
+ scored.sort((a, b) => {
175
+ if (a.match.score !== b.match.score) return a.match.score - b.match.score;
176
+ const aKey = formatModelString(a.model);
177
+ const bKey = formatModelString(b.model);
178
+ const aUsage = context.modelUsageRank.get(aKey) ?? Number.POSITIVE_INFINITY;
179
+ const bUsage = context.modelUsageRank.get(bKey) ?? Number.POSITIVE_INFINITY;
180
+ if (aUsage !== bUsage) return aUsage - bUsage;
181
+
182
+ const aProviderUsage = context.providerUsageRank.get(a.model.provider) ?? Number.POSITIVE_INFINITY;
183
+ const bProviderUsage = context.providerUsageRank.get(b.model.provider) ?? Number.POSITIVE_INFINITY;
184
+ if (aProviderUsage !== bProviderUsage) return aProviderUsage - bProviderUsage;
185
+
186
+ const aOrder = context.modelOrder.get(aKey) ?? 0;
187
+ const bOrder = context.modelOrder.get(bKey) ?? 0;
188
+ return aOrder - bOrder;
189
+ });
190
+ return scored[0]?.model;
191
+ }
192
+ }
166
193
  // No exact provider/model match - fall through to other matching
167
194
  }
168
195
 
@@ -3,6 +3,7 @@
3
3
  *
4
4
  * Provides tools for debugging, bug report generation, and system diagnostics.
5
5
  */
6
+ import * as fs from "node:fs/promises";
6
7
  import { Container, Loader, type SelectItem, SelectList, Spacer, Text } from "@oh-my-pi/pi-tui";
7
8
  import { getSessionsDir } from "../config";
8
9
  import { DynamicBorder } from "../modes/components/dynamic-border";
@@ -14,6 +15,7 @@ import { collectSystemInfo, formatSystemInfo } from "./system-info";
14
15
 
15
16
  /** Debug menu options */
16
17
  const DEBUG_MENU_ITEMS: SelectItem[] = [
18
+ { value: "open-artifacts", label: "Open: artifact folder", description: "Open session artifacts in file manager" },
17
19
  { value: "performance", label: "Report: performance issue", description: "Profile CPU, reproduce, then bundle" },
18
20
  { value: "dump", label: "Report: dump session", description: "Create report bundle immediately" },
19
21
  { value: "memory", label: "Report: memory issue", description: "Heap snapshot + bundle" },
@@ -40,7 +42,7 @@ export class DebugSelectorComponent extends Container {
40
42
  this.addChild(new Spacer(1));
41
43
 
42
44
  // Select list
43
- this.selectList = new SelectList(DEBUG_MENU_ITEMS, 6, getSelectListTheme());
45
+ this.selectList = new SelectList(DEBUG_MENU_ITEMS, 7, getSelectListTheme());
44
46
 
45
47
  this.selectList.onSelect = item => {
46
48
  onDone();
@@ -61,6 +63,9 @@ export class DebugSelectorComponent extends Container {
61
63
 
62
64
  private async handleSelection(value: string): Promise<void> {
63
65
  switch (value) {
66
+ case "open-artifacts":
67
+ await this.handleOpenArtifacts();
68
+ break;
64
69
  case "performance":
65
70
  await this.handlePerformanceReport();
66
71
  break;
@@ -276,6 +281,42 @@ export class DebugSelectorComponent extends Container {
276
281
  this.ctx.ui.requestRender();
277
282
  }
278
283
 
284
+ private async handleOpenArtifacts(): Promise<void> {
285
+ const sessionFile = this.ctx.sessionManager.getSessionFile();
286
+ if (!sessionFile) {
287
+ this.ctx.showWarning("No active session file.");
288
+ return;
289
+ }
290
+
291
+ const artifactsDir = sessionFile.slice(0, -6);
292
+
293
+ try {
294
+ const stat = await fs.stat(artifactsDir);
295
+ if (!stat.isDirectory()) {
296
+ this.ctx.showWarning("Artifact folder does not exist yet.");
297
+ return;
298
+ }
299
+ } catch {
300
+ this.ctx.showWarning("Artifact folder does not exist yet.");
301
+ return;
302
+ }
303
+
304
+ const openArgs =
305
+ process.platform === "darwin"
306
+ ? ["open", artifactsDir]
307
+ : process.platform === "win32"
308
+ ? ["cmd", "/c", "start", "", artifactsDir]
309
+ : ["xdg-open", artifactsDir];
310
+ const [cmd, ...args] = openArgs;
311
+
312
+ try {
313
+ Bun.spawn([cmd, ...args], { stdout: "ignore", stderr: "ignore" }).unref();
314
+ this.ctx.showStatus(`Opened: ${artifactsDir}`);
315
+ } catch (err) {
316
+ this.ctx.showError(`Failed to open artifact folder: ${err instanceof Error ? err.message : String(err)}`);
317
+ }
318
+ }
319
+
279
320
  private async handleClearCache(): Promise<void> {
280
321
  const sessionsDir = getSessionsDir();
281
322
 
@@ -1,5 +1,5 @@
1
1
  import type { AssistantMessage } from "@oh-my-pi/pi-ai";
2
- import { Container, getCapabilities, Markdown, Spacer, Text } from "@oh-my-pi/pi-tui";
2
+ import { Container, Markdown, Spacer, TERMINAL_INFO, Text } from "@oh-my-pi/pi-tui";
3
3
  import { hasPendingMermaid, prerenderMermaid } from "../../modes/theme/mermaid-cache";
4
4
  import { getMarkdownTheme, theme } from "../../modes/theme/theme";
5
5
 
@@ -38,8 +38,7 @@ export class AssistantMessageComponent extends Container {
38
38
  }
39
39
 
40
40
  private triggerMermaidPrerender(message: AssistantMessage): void {
41
- const caps = getCapabilities();
42
- if (!caps.images || this.prerenderInFlight) return;
41
+ if (!TERMINAL_INFO.imageProtocol || this.prerenderInFlight) return;
43
42
 
44
43
  // Check if any text content has pending mermaid blocks
45
44
  const hasPending = message.content.some(c => c.type === "text" && c.text.trim() && hasPendingMermaid(c.text));
@@ -8,7 +8,7 @@
8
8
  * 3. Add the handler in interactive-mode.ts settingsHandlers
9
9
  */
10
10
  import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
11
- import { getCapabilities } from "@oh-my-pi/pi-tui";
11
+ import { TERMINAL_INFO } from "@oh-my-pi/pi-tui";
12
12
  import type {
13
13
  ImageProviderOption,
14
14
  NotificationMethod,
@@ -485,7 +485,7 @@ export const SETTINGS_DEFS: SettingDef[] = [
485
485
  description: "Render images inline in terminal",
486
486
  get: sm => sm.getShowImages(),
487
487
  set: (sm, v) => sm.setShowImages(v),
488
- condition: () => !!getCapabilities().images,
488
+ condition: () => !!TERMINAL_INFO.imageProtocol,
489
489
  },
490
490
  {
491
491
  id: "autoResizeImages",
@@ -3,14 +3,15 @@ import {
3
3
  Box,
4
4
  type Component,
5
5
  Container,
6
- getCapabilities,
7
6
  getImageDimensions,
8
7
  Image,
9
8
  imageFallback,
10
9
  Spacer,
10
+ TERMINAL_INFO,
11
11
  Text,
12
12
  type TUI,
13
13
  } from "@oh-my-pi/pi-tui";
14
+ import { ImageProtocol } from "@oh-my-pi/pi-tui/terminal-image";
14
15
  import { sanitizeText } from "@oh-my-pi/pi-utils";
15
16
  import type { Theme } from "../../modes/theme/theme";
16
17
  import { theme } from "../../modes/theme/theme";
@@ -288,9 +289,8 @@ export class ToolExecutionComponent extends Container {
288
289
  * Kitty requires PNG format (f=100), so JPEG/GIF/WebP won't display.
289
290
  */
290
291
  private maybeConvertImagesForKitty(): void {
291
- const caps = getCapabilities();
292
292
  // Only needed for Kitty protocol
293
- if (caps.images !== "kitty") return;
293
+ if (TERMINAL_INFO.imageProtocol !== ImageProtocol.Kitty) return;
294
294
  if (!this.result) return;
295
295
 
296
296
  const imageBlocks = this.getAllImageBlocks();
@@ -520,18 +520,17 @@ export class ToolExecutionComponent extends Container {
520
520
 
521
521
  if (this.result) {
522
522
  const imageBlocks = this.getAllImageBlocks();
523
- const caps = getCapabilities();
524
523
 
525
524
  for (let i = 0; i < imageBlocks.length; i++) {
526
525
  const img = imageBlocks[i];
527
- if (caps.images && this.showImages && img.data && img.mimeType) {
526
+ if (TERMINAL_INFO.imageProtocol && this.showImages && img.data && img.mimeType) {
528
527
  // Use converted PNG for Kitty protocol if available
529
528
  const converted = this.convertedImages.get(i);
530
529
  const imageData = converted?.data ?? img.data;
531
530
  const imageMimeType = converted?.mimeType ?? img.mimeType;
532
531
 
533
532
  // For Kitty, skip non-PNG images that haven't been converted yet
534
- if (caps.images === "kitty" && imageMimeType !== "image/png") {
533
+ if (TERMINAL_INFO.imageProtocol === ImageProtocol.Kitty && imageMimeType !== "image/png") {
535
534
  continue;
536
535
  }
537
536
 
@@ -595,8 +594,7 @@ export class ToolExecutionComponent extends Container {
595
594
  })
596
595
  .join("\n");
597
596
 
598
- const caps = getCapabilities();
599
- if (imageBlocks.length > 0 && (!caps.images || !this.showImages)) {
597
+ if (imageBlocks.length > 0 && (!TERMINAL_INFO.imageProtocol || !this.showImages)) {
600
598
  const imageIndicators = imageBlocks
601
599
  .map((img: any) => {
602
600
  const dims = img.data ? (getImageDimensions(img.data, img.mimeType) ?? undefined) : undefined;