omni-pi 0.4.0 → 0.6.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/CHANGELOG.md ADDED
@@ -0,0 +1,42 @@
1
+ # Changelog
2
+
3
+ ## 0.6.1 - 2026-03-29
4
+
5
+ ### Provider management
6
+
7
+ - renamed bundled provider auth management from `/provider-auth` to `/manage-providers`
8
+ - limited `/model-setup list` to removing individual custom model entries instead of deleting whole custom providers
9
+ - documented the bundled provider list directly in `PROVIDERS.md`
10
+
11
+ ### CI and documentation
12
+
13
+ - added a unified `npm run verify` gate for local development, CI, and publish checks
14
+ - added docs coverage tests so command docs and the bundled provider list fail CI when they drift from the code
15
+ - added a tag-triggered release workflow that re-verifies the repo, creates a GitHub release, and publishes to npm when credentials are configured
16
+
17
+ ## 0.6.0 - 2026-03-27
18
+
19
+ ### Provider management
20
+
21
+ - restricted `/model-setup` to custom providers and custom model entries stored in `models.json`
22
+ - added `/provider-auth` to remove stored auth for bundled Pi providers from the UI
23
+ - added whole-provider removal for custom providers, not just single-model removal
24
+ - fixed `/model-setup list` so it only shows custom providers/models instead of the full authenticated runtime catalog
25
+
26
+ ### Provider discovery and refresh
27
+
28
+ - added startup refresh for authenticated, discoverable custom providers
29
+ - preserved dynamic headers and other existing model metadata when custom providers are edited or rediscovered
30
+ - improved custom-provider onboarding so users can add a provider first and discover models automatically
31
+ - stopped persisting invalid `contextWindow: 0` and `maxTokens: 0` values for discovered providers
32
+
33
+ ### Selector and UX improvements
34
+
35
+ - aligned Omni-Pi setup selectors with Pi-style searchable selection behavior
36
+ - limited the custom searchable selector to 10 visible rows and only enabled search when more than 10 items are present
37
+ - improved bundled command descriptions and provider-management messaging
38
+
39
+ ### Documentation
40
+
41
+ - documented the split between custom provider setup and bundled provider auth management in `README.md`
42
+ - added `PROVIDERS.md` with guidance for `/model-setup`, `/provider-auth`, and custom provider discovery behavior
package/CREDITS.md CHANGED
@@ -9,6 +9,17 @@ Omni-Pi exists because of the Pi ecosystem and the work of earlier authors.
9
9
  - [@mariozechner/pi-coding-agent](https://www.npmjs.com/package/@mariozechner/pi-coding-agent) by Mario Zechner
10
10
  - Pi coding agent package (direct dependency)
11
11
 
12
+ ## Bundled third-party extensions
13
+
14
+ - [pi-web-access](https://www.npmjs.com/package/pi-web-access)
15
+ - Web search and fetch tools for the agent
16
+ - [pi-interview](https://www.npmjs.com/package/pi-interview)
17
+ - Guided Q&A interface for user clarification
18
+ - [@juanibiapina/pi-powerbar](https://www.npmjs.com/package/@juanibiapina/pi-powerbar) by Juani Biapina
19
+ - Powerline-style status bar with extensible segments
20
+ - [@juanibiapina/pi-extension-settings](https://www.npmjs.com/package/@juanibiapina/pi-extension-settings) by Juani Biapina
21
+ - Settings persistence for Pi extensions
22
+
12
23
  ## Workflow and orchestration inspiration
13
24
 
14
25
  - [can1357/oh-my-pi](https://github.com/can1357/oh-my-pi)
package/PROVIDERS.md ADDED
@@ -0,0 +1,76 @@
1
+ # Provider Setup
2
+
3
+ Omni-Pi separates bundled providers from custom providers.
4
+
5
+ ## `/model-setup`
6
+
7
+ `/model-setup` is only for custom providers and custom model entries stored in `models.json`.
8
+
9
+ Use it when you want to configure:
10
+
11
+ - your own provider id
12
+ - an API type such as OpenAI-compatible or Anthropic-compatible
13
+ - a custom base URL
14
+ - an API key for that custom provider
15
+ - discovered models or manual model entries
16
+
17
+ `/model-setup list` only shows custom models from `models.json`, and it removes individual custom model entries only.
18
+
19
+ ## Bundled Providers
20
+
21
+ Pi's bundled providers are still available through the runtime model registry, but Omni-Pi does not manage them through `/model-setup`.
22
+
23
+ That means:
24
+
25
+ - `/model-setup` does not add bundled providers
26
+ - `/model-setup` does not list bundled providers
27
+ - `/model-setup` does not remove bundled providers
28
+
29
+ If a bundled provider already has valid auth in the Pi runtime, Omni-Pi may use it through normal model selection, but its setup is outside the custom-provider flow documented here.
30
+
31
+ Use `/manage-providers` to list bundled providers that currently have stored auth and remove that auth when needed.
32
+
33
+ The bundled-provider list below is expected to stay in sync with the exported provider setup list in `src/model-setup.ts`. The test suite checks that this section matches the code list.
34
+
35
+ ### Bundled provider list
36
+
37
+ - `anthropic` — API key
38
+ - `openai` — API key
39
+ - `openrouter` — API key
40
+ - `google` — API key
41
+ - `github-copilot` — OAuth
42
+ - `openai-codex` — OAuth
43
+ - `xai` — API key
44
+ - `zai` — API key
45
+ - `azure-openai-responses` — API key
46
+ - `nvidia` — API key
47
+ - `together` — API key
48
+ - `synthetic` — API key
49
+ - `nanogpt` — API key
50
+ - `xiaomi` — API key
51
+ - `moonshot` — API key
52
+ - `venice` — API key
53
+ - `kilo` — API key
54
+ - `gitlab-duo` — API key
55
+ - `qwen-portal` — API key
56
+ - `qianfan` — API key
57
+ - `cloudflare-ai-gateway` — API key
58
+
59
+ ## Custom Provider Discovery
60
+
61
+ For custom providers that expose a compatible model listing endpoint, Omni-Pi can fetch models for you after you add the provider details and credentials.
62
+
63
+ On launch, Omni-Pi also refreshes authenticated, discoverable custom providers that are already configured in `models.json`.
64
+
65
+ ## When To Use Which Path
66
+
67
+ Use `/model-setup` when:
68
+
69
+ - you are adding a non-bundled provider
70
+ - you need a custom base URL
71
+ - you want to manage your own discovered or manual model list
72
+
73
+ Do not use `/model-setup` when:
74
+
75
+ - you are trying to manage a built-in Pi provider
76
+ - you expect bundled providers to appear as removable custom entries
package/README.md CHANGED
@@ -1,8 +1,6 @@
1
1
  # Omni-Pi
2
2
 
3
- Omni-Pi is a Pi package built around one user-facing brain.
4
-
5
- The goal is simple: talk to a helpful agent, let it interview you until the request is precise, have it write the spec and task breakdown into `.omni/`, then implement the work in bounded slices with explicit verification and progress notes.
3
+ A batteries-included [Pi](https://github.com/badlogic/pi-mono) package that interviews the user, documents the spec, and implements work in bounded slices.
6
4
 
7
5
  Requires Node.js 22 or newer.
8
6
 
@@ -12,15 +10,13 @@ Requires Node.js 22 or newer.
12
10
 
13
11
  ## What It Does
14
12
 
15
- - One friendly brain talks to the user.
16
- - `.omni/` remains the durable memory layer for goals, specs, tasks, checks, progress, and decisions.
17
- - Work is broken into small, verifiable slices before code changes happen.
18
- - Verification is explicit and recorded alongside implementation progress.
13
+ - One conversational brain interviews the user until the request is precise.
14
+ - Writes specs, tasks, and progress into `.omni/` as durable project memory.
15
+ - Breaks work into small, verifiable slices and implements them one at a time.
16
+ - Bundles web search, guided interviews, themed UI, a task viewer, a powerbar, custom provider/model management, and automatic updates out of the box.
19
17
 
20
18
  ## Install
21
19
 
22
- Install the standalone executable:
23
-
24
20
  ```bash
25
21
  npm install -g omni-pi
26
22
  ```
@@ -32,61 +28,95 @@ cd your-project
32
28
  omni
33
29
  ```
34
30
 
35
- For local development from this checkout:
31
+ Custom provider setup and bundled provider behavior are documented in [PROVIDERS.md](PROVIDERS.md).
36
32
 
37
- ```bash
38
- git clone https://github.com/EdGy2k/Omni-Pi.git
39
- cd Omni-Pi
40
- npm install
41
- npm run chat
42
- ```
33
+ ## Features
43
34
 
44
- ## Use
35
+ ### Bundled Extensions
45
36
 
46
- Start Pi in the project you want to work on, with Omni-Pi installed, and describe what you want.
37
+ | Extension | What it does |
38
+ |-----------|-------------|
39
+ | **omni-core** | Brain workflow, themed header, session init, system prompt injection |
40
+ | **omni-providers** | Model provider wiring |
41
+ | **omni-memory** | `.omni/` durable memory bootstrap |
42
+ | **pi-web-access** | Web search and fetch tools for the agent |
43
+ | **pi-interview** | Guided Q&A when the agent needs clarification |
44
+ | **pi-powerbar** | Powerline-style status bar with segments |
45
+ | **pi-extension-settings** | Settings persistence for extensions |
47
46
 
48
- Example:
47
+ ### Commands
49
48
 
50
- ```text
51
- Build a small CLI notes app for me. I want add, list, and search commands. Store data locally in JSON. Ask me any questions you need before you start coding.
52
- ```
49
+ | Command | Description |
50
+ |---------|-------------|
51
+ | `/model-setup` | Add custom providers/models or remove custom model entries |
52
+ | `/manage-providers` | Remove stored auth for bundled providers |
53
+ | `/theme` | Switch between color presets (lavender, ember, ocean, mint, rose, gold, arctic, neon, copper, slate) |
54
+ | `/update` | Check for Omni-Pi updates |
53
55
 
54
- Expected behavior:
56
+ ### Keyboard Shortcuts
55
57
 
56
- - Omni-Pi interviews first when the request is underspecified.
57
- - It writes and updates `.omni/PROJECT.md`, `.omni/SPEC.md`, `.omni/TASKS.md`, `.omni/TESTS.md`, and `.omni/STATE.md`.
58
- - It hides internal implementation machinery instead of talking about planner/worker/expert handoffs.
59
- - It implements one bounded slice at a time and runs the planned checks.
58
+ | Shortcut | Description |
59
+ |----------|-------------|
60
+ | `Ctrl+Shift+T` | Toggle the task list widget (`.omni/TASKS.md` + `.omni/STATE.md`) |
60
61
 
61
- ## Durable Memory
62
+ ### Auto-Updater
62
63
 
63
- Omni-Pi keeps its working notes in `.omni/`:
64
+ Omni-Pi checks for new versions on startup (cached, re-checks every 4 hours). When an update is available, it prompts to install and restart. Pi's own update notification is suppressed to avoid duplication.
64
65
 
65
- - `PROJECT.md` captures the problem, users, constraints, and success criteria.
66
- - `SPEC.md` captures the exact requested behavior and implementation shape.
67
- - `TASKS.md` breaks work into bounded slices.
68
- - `TESTS.md` records the checks for the current slice.
69
- - `STATE.md`, `SESSION-SUMMARY.md`, and `DECISIONS.md` keep progress and rationale durable across sessions.
66
+ ## Provider Support
70
67
 
71
- ## Development
68
+ `/model-setup` is for custom providers and custom model entries only.
72
69
 
73
- `npm run chat` launches the local `omni` wrapper from this checkout, which in turn starts Pi with this package loaded.
70
+ Use it when you want to configure:
74
71
 
75
- For local verification:
72
+ - a custom provider id
73
+ - an API type and base URL
74
+ - an API key for that custom provider
75
+ - discovered models or manual model entries
76
76
 
77
- ```bash
78
- npm run check
79
- npm run lint
80
- npm test
81
- ```
77
+ Use `/manage-providers` to remove stored auth for bundled Pi providers.
78
+
79
+ See [PROVIDERS.md](PROVIDERS.md) for the current supported-provider list and auth-management split.
80
+
81
+ ## Durable Memory
82
+
83
+ Omni-Pi keeps its working notes in `.omni/`:
82
84
 
83
- For package verification:
85
+ | File | Purpose |
86
+ |------|---------|
87
+ | `PROJECT.md` | Problem, users, constraints, success criteria |
88
+ | `SPEC.md` | Exact requested behavior and implementation shape |
89
+ | `TASKS.md` | Work broken into bounded slices |
90
+ | `TESTS.md` | Checks for the current slice |
91
+ | `STATE.md` | Current phase, active task, blockers |
92
+ | `SESSION-SUMMARY.md` | Progress notes across sessions |
93
+ | `DECISIONS.md` | Rationale for key choices |
94
+
95
+ ## Development
84
96
 
85
97
  ```bash
86
- npm pack
87
- npm publish --dry-run
98
+ git clone https://github.com/EdGy2k/Omni-Pi.git
99
+ cd Omni-Pi
100
+ npm install
101
+ npm run chat # launch locally in dev mode
88
102
  ```
89
103
 
104
+ | Command | Purpose |
105
+ |---------|---------|
106
+ | `npm run chat` | Launch the local `omni` executable |
107
+ | `npm test` | Run the test suite (Vitest) |
108
+ | `npm run check` | TypeScript type-check |
109
+ | `npm run lint` | Biome lint + format check |
110
+ | `npm run verify` | Full local/CI gate: type-check, lint, test, and package dry-run |
111
+ | `npm run format` | Auto-fix lint and formatting |
112
+ | `npm install -g .` | Install globally from local checkout |
113
+
114
+ ## CI/CD
115
+
116
+ - Pull requests and pushes to `main` run `npm run verify`.
117
+ - The docs are part of the test contract, including a sync check between `PROVIDERS.md` and the bundled-provider setup list in code.
118
+ - Pushing a `v*` tag runs the release workflow, verifies the repo again, creates a GitHub release, and publishes to npm when `NPM_TOKEN` is configured.
119
+
90
120
  ## Attribution
91
121
 
92
122
  Omni-Pi builds on the Pi ecosystem. See [CREDITS.md](CREDITS.md).
@@ -7,20 +7,26 @@ import {
7
7
  import { renderHeader } from "../../src/header.js";
8
8
  import { registerModelCommand } from "../../src/model-command.js";
9
9
  import { registerOmniMessageRenderer } from "../../src/pi.js";
10
+ import { registerProviderAuthCommand } from "../../src/provider-auth-command.js";
10
11
  import { createOmniTheme } from "../../src/theme.js";
11
12
  import { registerThemeCommand } from "../../src/theme-command.js";
13
+ import { registerTodoShortcut } from "../../src/todo-shortcut.js";
14
+ import { registerUpdater } from "../../src/updater.js";
12
15
 
13
16
  export default function omniCoreExtension(api: ExtensionAPI): void {
14
17
  registerOmniMessageRenderer(api);
15
18
  registerModelCommand(api);
19
+ registerProviderAuthCommand(api);
16
20
  registerThemeCommand(api);
21
+ registerTodoShortcut(api);
22
+ registerUpdater(api);
17
23
 
18
24
  api.on("session_start", async (_event, ctx) => {
19
25
  await ensureOmniInitialized(ctx.cwd);
20
26
  ctx.ui.setTitle("Omni-Pi");
21
27
  ctx.ui.setTheme(createOmniTheme());
22
28
  ctx.ui.setHeader((_tui, theme) => renderHeader(theme));
23
- ctx.ui.setStatus("omni", undefined);
29
+ ctx.ui.setStatus("omni", "\x1b[2mctrl+shift+t tasks\x1b[0m");
24
30
  });
25
31
 
26
32
  api.on("before_agent_start", async (event, ctx) => {
@@ -1,5 +1,14 @@
1
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
2
+
3
+ import { refreshAuthenticatedProviderModels } from "../../src/model-setup.js";
4
+ import { registerOmniProviders } from "../../src/providers.js";
5
+
1
6
  export default async function omniProvidersExtension(
2
- _api: unknown,
7
+ api: ExtensionAPI,
3
8
  ): Promise<void> {
4
- return;
9
+ await registerOmniProviders(api);
10
+
11
+ api.on("session_start", async (_event, ctx) => {
12
+ await refreshAuthenticatedProviderModels(ctx.modelRegistry);
13
+ });
5
14
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omni-pi",
3
- "version": "0.4.0",
3
+ "version": "0.6.1",
4
4
  "description": "Single-agent Pi package that interviews the user, documents the spec, and implements work in bounded slices.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -30,12 +30,14 @@
30
30
  "files": [
31
31
  "bin",
32
32
  "agents",
33
+ "CHANGELOG.md",
33
34
  "extensions",
34
35
  "prompts",
35
36
  "skills",
36
37
  "src",
37
38
  "templates",
38
39
  "README.md",
40
+ "PROVIDERS.md",
39
41
  "CREDITS.md"
40
42
  ],
41
43
  "scripts": {
@@ -44,8 +46,9 @@
44
46
  "lint": "biome check .",
45
47
  "format": "biome check --write .",
46
48
  "test": "vitest run",
49
+ "verify": "npm run check && npm run lint && npm test && npm run pack:check",
47
50
  "pack:check": "npm pack --dry-run",
48
- "prepublishOnly": "npm run check && npm run lint && npm test"
51
+ "prepublishOnly": "npm run verify"
49
52
  },
50
53
  "devDependencies": {
51
54
  "@biomejs/biome": "2.4.9",
@@ -58,7 +61,10 @@
58
61
  "./extensions/omni-providers/index.ts",
59
62
  "./extensions/omni-core/index.ts",
60
63
  "./extensions/omni-memory/index.ts",
61
- "./node_modules/pi-web-access/index.ts"
64
+ "./node_modules/pi-web-access/index.ts",
65
+ "./node_modules/pi-interview/index.ts",
66
+ "./node_modules/@juanibiapina/pi-extension-settings/dist/index.js",
67
+ "./node_modules/@juanibiapina/pi-powerbar/dist"
62
68
  ],
63
69
  "skills": [
64
70
  "./skills",
@@ -70,7 +76,10 @@
70
76
  },
71
77
  "dependencies": {
72
78
  "@anthropic-ai/claude-agent-sdk": "0.2.84",
73
- "@mariozechner/pi-coding-agent": "^0.62.0",
79
+ "@juanibiapina/pi-extension-settings": "^0.6.0",
80
+ "@juanibiapina/pi-powerbar": "^0.7.1",
81
+ "@mariozechner/pi-coding-agent": "^0.63.0",
82
+ "pi-interview": "^0.5.5",
74
83
  "pi-subagents": "^0.11.11",
75
84
  "pi-web-access": "^0.10.3",
76
85
  "zod": "^4.3.6"
@@ -1,47 +1,177 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+
1
4
  import type {
2
5
  ExtensionAPI,
3
6
  ExtensionCommandContext,
4
7
  } from "@mariozechner/pi-coding-agent";
8
+ import { getAgentDir } from "@mariozechner/pi-coding-agent";
9
+
10
+ import { runModelSetupWizard } from "./model-setup.js";
11
+ import { searchableSelect } from "./searchable-select.js";
12
+
13
+ interface ModelsJsonModel {
14
+ id: string;
15
+ [key: string]: unknown;
16
+ }
17
+
18
+ interface ModelsJsonProvider {
19
+ models?: ModelsJsonModel[];
20
+ [key: string]: unknown;
21
+ }
22
+
23
+ interface ModelsJsonConfig {
24
+ providers?: Record<string, ModelsJsonProvider>;
25
+ }
5
26
 
6
- import {
7
- getAuthenticatedModelOptions,
8
- setupCustomProviderModel,
9
- } from "./model-setup.js";
27
+ type CustomModelEntry = { provider: string; modelId: string };
28
+ type ListOption = { provider: string; modelId: string; label: string };
29
+
30
+ function getModelsPath(): string {
31
+ return path.join(getAgentDir(), "models.json");
32
+ }
33
+
34
+ async function readModelsJson(): Promise<ModelsJsonConfig> {
35
+ try {
36
+ const content = await readFile(getModelsPath(), "utf8");
37
+ const parsed = JSON.parse(content) as ModelsJsonConfig;
38
+ return typeof parsed === "object" && parsed !== null ? parsed : {};
39
+ } catch {
40
+ return {};
41
+ }
42
+ }
43
+
44
+ async function writeModelsJson(config: ModelsJsonConfig): Promise<void> {
45
+ const modelsPath = getModelsPath();
46
+ await mkdir(path.dirname(modelsPath), { recursive: true });
47
+ await writeFile(modelsPath, `${JSON.stringify(config, null, 2)}\n`, "utf8");
48
+ }
49
+
50
+ function getCustomModels(config: ModelsJsonConfig): CustomModelEntry[] {
51
+ const entries: CustomModelEntry[] = [];
52
+ for (const [provider, providerConfig] of Object.entries(
53
+ config.providers ?? {},
54
+ )) {
55
+ for (const model of providerConfig.models ?? []) {
56
+ entries.push({ provider, modelId: model.id });
57
+ }
58
+ }
59
+ return entries;
60
+ }
61
+
62
+ export function removeCustomModelFromConfig(
63
+ config: ModelsJsonConfig,
64
+ provider: string,
65
+ modelId: string,
66
+ ): ModelsJsonConfig {
67
+ const providers = { ...(config.providers ?? {}) };
68
+ const providerConfig = providers[provider];
69
+ if (!providerConfig?.models) {
70
+ return {
71
+ ...config,
72
+ providers,
73
+ };
74
+ }
75
+
76
+ providerConfig.models = providerConfig.models.filter(
77
+ (model) => model.id !== modelId,
78
+ );
79
+ if (providerConfig.models.length === 0) {
80
+ delete providers[provider];
81
+ }
82
+
83
+ return {
84
+ ...config,
85
+ providers,
86
+ };
87
+ }
88
+
89
+ function buildListOptions(customModels: CustomModelEntry[]): ListOption[] {
90
+ return customModels
91
+ .map(({ provider, modelId }) => ({
92
+ provider,
93
+ modelId,
94
+ label: `${provider}/${modelId} ✕`,
95
+ }))
96
+ .sort((left, right) => left.label.localeCompare(right.label));
97
+ }
10
98
 
11
99
  async function handleAdd(ctx: ExtensionCommandContext): Promise<void> {
12
- const result = await setupCustomProviderModel({ ctx });
100
+ const result = await runModelSetupWizard({ ctx });
13
101
  ctx.ui.notify(result.summary, "info");
14
102
  }
15
103
 
16
104
  async function handleList(ctx: ExtensionCommandContext): Promise<void> {
17
- const models = getAuthenticatedModelOptions(ctx.modelRegistry);
105
+ const config = await readModelsJson();
106
+ const custom = getCustomModels(config);
107
+ const options = buildListOptions(custom);
18
108
 
19
- if (models.length === 0) {
20
- ctx.ui.notify("No models available.", "info");
109
+ if (options.length === 0) {
110
+ ctx.ui.notify("No custom models found.", "info");
21
111
  return;
22
112
  }
23
113
 
24
- await ctx.ui.select("Available models:", models);
114
+ while (true) {
115
+ const selected = await searchableSelect(
116
+ ctx.ui,
117
+ "Custom models (✕ = remove):",
118
+ options.map((option) => ({
119
+ label: option.label,
120
+ value: option.label,
121
+ searchText: option.label.replace("✕", ""),
122
+ })),
123
+ );
124
+ if (selected === undefined) return;
125
+ const selectedOption = options.find((option) => option.label === selected);
126
+ if (!selectedOption) continue;
127
+
128
+ const modelRef = `${selectedOption.provider}/${selectedOption.modelId}`;
129
+ const confirmed = await ctx.ui.confirm(
130
+ "Remove model?",
131
+ `Remove ${modelRef} from models.json?`,
132
+ );
133
+ if (!confirmed) return;
134
+
135
+ const freshConfig = await readModelsJson();
136
+ await writeModelsJson(
137
+ removeCustomModelFromConfig(
138
+ freshConfig,
139
+ selectedOption.provider,
140
+ selectedOption.modelId,
141
+ ),
142
+ );
143
+
144
+ ctx.modelRegistry.refresh();
145
+ ctx.ui.notify(`Removed ${modelRef}.`, "info");
146
+ return;
147
+ }
25
148
  }
26
149
 
27
150
  export function registerModelCommand(api: ExtensionAPI): void {
28
151
  api.registerCommand("model-setup", {
29
- description: "Add or list custom model providers",
152
+ description: "Add custom providers/models or remove custom model entries",
30
153
  async handler(args: string, ctx: ExtensionCommandContext) {
31
154
  const sub = args.trim().toLowerCase();
32
155
 
33
156
  if (sub === "add") return handleAdd(ctx);
34
157
  if (sub === "list") return handleList(ctx);
35
158
 
36
- const choice = await ctx.ui.select("Model setup:", [
37
- "add — Add a custom provider/model",
38
- "list Show available models",
159
+ const choice = await searchableSelect(ctx.ui, "Model setup:", [
160
+ {
161
+ label: "add Add a custom provider or model",
162
+ value: "add",
163
+ searchText: "add custom provider model",
164
+ },
165
+ {
166
+ label: "list — Show custom models / remove model entries",
167
+ value: "list",
168
+ searchText: "list remove custom models",
169
+ },
39
170
  ]);
40
171
  if (!choice) return;
41
172
 
42
- const picked = choice.split("")[0].trim();
43
- if (picked === "add") return handleAdd(ctx);
44
- if (picked === "list") return handleList(ctx);
173
+ if (choice === "add") return handleAdd(ctx);
174
+ if (choice === "list") return handleList(ctx);
45
175
  },
46
176
  });
47
177
  }