From 7d721dc420c34b5e0001d27152634dc2c643b803 Mon Sep 17 00:00:00 2001 From: oldschoola Date: Mon, 15 Jun 2026 03:58:20 -0700 Subject: [PATCH] Add Umans AI Coding Plan provider --- docs/environment-variables.md | 1 + docs/providers.md | 1 + packages/ai/CHANGELOG.md | 4 + packages/ai/README.md | 3 + packages/ai/src/registry/registry.ts | 2 + packages/ai/src/registry/umans.ts | 23 ++ packages/ai/test/provider-registry.test.ts | 5 +- packages/catalog/CHANGELOG.md | 4 + packages/catalog/src/identity/priority.ts | 1 + packages/catalog/src/models.json | 201 ++++++++++++++++++ .../src/provider-models/descriptors.ts | 9 + .../src/provider-models/openai-compat.ts | 116 ++++++++++ packages/catalog/test/umans-provider.test.ts | 111 ++++++++++ packages/coding-agent/CHANGELOG.md | 4 + packages/coding-agent/src/cli/args.ts | 1 + 15 files changed, 485 insertions(+), 1 deletion(-) create mode 100644 packages/ai/src/registry/umans.ts create mode 100644 packages/catalog/test/umans-provider.test.ts diff --git a/docs/environment-variables.md b/docs/environment-variables.md index fd8c473f5..2becaac84 100644 --- a/docs/environment-variables.md +++ b/docs/environment-variables.md @@ -62,6 +62,7 @@ These are consumed via `getEnvApiKey()` (`packages/ai/src/stream.ts`) unless not | `MISTRAL_API_KEY` | Mistral auth | Using Mistral models | | | `ZAI_API_KEY` | z.ai auth | Using z.ai models | Also used by z.ai web search provider | | `ZHIPU_API_KEY` | Zhipu Coding Plan auth | Using `zhipu-coding-plan` provider | | +| `UMANS_AI_CODING_PLAN_API_KEY` | Umans AI Coding Plan auth | Using `umans` provider | | | `MINIMAX_API_KEY` | MiniMax auth | Using `minimax` provider | | | `MINIMAX_CODE_API_KEY` | MiniMax Code auth | Using `minimax-code` provider | | | `MINIMAX_CODE_CN_API_KEY` | MiniMax Code CN auth | Using `minimax-code-cn` provider | | diff --git a/docs/providers.md b/docs/providers.md index 75c747642..85c67c5ea 100644 --- a/docs/providers.md +++ b/docs/providers.md @@ -114,6 +114,7 @@ Each provider has one or more environment variables that supply a key when no st | `zai` | `ZAI_API_KEY` | | `zenmux` | `ZENMUX_API_KEY` | | `zhipu-coding-plan` | `ZHIPU_API_KEY` | +| `umans` | `UMANS_AI_CODING_PLAN_API_KEY` | | `qianfan` | `QIANFAN_API_KEY` | | `qwen-portal` | `QWEN_OAUTH_TOKEN`, then `QWEN_PORTAL_API_KEY` | | `synthetic` | `SYNTHETIC_API_KEY` | diff --git a/packages/ai/CHANGELOG.md b/packages/ai/CHANGELOG.md index 88f374b0b..6e443bf49 100644 --- a/packages/ai/CHANGELOG.md +++ b/packages/ai/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Added + +- Added Umans AI Coding Plan API-key login support and `UMANS_AI_CODING_PLAN_API_KEY` environment fallback. + ## [15.13.2] - 2026-06-15 ### Added diff --git a/packages/ai/README.md b/packages/ai/README.md index 6decf9cba..f485d9621 100644 --- a/packages/ai/README.md +++ b/packages/ai/README.md @@ -68,6 +68,7 @@ Unified LLM API with automatic model discovery, provider configuration, token an - **Kilo Gateway** (supports OAuth `/login kilo` or `KILO_API_KEY`) - **LiteLLM** (requires `LITELLM_API_KEY`) - **zAI** (requires `ZAI_API_KEY`) +- **Umans AI Coding Plan** (supports `/login umans` or `UMANS_AI_CODING_PLAN_API_KEY`) - **MiniMax Token Plan** (requires `MINIMAX_CODE_API_KEY` or `MINIMAX_CODE_CN_API_KEY`) - **Xiaomi MiMo** (requires `XIAOMI_API_KEY`) - **ZenMux** (requires `ZENMUX_API_KEY`) @@ -952,6 +953,7 @@ In Node.js environments, you can set environment variables to avoid passing API | Ollama Cloud | `OLLAMA_CLOUD_API_KEY` | | Qwen Portal | `QWEN_OAUTH_TOKEN` or `QWEN_PORTAL_API_KEY` | | zAI | `ZAI_API_KEY` | +| Umans AI Coding Plan | `UMANS_AI_CODING_PLAN_API_KEY` | | MiniMax Code | `MINIMAX_CODE_API_KEY` (international) or `MINIMAX_CODE_CN_API_KEY` (China) | | Xiaomi MiMo | `XIAOMI_API_KEY` | | ZenMux | `ZENMUX_API_KEY` | @@ -978,6 +980,7 @@ Provider endpoint defaults for the current OpenAI-compatible integrations: - Xiaomi MiMo: `https://api.xiaomimimo.com/anthropic` - ZenMux (OpenAI): `https://zenmux.ai/api/v1` - ZenMux (Anthropic models): `https://zenmux.ai/api/anthropic` +- Umans AI Coding Plan: `https://api.code.umans.ai` - vLLM: `http://127.0.0.1:8000/v1` - Ollama: local OpenAI-compatible runtime (`http://127.0.0.1:11434/v1`) - Ollama Cloud: native Ollama API host (`https://ollama.com/api`, configured here as base URL `https://ollama.com`) diff --git a/packages/ai/src/registry/registry.ts b/packages/ai/src/registry/registry.ts index e49787b77..72484d884 100644 --- a/packages/ai/src/registry/registry.ts +++ b/packages/ai/src/registry/registry.ts @@ -45,6 +45,7 @@ import { syntheticProvider } from "./synthetic"; import { tavilyProvider } from "./tavily"; import { togetherProvider } from "./together"; import type { ProviderDefinition } from "./types"; +import { umansProvider } from "./umans"; import { veniceProvider } from "./venice"; import { vercelAiGatewayProvider } from "./vercel-ai-gateway"; import { vllmProvider } from "./vllm"; @@ -83,6 +84,7 @@ const ALL = [ alibabaCodingPlanProvider, aimlApiProvider, zhipuCodingPlanProvider, + umansProvider, qwenPortalProvider, minimaxCodeProvider, minimaxCodeCnProvider, diff --git a/packages/ai/src/registry/umans.ts b/packages/ai/src/registry/umans.ts new file mode 100644 index 000000000..2231578cb --- /dev/null +++ b/packages/ai/src/registry/umans.ts @@ -0,0 +1,23 @@ +import { createApiKeyLogin } from "./api-key-login"; +import type { OAuthLoginCallbacks } from "./oauth/types"; +import type { ProviderDefinition } from "./types"; + +export const loginUmans = createApiKeyLogin({ + providerLabel: "Umans AI Coding Plan", + authUrl: "https://app.umans.ai/billing", + instructions: "Create or copy your Umans API key from Dashboard → API Keys.", + promptMessage: "Paste your Umans API key", + placeholder: "sk-...", + validation: { + kind: "anthropic-messages", + provider: "Umans AI Coding Plan", + baseUrl: "https://api.code.umans.ai", + model: "umans-coder", + }, +}); + +export const umansProvider = { + id: "umans", + name: "Umans AI Coding Plan", + login: (cb: OAuthLoginCallbacks) => loginUmans(cb), +} as const satisfies ProviderDefinition; diff --git a/packages/ai/test/provider-registry.test.ts b/packages/ai/test/provider-registry.test.ts index ee9bd8c7d..b41c8a11b 100644 --- a/packages/ai/test/provider-registry.test.ts +++ b/packages/ai/test/provider-registry.test.ts @@ -13,7 +13,7 @@ import type { OAuthCredentials, OAuthProvider } from "@oh-my-pi/pi-ai/registry/o import { getEnvApiKey } from "@oh-my-pi/pi-ai/stream"; const FIXTURE_SOURCE = "provider-registry-test"; -const ENV_KEYS = ["ZENMUX_API_KEY", "EXA_API_KEY", "XAI_OAUTH_TOKEN"] as const; +const ENV_KEYS = ["ZENMUX_API_KEY", "EXA_API_KEY", "XAI_OAUTH_TOKEN", "UMANS_AI_CODING_PLAN_API_KEY"] as const; const originalEnv = new Map(ENV_KEYS.map(key => [key, Bun.env[key]])); afterEach(() => { @@ -35,6 +35,8 @@ describe("provider registry auth surface", () => { Bun.env.EXA_API_KEY = "exa-env"; // Plain name derived from the catalog table's `envVars`. expect(getEnvApiKey("zenmux")).toBe("zenmux-env"); + Bun.env.UMANS_AI_CODING_PLAN_API_KEY = "umans-env"; + expect(getEnvApiKey("umans")).toBe("umans-env"); // Legacy search-tool key preserved (not a registry provider def). expect(getEnvApiKey("exa")).toBe("exa-env"); }); @@ -48,6 +50,7 @@ describe("provider registry auth surface", () => { const ids = getOAuthProviders().map(provider => provider.id); expect(ids).toContain("zenmux"); expect(ids).toContain("kagi"); + expect(ids).toContain("umans"); // openai has no interactive login flow. expect(ids).not.toContain("openai"); }); diff --git a/packages/catalog/CHANGELOG.md b/packages/catalog/CHANGELOG.md index fb65cf315..375e0c29d 100644 --- a/packages/catalog/CHANGELOG.md +++ b/packages/catalog/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Added + +- Added the Umans AI Coding Plan provider catalog with Anthropic-compatible model metadata and dynamic discovery. + ## [15.13.2] - 2026-06-15 ### Added diff --git a/packages/catalog/src/identity/priority.ts b/packages/catalog/src/identity/priority.ts index 9e012f669..6612c9e7b 100644 --- a/packages/catalog/src/identity/priority.ts +++ b/packages/catalog/src/identity/priority.ts @@ -27,6 +27,7 @@ const DEFAULT_MODEL_PROVIDER_ORDER = [ // Generic gateways and editor/proxy providers. These are useful when picked // explicitly, but should not win ambiguous automatic role selection. "alibaba-coding-plan", + "umans", "google-antigravity", "opencode-zen", "gitlab-duo", diff --git a/packages/catalog/src/models.json b/packages/catalog/src/models.json index 809d52d50..c2c677cff 100644 --- a/packages/catalog/src/models.json +++ b/packages/catalog/src/models.json @@ -62919,6 +62919,188 @@ "maxTokens": 8192 } }, + "umans": { + "umans-coder": { + "id": "umans-coder", + "name": "Umans Coder", + "api": "anthropic-messages", + "provider": "umans", + "baseUrl": "https://api.code.umans.ai", + "reasoning": true, + "input": [ + "text", + "image" + ], + "cost": { + "input": 0, + "output": 0, + "cacheRead": 0, + "cacheWrite": 0 + }, + "contextWindow": 262144, + "maxTokens": 262144, + "thinking": { + "mode": "budget", + "efforts": [ + "minimal", + "low", + "medium", + "high", + "xhigh" + ] + } + }, + "umans-flash": { + "id": "umans-flash", + "name": "Umans Flash", + "api": "anthropic-messages", + "provider": "umans", + "baseUrl": "https://api.code.umans.ai", + "reasoning": true, + "input": [ + "text", + "image" + ], + "cost": { + "input": 0, + "output": 0, + "cacheRead": 0, + "cacheWrite": 0 + }, + "contextWindow": 262144, + "maxTokens": 262144, + "thinking": { + "mode": "budget", + "efforts": [ + "minimal", + "low", + "medium", + "high", + "xhigh" + ] + } + }, + "umans-glm-5.1": { + "id": "umans-glm-5.1", + "name": "GLM 5.1", + "api": "anthropic-messages", + "provider": "umans", + "baseUrl": "https://api.code.umans.ai", + "reasoning": true, + "input": [ + "text", + "image" + ], + "cost": { + "input": 0, + "output": 0, + "cacheRead": 0, + "cacheWrite": 0 + }, + "contextWindow": 204800, + "maxTokens": 131072, + "thinking": { + "mode": "budget", + "efforts": [ + "minimal", + "low", + "medium", + "high", + "xhigh" + ] + } + }, + "umans-kimi-k2.6": { + "id": "umans-kimi-k2.6", + "name": "Kimi K2.6", + "api": "anthropic-messages", + "provider": "umans", + "baseUrl": "https://api.code.umans.ai", + "reasoning": true, + "input": [ + "text", + "image" + ], + "cost": { + "input": 0, + "output": 0, + "cacheRead": 0, + "cacheWrite": 0 + }, + "contextWindow": 262144, + "maxTokens": 262144, + "thinking": { + "mode": "budget", + "efforts": [ + "minimal", + "low", + "medium", + "high", + "xhigh" + ] + } + }, + "umans-kimi-k2.7": { + "id": "umans-kimi-k2.7", + "name": "Kimi K2.7 Code", + "api": "anthropic-messages", + "provider": "umans", + "baseUrl": "https://api.code.umans.ai", + "reasoning": true, + "input": [ + "text", + "image" + ], + "cost": { + "input": 0, + "output": 0, + "cacheRead": 0, + "cacheWrite": 0 + }, + "contextWindow": 262144, + "maxTokens": 262144, + "thinking": { + "mode": "budget", + "efforts": [ + "minimal", + "low", + "medium", + "high", + "xhigh" + ] + } + }, + "umans-qwen3.6-35b-a3b": { + "id": "umans-qwen3.6-35b-a3b", + "name": "Qwen3.6 35B A3B", + "api": "anthropic-messages", + "provider": "umans", + "baseUrl": "https://api.code.umans.ai", + "reasoning": true, + "input": [ + "text", + "image" + ], + "cost": { + "input": 0, + "output": 0, + "cacheRead": 0, + "cacheWrite": 0 + }, + "contextWindow": 262144, + "maxTokens": 262144, + "thinking": { + "mode": "budget", + "efforts": [ + "minimal", + "low", + "medium", + "high", + "xhigh" + ] + } + } + }, "venice": { "aion-labs-aion-2-0": { "id": "aion-labs-aion-2-0", @@ -71988,6 +72170,25 @@ ] } }, + "mimo-v2.5-asr": { + "id": "mimo-v2.5-asr", + "name": "mimo-v2.5-asr", + "api": "openai-completions", + "provider": "xiaomi", + "baseUrl": "https://api.xiaomimimo.com/v1", + "reasoning": false, + "input": [ + "text" + ], + "cost": { + "input": 0, + "output": 0, + "cacheRead": 0, + "cacheWrite": 0 + }, + "contextWindow": null, + "maxTokens": null + }, "mimo-v2.5-pro": { "id": "mimo-v2.5-pro", "name": "MiMo-V2.5-Pro", diff --git a/packages/catalog/src/provider-models/descriptors.ts b/packages/catalog/src/provider-models/descriptors.ts index 2810795d4..d023aec9a 100644 --- a/packages/catalog/src/provider-models/descriptors.ts +++ b/packages/catalog/src/provider-models/descriptors.ts @@ -37,6 +37,7 @@ import { qwenPortalModelManagerOptions, syntheticModelManagerOptions, togetherModelManagerOptions, + umansModelManagerOptions, veniceModelManagerOptions, vercelAiGatewayModelManagerOptions, vllmModelManagerOptions, @@ -308,6 +309,14 @@ export const CATALOG_PROVIDERS = [ createModelManagerOptions: (config: ModelManagerConfig) => togetherModelManagerOptions(config), catalogDiscovery: { label: "Together" }, }, + { + id: "umans", + defaultModel: "umans-coder", + envVars: ["UMANS_AI_CODING_PLAN_API_KEY"], + createModelManagerOptions: (config: ModelManagerConfig) => umansModelManagerOptions(config), + dynamicModelsAuthoritative: true, + catalogDiscovery: { label: "Umans AI Coding Plan", allowUnauthenticated: true }, + }, { id: "venice", defaultModel: "llama-3.3-70b", diff --git a/packages/catalog/src/provider-models/openai-compat.ts b/packages/catalog/src/provider-models/openai-compat.ts index 7ae1dd7a9..64ed8f3cb 100644 --- a/packages/catalog/src/provider-models/openai-compat.ts +++ b/packages/catalog/src/provider-models/openai-compat.ts @@ -568,6 +568,120 @@ function createSimpleAnthropicProviderOptions( }; } +// --------------------------------------------------------------------------- +// Umans AI Coding Plan +// --------------------------------------------------------------------------- + +const UMANS_BASE_URL = "https://api.code.umans.ai"; +const UMANS_MODELS_INFO_PATH = "/models/info"; + +export interface UmansModelManagerConfig { + apiKey?: string; + baseUrl?: string; + fetch?: FetchImpl; +} + +interface UmansModelInfo { + name?: unknown; + display_name?: unknown; + capabilities?: unknown; +} + +function normalizeUmansBaseUrl(baseUrl: string | undefined): string { + const normalized = normalizeAnthropicBaseUrl(baseUrl, UMANS_BASE_URL); + return normalized.endsWith("/v1") ? normalized.slice(0, -3) : normalized; +} + +function umansSupportsVision(value: unknown): boolean { + return value === true || (typeof value === "string" && value.length > 0); +} + +function umansReasoningSupported(value: unknown): boolean { + if (isRecord(value)) { + return value.supported === true; + } + return value === true; +} + +function mapUmansModelInfo( + modelId: string, + raw: UmansModelInfo, + baseUrl: string, + reference: ModelSpec<"anthropic-messages"> | undefined, +): ModelSpec<"anthropic-messages"> | null { + if (!modelId) return null; + const capabilities = isRecord(raw.capabilities) ? raw.capabilities : {}; + const supportsTools = capabilities.supports_tools; + return { + ...reference, + id: modelId, + name: toModelName(raw.display_name, toModelName(raw.name, modelId)), + api: "anthropic-messages", + provider: "umans", + baseUrl, + reasoning: umansReasoningSupported(capabilities.reasoning), + input: umansSupportsVision(capabilities.supports_vision) ? ["text", "image"] : ["text"], + ...(supportsTools === false ? { supportsTools: false } : {}), + cost: reference?.cost ?? { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: toPositiveNumber(capabilities.context_window, reference?.contextWindow ?? null), + maxTokens: toPositiveNumber(capabilities.max_completion_tokens, reference?.maxTokens ?? null), + }; +} + +async function fetchUmansModelsInfo(options: { + baseUrl: string; + apiKey?: string; + fetch?: FetchImpl; + references: Map>; +}): Promise[] | null> { + const discoveryBaseUrl = toAnthropicDiscoveryBaseUrl(options.baseUrl); + const requestHeaders: Record = { Accept: "application/json" }; + if (options.apiKey) { + requestHeaders["x-api-key"] = options.apiKey; + } + const fetchImpl = options.fetch ?? fetch; + let response: Response; + try { + response = await fetchImpl(`${discoveryBaseUrl}${UMANS_MODELS_INFO_PATH}`, { + method: "GET", + headers: requestHeaders, + }); + } catch { + return null; + } + if (!response.ok) { + return null; + } + let payload: unknown; + try { + payload = await response.json(); + } catch { + return null; + } + if (!isRecord(payload)) { + return null; + } + const models: ModelSpec<"anthropic-messages">[] = []; + for (const [modelId, value] of Object.entries(payload)) { + if (!isRecord(value)) continue; + const mapped = mapUmansModelInfo(modelId, value, options.baseUrl, options.references.get(modelId)); + if (mapped) { + models.push(mapped); + } + } + return models.sort((left, right) => left.id.localeCompare(right.id)); +} + +export function umansModelManagerOptions(config?: UmansModelManagerConfig): ModelManagerOptions<"anthropic-messages"> { + const apiKey = config?.apiKey; + const baseUrl = normalizeUmansBaseUrl(config?.baseUrl); + const references = createBundledReferenceMap<"anthropic-messages">("umans"); + return { + providerId: "umans", + dynamicModelsAuthoritative: true, + fetchDynamicModels: () => fetchUmansModelsInfo({ baseUrl, apiKey, fetch: config?.fetch, references }), + }; +} // --------------------------------------------------------------------------- // 1. OpenAI // --------------------------------------------------------------------------- @@ -3245,6 +3359,8 @@ const MODELS_DEV_PROVIDER_DESCRIPTORS_CORE: readonly ModelsDevProviderDescriptor const MODELS_DEV_PROVIDER_DESCRIPTORS_CODING_PLANS: readonly ModelsDevProviderDescriptor[] = [ // --- zAI --- anthropicMessagesDescriptor("zai-coding-plan", "zai", "https://api.z.ai/api/anthropic"), + // --- Umans AI Coding Plan --- + anthropicMessagesDescriptor("umans-ai-coding-plan", "umans", UMANS_BASE_URL), // --- Xiaomi --- openAiCompletionsDescriptor("xiaomi", "xiaomi", "https://api.xiaomimimo.com/v1", { defaultContextWindow: 262144, diff --git a/packages/catalog/test/umans-provider.test.ts b/packages/catalog/test/umans-provider.test.ts new file mode 100644 index 000000000..470cd35b0 --- /dev/null +++ b/packages/catalog/test/umans-provider.test.ts @@ -0,0 +1,111 @@ +import { describe, expect, it } from "bun:test"; +import { + MODELS_DEV_PROVIDER_DESCRIPTORS, + mapModelsDevToModels, + umansModelManagerOptions, +} from "@oh-my-pi/pi-catalog/provider-models/openai-compat"; +import type { FetchImpl } from "@oh-my-pi/pi-catalog/types"; +import modelsJson from "../src/models.json"; + +interface BundledModel { + api: string; + provider: string; + baseUrl: string; + reasoning: boolean; + input: string[]; + contextWindow: number | null; + maxTokens: number | null; +} + +describe("umans provider catalog", () => { + it("discovers Anthropic-route models from the public models info endpoint", async () => { + const requestedUrls: string[] = []; + const fetchImpl: FetchImpl = async input => { + requestedUrls.push(String(input)); + return new Response( + JSON.stringify({ + "umans-coder": { + display_name: "Umans Coder", + capabilities: { + context_window: 262_144, + max_completion_tokens: 262_144, + supports_vision: true, + supports_tools: true, + reasoning: { supported: true }, + }, + }, + }), + { status: 200, headers: { "Content-Type": "application/json" } }, + ); + }; + + const options = umansModelManagerOptions({ fetch: fetchImpl }); + const fetchDynamicModels = options.fetchDynamicModels; + if (!fetchDynamicModels) throw new Error("Umans dynamic discovery is not configured"); + + const models = await fetchDynamicModels(); + + expect(requestedUrls).toEqual(["https://api.code.umans.ai/v1/models/info"]); + expect(models).not.toBeNull(); + const model = models?.[0]; + expect(model).toMatchObject({ + id: "umans-coder", + name: "Umans Coder", + api: "anthropic-messages", + provider: "umans", + baseUrl: "https://api.code.umans.ai", + reasoning: true, + input: ["text", "image"], + contextWindow: 262_144, + maxTokens: 262_144, + }); + }); + + it("maps the models.dev Umans provider to the Anthropic endpoint", () => { + const models = mapModelsDevToModels( + { + "umans-ai-coding-plan": { + models: { + "umans-coder": { + name: "Umans Coder", + tool_call: true, + reasoning: true, + modalities: { input: ["text", "image"] }, + limit: { context: 262_144, output: 262_144 }, + cost: { input: 0, output: 0, cache_read: 0, cache_write: 0 }, + }, + }, + }, + }, + MODELS_DEV_PROVIDER_DESCRIPTORS, + ).filter(model => model.provider === "umans"); + + expect(models).toHaveLength(1); + expect(models[0]).toMatchObject({ + id: "umans-coder", + api: "anthropic-messages", + provider: "umans", + baseUrl: "https://api.code.umans.ai", + reasoning: true, + input: ["text", "image"], + contextWindow: 262_144, + maxTokens: 262_144, + }); + }); + + it("bundles the default Umans coding model", () => { + const providers = modelsJson as Record>; + const model = providers.umans?.["umans-coder"]; + + expect(model).toBeDefined(); + expect(model).toMatchObject({ + api: "anthropic-messages", + provider: "umans", + baseUrl: "https://api.code.umans.ai", + reasoning: true, + input: ["text", "image"], + contextWindow: 262_144, + maxTokens: 262_144, + }); + }); +}); diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index e1682b7e6..aa8a4c8d3 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Added + +- Added `UMANS_AI_CODING_PLAN_API_KEY` to the CLI environment help. + ## [15.13.2] - 2026-06-15 ### Added diff --git a/packages/coding-agent/src/cli/args.ts b/packages/coding-agent/src/cli/args.ts index 10efe1ffa..f8a32ae06 100644 --- a/packages/coding-agent/src/cli/args.ts +++ b/packages/coding-agent/src/cli/args.ts @@ -279,6 +279,7 @@ export function getExtraHelpText(): string { KILO_API_KEY - Kilo Gateway models MISTRAL_API_KEY - Mistral models ZAI_API_KEY - z.ai models (ZhipuAI/GLM) + UMANS_AI_CODING_PLAN_API_KEY - Umans AI Coding Plan models MINIMAX_API_KEY - MiniMax models OPENCODE_API_KEY - OpenCode Zen/OpenCode Go models CURSOR_ACCESS_TOKEN - Cursor AI models