From 03b061773c35d69d01fdb3ae5a566b24d6c10c77 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Wed, 17 Dec 2025 00:39:14 +0100 Subject: [PATCH] Release v0.22.4 - Add --list-models CLI flag for listing/finding models with fuzzy search fixes #203 --- package-lock.json | 40 +++---- packages/agent/package.json | 6 +- packages/ai/package.json | 2 +- packages/coding-agent/CHANGELOG.md | 6 +- packages/coding-agent/package.json | 8 +- packages/coding-agent/src/cli/args.ts | 9 ++ packages/coding-agent/src/cli/list-models.ts | 109 +++++++++++++++++++ packages/coding-agent/src/main.ts | 8 ++ packages/mom/package.json | 8 +- packages/pods/package.json | 4 +- packages/proxy/package.json | 2 +- packages/tui/package.json | 2 +- packages/web-ui/example/package.json | 2 +- packages/web-ui/package.json | 6 +- 14 files changed, 171 insertions(+), 41 deletions(-) create mode 100644 packages/coding-agent/src/cli/list-models.ts diff --git a/package-lock.json b/package-lock.json index 59490812..cce50150 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6063,11 +6063,11 @@ }, "packages/agent": { "name": "@mariozechner/pi-agent-core", - "version": "0.22.3", + "version": "0.22.4", "license": "MIT", "dependencies": { - "@mariozechner/pi-ai": "^0.22.3", - "@mariozechner/pi-tui": "^0.22.3" + "@mariozechner/pi-ai": "^0.22.4", + "@mariozechner/pi-tui": "^0.22.4" }, "devDependencies": { "@types/node": "^24.3.0", @@ -6097,7 +6097,7 @@ }, "packages/ai": { "name": "@mariozechner/pi-ai", - "version": "0.22.3", + "version": "0.22.4", "license": "MIT", "dependencies": { "@anthropic-ai/sdk": "0.71.2", @@ -6139,12 +6139,12 @@ }, "packages/coding-agent": { "name": "@mariozechner/pi-coding-agent", - "version": "0.22.3", + "version": "0.22.4", "license": "MIT", "dependencies": { - "@mariozechner/pi-agent-core": "^0.22.3", - "@mariozechner/pi-ai": "^0.22.3", - "@mariozechner/pi-tui": "^0.22.3", + "@mariozechner/pi-agent-core": "^0.22.4", + "@mariozechner/pi-ai": "^0.22.4", + "@mariozechner/pi-tui": "^0.22.4", "chalk": "^5.5.0", "diff": "^8.0.2", "glob": "^11.0.3", @@ -6182,13 +6182,13 @@ }, "packages/mom": { "name": "@mariozechner/pi-mom", - "version": "0.22.3", + "version": "0.22.4", "license": "MIT", "dependencies": { "@anthropic-ai/sandbox-runtime": "^0.0.16", - "@mariozechner/pi-agent-core": "^0.22.3", - "@mariozechner/pi-ai": "^0.22.3", - "@mariozechner/pi-coding-agent": "^0.22.3", + "@mariozechner/pi-agent-core": "^0.22.4", + "@mariozechner/pi-ai": "^0.22.4", + "@mariozechner/pi-coding-agent": "^0.22.4", "@sinclair/typebox": "^0.34.0", "@slack/socket-mode": "^2.0.0", "@slack/web-api": "^7.0.0", @@ -6227,10 +6227,10 @@ }, "packages/pods": { "name": "@mariozechner/pi", - "version": "0.22.3", + "version": "0.22.4", "license": "MIT", "dependencies": { - "@mariozechner/pi-agent-core": "^0.22.3", + "@mariozechner/pi-agent-core": "^0.22.4", "chalk": "^5.5.0" }, "bin": { @@ -6243,7 +6243,7 @@ }, "packages/proxy": { "name": "@mariozechner/pi-proxy", - "version": "0.22.3", + "version": "0.22.4", "dependencies": { "@hono/node-server": "^1.14.0", "hono": "^4.6.16" @@ -6259,7 +6259,7 @@ }, "packages/tui": { "name": "@mariozechner/pi-tui", - "version": "0.22.3", + "version": "0.22.4", "license": "MIT", "dependencies": { "@types/mime-types": "^2.1.4", @@ -6303,12 +6303,12 @@ }, "packages/web-ui": { "name": "@mariozechner/pi-web-ui", - "version": "0.22.3", + "version": "0.22.4", "license": "MIT", "dependencies": { "@lmstudio/sdk": "^1.5.0", - "@mariozechner/pi-ai": "^0.22.3", - "@mariozechner/pi-tui": "^0.22.3", + "@mariozechner/pi-ai": "^0.22.4", + "@mariozechner/pi-tui": "^0.22.4", "docx-preview": "^0.3.7", "jszip": "^3.10.1", "lucide": "^0.544.0", @@ -6329,7 +6329,7 @@ }, "packages/web-ui/example": { "name": "pi-web-ui-example", - "version": "1.10.3", + "version": "1.10.4", "dependencies": { "@mariozechner/mini-lit": "^0.2.0", "@mariozechner/pi-ai": "file:../../ai", diff --git a/packages/agent/package.json b/packages/agent/package.json index c8c47384..9f9fb5a7 100644 --- a/packages/agent/package.json +++ b/packages/agent/package.json @@ -1,6 +1,6 @@ { "name": "@mariozechner/pi-agent-core", - "version": "0.22.3", + "version": "0.22.4", "description": "General-purpose agent with transport abstraction, state management, and attachment support", "type": "module", "main": "./dist/index.js", @@ -18,8 +18,8 @@ "prepublishOnly": "npm run clean && npm run build" }, "dependencies": { - "@mariozechner/pi-ai": "^0.22.3", - "@mariozechner/pi-tui": "^0.22.3" + "@mariozechner/pi-ai": "^0.22.4", + "@mariozechner/pi-tui": "^0.22.4" }, "keywords": [ "ai", diff --git a/packages/ai/package.json b/packages/ai/package.json index 9f880501..505f54ec 100644 --- a/packages/ai/package.json +++ b/packages/ai/package.json @@ -1,6 +1,6 @@ { "name": "@mariozechner/pi-ai", - "version": "0.22.3", + "version": "0.22.4", "description": "Unified LLM API with automatic model discovery and provider configuration", "type": "module", "main": "./dist/index.js", diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index e041c237..536fa68b 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -1,6 +1,10 @@ # Changelog -## [Unreleased] +## [0.22.4] - 2025-12-17 + +### Added + +- `--list-models [search]` CLI flag to list available models with optional fuzzy search. Shows provider, model ID, context window, max output, thinking support, and image support. Only lists models with configured API keys. ([#203](https://github.com/badlogic/pi-mono/issues/203)) ### Fixed diff --git a/packages/coding-agent/package.json b/packages/coding-agent/package.json index 81a60be3..39d7b494 100644 --- a/packages/coding-agent/package.json +++ b/packages/coding-agent/package.json @@ -1,6 +1,6 @@ { "name": "@mariozechner/pi-coding-agent", - "version": "0.22.3", + "version": "0.22.4", "description": "Coding agent CLI with read, bash, edit, write tools and session management", "type": "module", "piConfig": { @@ -39,9 +39,9 @@ "prepublishOnly": "npm run clean && npm run build" }, "dependencies": { - "@mariozechner/pi-agent-core": "^0.22.3", - "@mariozechner/pi-ai": "^0.22.3", - "@mariozechner/pi-tui": "^0.22.3", + "@mariozechner/pi-agent-core": "^0.22.4", + "@mariozechner/pi-ai": "^0.22.4", + "@mariozechner/pi-tui": "^0.22.4", "chalk": "^5.5.0", "diff": "^8.0.2", "glob": "^11.0.3", diff --git a/packages/coding-agent/src/cli/args.ts b/packages/coding-agent/src/cli/args.ts index 4f7de220..e6a5d018 100644 --- a/packages/coding-agent/src/cli/args.ts +++ b/packages/coding-agent/src/cli/args.ts @@ -29,6 +29,7 @@ export interface Args { print?: boolean; export?: string; noSkills?: boolean; + listModels?: string | true; messages: string[]; fileArgs: string[]; } @@ -110,6 +111,13 @@ export function parseArgs(args: string[]): Args { result.hooks.push(args[++i]); } else if (arg === "--no-skills") { result.noSkills = true; + } else if (arg === "--list-models") { + // Check if next arg is a search pattern (not a flag or file arg) + if (i + 1 < args.length && !args[i + 1].startsWith("-") && !args[i + 1].startsWith("@")) { + result.listModels = args[++i]; + } else { + result.listModels = true; + } } else if (arg.startsWith("@")) { result.fileArgs.push(arg.slice(1)); // Remove @ prefix } else if (!arg.startsWith("-")) { @@ -145,6 +153,7 @@ ${chalk.bold("Options:")} --hook Load a hook file (can be used multiple times) --no-skills Disable skills discovery and loading --export Export session file to HTML and exit + --list-models [search] List available models (with optional fuzzy search) --help, -h Show this help --version, -v Show version number diff --git a/packages/coding-agent/src/cli/list-models.ts b/packages/coding-agent/src/cli/list-models.ts new file mode 100644 index 00000000..c677f828 --- /dev/null +++ b/packages/coding-agent/src/cli/list-models.ts @@ -0,0 +1,109 @@ +/** + * List available models with optional fuzzy search + */ + +import type { Api, Model } from "@mariozechner/pi-ai"; +import { getAvailableModels } from "../core/model-config.js"; +import { fuzzyFilter } from "../utils/fuzzy.js"; + +/** + * Format a number as human-readable (e.g., 200000 -> "200K", 1000000 -> "1M") + */ +function formatTokenCount(count: number): string { + if (count >= 1_000_000) { + const millions = count / 1_000_000; + return millions % 1 === 0 ? `${millions}M` : `${millions.toFixed(1)}M`; + } + if (count >= 1_000) { + const thousands = count / 1_000; + return thousands % 1 === 0 ? `${thousands}K` : `${thousands.toFixed(1)}K`; + } + return count.toString(); +} + +/** + * List available models, optionally filtered by search pattern + */ +export async function listModels(searchPattern?: string): Promise { + const { models, error } = await getAvailableModels(); + + if (error) { + console.error(error); + process.exit(1); + } + + if (models.length === 0) { + console.log("No models available. Set API keys in environment variables."); + return; + } + + // Apply fuzzy filter if search pattern provided + let filteredModels: Model[] = models; + if (searchPattern) { + filteredModels = fuzzyFilter(models, searchPattern, (m) => `${m.provider} ${m.id}`); + } + + if (filteredModels.length === 0) { + console.log(`No models matching "${searchPattern}"`); + return; + } + + // Sort by provider, then by model id + filteredModels.sort((a, b) => { + const providerCmp = a.provider.localeCompare(b.provider); + if (providerCmp !== 0) return providerCmp; + return a.id.localeCompare(b.id); + }); + + // Calculate column widths + const rows = filteredModels.map((m) => ({ + provider: m.provider, + model: m.id, + context: formatTokenCount(m.contextWindow), + maxOut: formatTokenCount(m.maxTokens), + thinking: m.reasoning ? "yes" : "no", + images: m.input.includes("image") ? "yes" : "no", + })); + + const headers = { + provider: "provider", + model: "model", + context: "context", + maxOut: "max-out", + thinking: "thinking", + images: "images", + }; + + const widths = { + provider: Math.max(headers.provider.length, ...rows.map((r) => r.provider.length)), + model: Math.max(headers.model.length, ...rows.map((r) => r.model.length)), + context: Math.max(headers.context.length, ...rows.map((r) => r.context.length)), + maxOut: Math.max(headers.maxOut.length, ...rows.map((r) => r.maxOut.length)), + thinking: Math.max(headers.thinking.length, ...rows.map((r) => r.thinking.length)), + images: Math.max(headers.images.length, ...rows.map((r) => r.images.length)), + }; + + // Print header + const headerLine = [ + headers.provider.padEnd(widths.provider), + headers.model.padEnd(widths.model), + headers.context.padEnd(widths.context), + headers.maxOut.padEnd(widths.maxOut), + headers.thinking.padEnd(widths.thinking), + headers.images.padEnd(widths.images), + ].join(" "); + console.log(headerLine); + + // Print rows + for (const row of rows) { + const line = [ + row.provider.padEnd(widths.provider), + row.model.padEnd(widths.model), + row.context.padEnd(widths.context), + row.maxOut.padEnd(widths.maxOut), + row.thinking.padEnd(widths.thinking), + row.images.padEnd(widths.images), + ].join(" "); + console.log(line); + } +} diff --git a/packages/coding-agent/src/main.ts b/packages/coding-agent/src/main.ts index 4aab1fcc..0d08ebc1 100644 --- a/packages/coding-agent/src/main.ts +++ b/packages/coding-agent/src/main.ts @@ -6,6 +6,7 @@ import { Agent, type Attachment, ProviderTransport, type ThinkingLevel } from "@ import chalk from "chalk"; import { type Args, parseArgs, printHelp } from "./cli/args.js"; import { processFileArguments } from "./cli/file-processor.js"; +import { listModels } from "./cli/list-models.js"; import { selectSession } from "./cli/session-picker.js"; import { getModelsPath, VERSION } from "./config.js"; import { AgentSession } from "./core/agent-session.js"; @@ -149,6 +150,13 @@ export async function main(args: string[]) { return; } + // Handle --list-models flag: list available models and exit + if (parsed.listModels !== undefined) { + const searchPattern = typeof parsed.listModels === "string" ? parsed.listModels : undefined; + await listModels(searchPattern); + return; + } + // Handle --export flag: convert session file to HTML and exit if (parsed.export) { try { diff --git a/packages/mom/package.json b/packages/mom/package.json index 7dbdf5d9..fe7c505f 100644 --- a/packages/mom/package.json +++ b/packages/mom/package.json @@ -1,6 +1,6 @@ { "name": "@mariozechner/pi-mom", - "version": "0.22.3", + "version": "0.22.4", "description": "Slack bot that delegates messages to the pi coding agent", "type": "module", "bin": { @@ -21,9 +21,9 @@ }, "dependencies": { "@anthropic-ai/sandbox-runtime": "^0.0.16", - "@mariozechner/pi-agent-core": "^0.22.3", - "@mariozechner/pi-ai": "^0.22.3", - "@mariozechner/pi-coding-agent": "^0.22.3", + "@mariozechner/pi-agent-core": "^0.22.4", + "@mariozechner/pi-ai": "^0.22.4", + "@mariozechner/pi-coding-agent": "^0.22.4", "@sinclair/typebox": "^0.34.0", "@slack/socket-mode": "^2.0.0", "@slack/web-api": "^7.0.0", diff --git a/packages/pods/package.json b/packages/pods/package.json index 40d77d5a..c0793bb3 100644 --- a/packages/pods/package.json +++ b/packages/pods/package.json @@ -1,6 +1,6 @@ { "name": "@mariozechner/pi", - "version": "0.22.3", + "version": "0.22.4", "description": "CLI tool for managing vLLM deployments on GPU pods", "type": "module", "bin": { @@ -34,7 +34,7 @@ "node": ">=20.0.0" }, "dependencies": { - "@mariozechner/pi-agent-core": "^0.22.3", + "@mariozechner/pi-agent-core": "^0.22.4", "chalk": "^5.5.0" }, "devDependencies": {} diff --git a/packages/proxy/package.json b/packages/proxy/package.json index 313b8be7..27045d20 100644 --- a/packages/proxy/package.json +++ b/packages/proxy/package.json @@ -1,6 +1,6 @@ { "name": "@mariozechner/pi-proxy", - "version": "0.22.3", + "version": "0.22.4", "type": "module", "description": "CORS and authentication proxy for pi-ai", "main": "dist/index.js", diff --git a/packages/tui/package.json b/packages/tui/package.json index 324d0b0e..8a83579d 100644 --- a/packages/tui/package.json +++ b/packages/tui/package.json @@ -1,6 +1,6 @@ { "name": "@mariozechner/pi-tui", - "version": "0.22.3", + "version": "0.22.4", "description": "Terminal User Interface library with differential rendering for efficient text-based applications", "type": "module", "main": "dist/index.js", diff --git a/packages/web-ui/example/package.json b/packages/web-ui/example/package.json index 82c2748d..c4614c12 100644 --- a/packages/web-ui/example/package.json +++ b/packages/web-ui/example/package.json @@ -1,6 +1,6 @@ { "name": "pi-web-ui-example", - "version": "1.10.3", + "version": "1.10.4", "private": true, "type": "module", "scripts": { diff --git a/packages/web-ui/package.json b/packages/web-ui/package.json index f8839176..ebc7b557 100644 --- a/packages/web-ui/package.json +++ b/packages/web-ui/package.json @@ -1,6 +1,6 @@ { "name": "@mariozechner/pi-web-ui", - "version": "0.22.3", + "version": "0.22.4", "description": "Reusable web UI components for AI chat interfaces powered by @mariozechner/pi-ai", "type": "module", "main": "dist/index.js", @@ -18,8 +18,8 @@ }, "dependencies": { "@lmstudio/sdk": "^1.5.0", - "@mariozechner/pi-ai": "^0.22.3", - "@mariozechner/pi-tui": "^0.22.3", + "@mariozechner/pi-ai": "^0.22.4", + "@mariozechner/pi-tui": "^0.22.4", "docx-preview": "^0.3.7", "jszip": "^3.10.1", "lucide": "^0.544.0",