Oh My OpenAgent for VS Code
July 31, 2026 · View on GitHub
A VS Code companion extension for Oh My OpenAgent — a community-driven enhancement layer for OpenAI Codex CLI. It gives you a visual tree of agents, categories, and profiles, and a form-based editor for model overrides without hand-editing JSONC files.
- GitHub: github.com/oh-my-openagent/oh-my-openagent — main project repository
- Site: omo.dev — official documentation and downloads
Screenshots
The sidebar view shows your active config, all built-in agents and categories with their assigned models, and saved profiles. The editor panel opens on demand for any agent or category — it supports model selection, sampling parameters, thinking budgets, and fallback models.
| Sidebar overview | Agent editor in action |
|---|---|
![]() | ![]() |
Features
- Agent and category model overrides: see all 11 built-in agents and 8 built-in categories in a hierarchical tree. Edit any item in the webview form. Saving an edit to a built-in item creates its override. The tree renders labels like
sisyphus → opencode-go/kimi-k2.7-codeso you always know which model is assigned. - Ordered model cards: position 1 is Main and every later card is a fallback. Drag any fallback into position 1 to replace Main. The
Draghandle explains this on hover and supports keyboard reordering: press Space or Enter to pick up, use Arrow Up or Arrow Down to move, press Space or Enter to drop, or Escape to cancel. Main settings are shared defaults for Main and for inherited fallback fields. Each fallback's Advanced controls let you chooseInherit defaultorOverride; inherited properties are omitted when saved. - Lazy model picker — the Model field is populated asynchronously from the local
opencode models --verboseCLI, with a free-form fallback when the CLI is unavailable. The discovered model IDs appear as autocomplete suggestions alongside each model's capabilities and variants. A reload button lets you re-run discovery at any time. - JSONC preservation — all writes go through
jsonc-parservia a per-path diff engine. TheConfigStorecompares the original and modified config recursively, then callsmodify()on each changed JSON path individually. Comments, trailing commas, and formatting on untouched keys survive every edit. - Profiles — snapshot the current
agentsandcategoriessections into named profiles stored in a sidecar file (omo.profiles.json). Switch between them instantly with full JSONC preservation. Each profile can carry an optional description. Active profile is marked with a check icon and(active)label. - Profile import and export — move individual profiles or your whole sidecar in and out as JSON or JSONC. Import a single
{ agents, categories }fragment, or import a full{ version: 1, profiles: [...] }sidecar and choose whether to extend the existing list or replace it. Export one profile or every profile at once. - JSON profile editing — open a saved profile or the active config's
agents/categoriesas JSON in an untitled editor, edit freely, and save to apply. Useful for bulk changes that the form editor does not expose. - Sidebar integration — the
Oh My OpenAgentactivity bar view puts everything one click away. Three collapsible groups (Agents, Categories, Profiles) with inline edit buttons, context menu actions, and tooltips that show configured parameters on hover. - Commands where they belong:
Open Agent Manager,Refresh,Create Profile,Import Profiles, andExport All Profilesare available from the Command Palette and view title. Editing, override management, profile actions, and JSON editing appear only when their sidebar context applies.
Requirements
- VS Code 1.85 or newer
- An existing Oh My OpenAgent configuration, or a first-run scenario where the extension will create one for you
Development and packaging require Node.js 22 or newer.
The extension edits the [opencode] block of the unified omo config:
- User layer —
~/.omo/omo.jsonc(falling back toomo.json) on every platform. This is where all writes go. - Project layers —
.omo/omo.jsonc(then.omo/omo.json) in every directory from the workspace root up to your home directory. The nearest project file wins on read and beats the user layer; the extension never writes to project files.
On a fresh install the extension creates ~/.omo/omo.jsonc on first write. Legacy oh-my-openagent.json[c] / oh-my-opencode.json[c] files are no longer read — run bunx oh-my-openagent config migrate once to import them into the unified file.
Profiles live next to the user config in omo.profiles.json. A legacy oh-my-openagent.profiles.json sidecar is renamed automatically on first access.
Installation
From a .vsix file
- Download
oh-my-openagent-vscode-X.Y.Z.vsixfrom the release page. - Open VS Code and run
Extensions: Install from VSIX...from the Command Palette. - Select the downloaded file.
Development build
-
Clone the repository:
git clone https://github.com/oh-my-openagent/oh-my-openagent-vscode.git cd oh-my-openagent-vscode -
Install dependencies:
npm install -
Compile:
npm run compile -
Press
F5to open the Extension Development Host with the extension loaded.
Usage
Opening the view
- Click the
Oh My OpenAgentactivity bar icon (robot symbol). - Or run the command
Oh My OpenAgent: Open Agent Managerfrom the Command Palette.
The sidebar nests the tree under the active config file:
- Active config file — the name of the user-layer file (e.g.
omo.jsonc). Hover to see the full resolved path. It expands to show the active profile (when one is active) and, nested under it, the Agents and Categories groups. When no profile is active, Agents and Categories appear directly under the config file. - Agents — built-in agents; overridden agents are shown as override items.
- Categories — built-in categories; overridden categories are shown as override items.
- Profiles — saved snapshots of your agents and categories. A root-level sibling of the config file.
Agents, categories, and profiles are expandable when they contain configured values:
- An expanded agent or category shows its configured parameters (
variant,reasoning,temperature,top_p,maxTokens,thinking,verbosity,disabled) and afallbacks (N)group with each fallback model. - An expanded profile shows nested Agents and Categories subgroups with the same
name → modelleaves you see in the main tree, so you can inspect what the profile captured before activating it.
The Profiles group always shows the active state: when no profile is active, a No active profile indicator appears at the top of the group. The active profile is marked with a check icon and an (active) description.
Editing an agent or category
- Hover over the agent or category and click the pencil inline action, or right-click and choose
Edit Agent/Edit Category. Both built-in items, existing override items, and profile-contained items can be edited. - The webview editor opens with sections for Model, Sampling, Thinking, and Fallback models.
- The cards form one ordered model list. Position 1 is Main, and later cards are fallbacks. Drag any fallback into position 1 to replace Main. Hover over a
Draghandle for this reminder. For keyboard reordering, focus a handle, press Space or Enter to pick up, use Arrow Up or Arrow Down to move, press Space or Enter to drop, or Escape to cancel. There are no separate promotion or move controls. - The main Model field is a free-form text input with a lazy datalist. While the editor loads, the extension runs
opencode models --verboselocally and offers the returned model IDs as autocomplete suggestions, together with each model's capabilities and variants. Any existing model value is preserved, even if it is not in the discovered list. - Edit Sampling and Thinking below the cards to set the shared defaults. They apply to Main and to every fallback field set to
Inherit default. In a fallback's Advanced section, useOverridefor a model-specific value orInherit defaultto use the shared value. Only explicit fallback overrides are written to that fallback entry. - When a fallback becomes Main, its explicit overrides become the active shared defaults. Its
Inherit defaultandOverridechoices remain attached to the model and return when it becomes a fallback again during the same editor session. - Hover over the reload button next to the Model field to re-run discovery and refresh the model list at any time. If discovered capabilities do not support an effective setting, including an inherited setting, the editor marks the conflict and blocks saving until you clear or change it.
- Use a fallback's Remove button to remove it. Removing the final fallback explicitly removes the fallback chain from the saved config.
- Change values and click Save. Saving a built-in agent or category creates its override in the active config. The sidebar refreshes after the JSONC-preserving write completes. Fields that are not exposed in the form, such as
permission,tools,prompt, andproviderOptions, are preserved rather than overwritten. - The upstream config can store only top-level defaults and fallback entries. It cannot represent the fallback intent attached to the model that is currently Main after the editor is closed and reopened. Transient webview restoration preserves dirty in-panel state, but a saved config cannot preserve that Main model's fallback intent across a new editor session.
- Hover over any agent or category leaf in the sidebar to see a tooltip with the configured parameters (temperature, top-p, max tokens, reasoning effort, thinking budget, variant, and fallback models).
Context menu actions
Right-click items in the Models view for more options:
- On a built-in agent:
Edit Agentopens the editor. Saving creates the agent override. - On a built-in category:
Edit Categoryopens the editor. Saving creates the category override. - On an override item:
Edit Agent/Edit Categoryopens the editor. - On a profile:
Activate,Rename,Duplicate,Delete,Export Profile, orEdit Profile JSON. - Profile-contained agent/category leaves reuse the same
Edit Agent/Edit Categorycommands as the main tree. - On a modified active profile:
Save Active Profilesnapshots the current config back into that profile. - On the active config file item:
Edit Active Profile JSONopens the active config'sagents/categoriesas JSON. - On the Profiles group header:
Create Profile from Config File…,Import Profiles, orExport All Profiles.
The view title also provides Refresh, Create Profile, Import Profiles, and Export All Profiles buttons.
Commands
The extension contributes 16 commands. All are prefixed with Oh My OpenAgent. Open Agent Manager, Refresh, Create Profile, Create Profile from Config File…, Import Profiles, and Export All Profiles are visible in the Command Palette and view title. The remaining commands are contextual sidebar actions.
| Command | Availability | What it does |
|---|---|---|
Open Agent Manager | Command Palette | Focuses the Oh My OpenAgent sidebar view. |
Edit Agent | Contextual | Opens the editor for the selected agent. |
Edit Category | Contextual | Opens the editor for the selected category. |
Refresh | Command Palette and view title | Refreshes the Models tree from disk. |
Create Profile | Command Palette and view title | Creates a new profile from the current config. |
Create Profile from Config File… | Command Palette | Creates a profile from a .json or .jsonc Oh My OpenAgent config file's agents and categories sections. The active config is not modified. |
Activate Profile | Contextual | Applies the selected profile to the active config. |
Rename Profile | Contextual | Renames the selected profile. |
Duplicate Profile | Contextual | Creates a copy of the selected profile. |
Delete Profile | Contextual | Deletes the selected profile after confirmation. |
Save Active Profile | Contextual | Saves the current config into the modified active profile. |
Import Profiles | Command Palette and view title | Imports one .json or .jsonc file as a single profile fragment or a full sidecar. |
Export All Profiles | Command Palette and view title | Exports the full sidecar to a JSON file. |
Export Profile | Contextual | Exports the selected profile as a single { agents, categories } fragment. |
Edit Profile JSON | Contextual | Opens the selected saved profile's agents/categories as JSON for editing. |
Edit Active Profile JSON | Contextual | Opens the active config's agents/categories as JSON for editing. |
Profiles
Profiles are named snapshots of the agents and categories sections of your active config. They are stored in omo.profiles.json, next to the user config file.
Create a profile
- Click the
Create Profileicon in the Models view title, or runOh My OpenAgent: Create Profile. - Enter a unique profile name.
- Optionally enter a description.
The new profile captures the current agents and categories exactly as they are on disk.
To create a profile from another config file instead of the active config, use Create Profile from Config File… (Command Palette or right-click the Profiles group header). It extracts agents and categories from any Oh My OpenAgent .json/.jsonc config — a full omo.jsonc works (the [opencode] block is merged over the shared base); other top-level keys are ignored — and never modifies the active config.
Activate a profile
- Right-click the profile in the sidebar and choose
Activate. - The active config's
agentsandcategoriessections are replaced with the profile's values.
Activation preserves comments and trailing commas in the active config because it reuses the same JSONC-preserving write path as the editor.
Rename, duplicate, or delete a profile
- Rename updates the profile name. If it was the active profile,
lastActiveProfileis updated automatically. - Duplicate creates a deep copy under a new name; the original is unchanged.
- Delete asks for confirmation and removes the profile. If it was the active profile, the active marker is cleared.
Import and export format
The transfer layer accepts both single-profile fragments and full sidecar files. Input may be JSON or JSONC (comments and trailing commas are allowed). Output is always canonical JSON with two-space indentation and sorted object keys.
A single-profile fragment contains only the profile payload:
{
"agents": {
"sisyphus": {
"model": "opencode-go/kimi-k2.7-code"
}
},
"categories": {
}
}
At least one of agents or categories must be present. The top-level keys version and profiles are reserved for sidecar files; a fragment cannot mix them with agents or categories.
A full sidecar follows this shape:
{
"version": 1,
"profiles": [
{
"name": "fast",
"agents": {
"sisyphus": {
"model": "opencode-go/kimi-k2.7-code"
}
},
"createdAt": "2026-07-23T12:00:00Z",
"updatedAt": "2026-07-23T12:00:00Z"
}
],
"lastActiveProfile": "fast"
}
version must be 1. profiles is required. lastActiveProfile is optional. Each profile object may contain name, description, agents, categories, createdAt, and updatedAt. Timestamps must be valid ISO 8601 strings.
Import semantics:
- JSONC input is accepted; the parser allows comments and trailing commas. Canonical JSON is produced on export.
- Semantic losslessness: values and structure survive, but comments, formatting, and key order are normalized. Export sorts object keys alphabetically.
- Extend (default) appends imported profiles to the existing list. Replace overwrites the existing list.
- Name collisions are resolved with exact case-sensitive matching. If
fastalready exists, the imported profile becomesfast-2, thenfast-3, and so on. - Size limit: transfer inputs are rejected when they exceed 5 MiB. UTF-8 is required; a leading UTF-8 BOM is accepted and stripped.
- Imported
lastActiveProfileis ignored. Your local active marker is preserved only when a profile with that exact name still exists after the import. - No auto-activation: importing a profile or sidecar updates the sidecar file only; the active config is not changed.
Export semantics:
- Single-profile export writes
{ agents?, categories? }as a fragment file. - Full sidecar export writes
{ version: 1, profiles: [...], lastActiveProfile? }. - Provider options warning: export warns when a profile contains non-empty
providerOptions, because provider options often hold credentials or API keys. The warning is informational; you can cancel or proceed. - Active saved-profile save: when you save a modified active profile, the active config is updated first, then the profile is snapshotted. If snapshotting fails, the config change remains and the sidecar stays unchanged, so partial failures leave the live config in the intended state.
Architecture
The extension follows a clean layered architecture with strict separation of concerns:
extension.ts (activation orchestrator)
|
├── commands.ts (16 command registrations)
|
├── profileTransferCommands.ts (import/export/JSON-edit handlers)
│ ├── profileTransferCommandHandlers.ts
│ └── profileTransferFiles.ts (VS Code open/save dialog helpers)
|
├── ui/
│ ├── agentModelTreeProvider.ts (sidebar TreeDataProvider)
│ ├── agentEditorPanel.ts (webview panel singleton and JSON editor host)
│ ├── profileJsonEditorHost.ts (active/saved profile JSON editing protocol)
│ └── webview/ (ordered model routing, capability validation, and ProfileJsonEditor)
│ ├── App.svelte / AgentFormEditor.svelte
│ └── ProfileJsonEditor.svelte
│
└── config/
├── schema.ts (TypeScript types for OmO config)
├── configStore.ts (JSONC read/write, file watching)
├── profileStore.ts (profile CRUD, activation, import/export)
├── profileTransfer.ts (parser and byte-limit/UTF-8 validation)
├── profileValidation.ts (fragment/sidecar runtime validator)
├── profileEntryValidation.ts (per-field validation rules)
└── profileTransferSerialization.ts (canonical JSON, name collision, export helpers)
Key design decisions
- ConfigStore is pure Node.js — zero VS Code dependency, making it testable in isolation with vitest. File watching uses
fs.watchwith 150 ms debounce and asuppressWatchflag to ignore self-triggered events during atomic writes. - Atomic writes everywhere — both
ConfigStoreandProfileStorewrite via temp-file +fs.renameSync, guaranteeing no partial content even on crash. - Per-path JSONC diffing —
updateConfig()deep-clones the parsed config, runs the updater callback, thendiffConfigs()recursively compares original and draft. Each changed JSON path gets its ownjsonc-parsermodify()call, so comments and formatting on untouched keys are never disturbed. - Webview security — strict CSP with
default-src 'none', per-render nonces viacrypto.randomBytes(16), local resource roots restricted toout/only, and all DOM text insertion uses.textContent(neverinnerHTML). - Singleton editor panel —
AgentEditorPaneluses a staticcurrentPanelreference to prevent multiple webview instances. Panel state survives tab switches viaretainContextWhenHidden: true. - Sidecar profiles — profiles are stored in a separate plain JSON file (
omo.profiles.json) so the main OmO config stays schema-clean. Profile activation writes into the main config's[opencode]block through the JSONC-preservingConfigStore.updateConfig()path. - Transfer canonical JSON — export serializes only JSON-safe values (null, booleans, finite numbers, strings, dense arrays, and plain objects), sorts keys, and appends a trailing newline. This prevents accidental disclosure of getters, symbols, or cyclic structures.
- Profile JSON editor host —
profileJsonEditorHost.tsisolates the active/saved target protocol from the UI, whileProfileJsonEditor.svelteprovides a focused textarea with save, dirty tracking, and error display.
Built-in inventory
| Domain | Items |
|---|---|
| Agents | sisyphus, hephaestus, prometheus, oracle, librarian, explore, multimodal-looker, metis, momus, atlas, sisyphus-junior (11 total) |
| Categories | visual-engineering, ultrabrain, deep, artistry, quick, unspecified-low, unspecified-high, writing (8 total) |
Development
This extension is built with TypeScript and esbuild. Development and packaging require Node.js 22 or newer. The extension itself runs on VS Code 1.85 or newer.
# Install dependencies
npm install
# One-shot compile
npm run compile
# Watch mode
npm run watch
# Run tests
npm test
# Package for release
npm run package
To run the extension locally, press F5 in VS Code. This opens the Extension Development Host with the compiled extension loaded.
Tests
Tests are written with Vitest. The suite covers the extension's main behaviors:
| Suite | What it covers |
|---|---|
modelDiscovery.test.ts | opencode models parsing, fallback handling, metadata normalization, and cache behavior |
agentModelTreeProvider.test.ts | Tree structure, context values, override detection, active profile indicators, refresh, and disposal |
agentEditorPanel.test.ts | Webview lifecycle, messages, save writes, profile creation, and model discovery reloads |
profileStore.test.ts | Profile creation, rename, duplicate, isolation, activation, save-back, import, export, and change events |
profileTransfer.test.ts | Byte limits, UTF-8 handling, JSONC parsing, duplicate-key detection, and fragment/sidecar classification |
profileTransferSerialization.test.ts | Canonical JSON serialization, name derivation, collision resolution, and provider-options detection |
profileValidation.test.ts | Fragment and sidecar validation, timestamp checks, nested field policies, and unknown-key rejection |
profileTransferCommands.test.ts | Import/export/JSON-edit command handlers, dialogs, cancellation, and error reporting |
profileTransferFiles.test.ts | VS Code open/save dialog helpers and successful transfer-file operations |
profileTransferFiles.rejections.test.ts | Primitive rejection contract for transfer-file dialog failures |
smoke.test.ts | End-to-end editor saves across stores, panel, tree, JSONC writes, profile transfer round-trips, and JSON editing |
packageMenus.test.ts | The 15-command contribution surface and contextual menu visibility |
configStore.test.ts | Config discovery, JSONC parsing, formatting-preserving updates, key removal, and file watching |
modelRouting.test.ts | Ordered-card promotion, shared defaults, fallback inheritance and overrides, serialization, removal, and session-bound routing intent |
modelCapabilities.test.ts | Capability validation for effective inherited and overridden settings |
webview.test.ts | Model picker behavior, ordered-list editor integration, drag promotion, fallback editing, profile JSON editor UI, and persisted state |
editorPayloadValidation.test.ts | Structured-editor allow-lists for agent and category save payloads |
reorder.test.ts | Fallback list reordering via drag and keyboard |
schema.test.ts | Profile schema contract and shape invariants |
The JSONC preservation tests verify that comments, trailing commas, and formatting survive round-trips through updateConfig() and profile activation — confirmed against real config fixtures with inline comments and trailing commas.
npm test
Release / packaging
-
Update the version in
package.json. -
Run the packaging command:
npm run packagenpm run packageproduces a.vsixfile in the current directory. -
The resulting
.vsixfile can be uploaded to a release page or installed directly.
The package script uses the maintained @vscode/vsce packaging tool. The packaging rules in .vscodeignore make sure out/extension.js, out/webview.js, src/ui/webview/webview.html, and src/ui/webview/webview.css are included, while source maps, tests, and node_modules are excluded. Generated VSIX files and verification evidence are kept in .omo/evidence and are not committed.
License
MIT License

