omoctl 0.1.0__tar.gz

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.
@@ -0,0 +1,51 @@
1
+ name: Release to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*.*.*"
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ build:
11
+ name: Build distributions
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+
16
+ - name: Install uv
17
+ uses: astral-sh/setup-uv@v6
18
+ with:
19
+ python-version: "3.12"
20
+
21
+ - name: Build sdist + wheel
22
+ run: uv build
23
+
24
+ - name: Verify metadata
25
+ run: |
26
+ uv tool run --from twine twine check dist/*
27
+
28
+ - name: Upload distributions
29
+ uses: actions/upload-artifact@v4
30
+ with:
31
+ name: dist
32
+ path: dist/
33
+
34
+ publish:
35
+ name: Publish to PyPI
36
+ needs: build
37
+ runs-on: ubuntu-latest
38
+ environment:
39
+ name: pypi
40
+ url: https://pypi.org/p/omoctl
41
+ permissions:
42
+ id-token: write
43
+ steps:
44
+ - name: Download distributions
45
+ uses: actions/download-artifact@v4
46
+ with:
47
+ name: dist
48
+ path: dist/
49
+
50
+ - name: Publish to PyPI
51
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,7 @@
1
+ .venv
2
+
3
+ dist
4
+
5
+ __pycache__
6
+ .mypy_cache
7
+ .ruff_cache
omoctl-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 André Ferreira
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
omoctl-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,244 @@
1
+ Metadata-Version: 2.4
2
+ Name: omoctl
3
+ Version: 0.1.0
4
+ Summary: CLI tool for managing oh-my-openagent (OMO) profiles
5
+ Project-URL: Homepage, https://github.com/anfreire/omoctl
6
+ Project-URL: Repository, https://github.com/anfreire/omoctl
7
+ Project-URL: Issues, https://github.com/anfreire/omoctl/issues
8
+ Author-email: André Ferreira <anfreire.dev@gmail.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: ai-agents,cli,oh-my-openagent,omo,opencode,profile-manager
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Operating System :: MacOS
16
+ Classifier: Operating System :: POSIX :: Linux
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Software Development
22
+ Classifier: Topic :: Utilities
23
+ Requires-Python: >=3.11
24
+ Requires-Dist: dacite>=1.8
25
+ Requires-Dist: pyyaml>=6.0
26
+ Description-Content-Type: text/markdown
27
+
28
+ # omoctl
29
+
30
+ CLI tool for managing [oh-my-openagent](https://github.com/code-yeongyu/oh-my-openagent) profiles in [OpenCode](https://opencode.ai).
31
+
32
+ Define profiles, patch models across providers, and switch between configurations with a single command.
33
+
34
+ ## Run
35
+
36
+ No install needed — run on demand with `uvx`:
37
+
38
+ ```bash
39
+ uvx omoctl --help
40
+ uvx omoctl update
41
+ uvx omoctl switch claude
42
+ ```
43
+
44
+ > The first run downloads the package; subsequent runs are cached.
45
+
46
+ ### Prerequisites
47
+
48
+ - Python 3.11+
49
+ - [bun](https://bun.sh) (for fetching OMO configs via `oh-my-opencode`)
50
+ - OpenCode installed with a populated model cache (`~/.cache/opencode/models.json`)
51
+
52
+ ## Quick Start
53
+
54
+ ```bash
55
+ omoctl update # fetch & build all profiles
56
+ omoctl list # see what's available
57
+ omoctl switch claude # activate a profile
58
+ omoctl # show active profile
59
+ omoctl validate # check config against available models/agents
60
+ ```
61
+
62
+ ## Commands
63
+
64
+ | Command | Description |
65
+ |---|---|
66
+ | `omoctl` | Show active profile |
67
+ | `omoctl list` | List all profiles |
68
+ | `omoctl switch <profile>` | Switch to a profile (by name or alias) |
69
+ | `omoctl update [profile]` | Fetch fresh OMO configs, apply patches, save. All profiles if omitted |
70
+ | `omoctl remove <profile>` | Remove a stored profile |
71
+ | `omoctl validate` | Validate config against available models, agents, and categories |
72
+ | `omoctl show [-a\|-n\|-j]` | Show active profile: header + JSON by default; `-a` alias only, `-n` name only, `-j` JSON only |
73
+ | `omoctl version` | Print version |
74
+
75
+ ## Config
76
+
77
+ Located at `~/.config/omoctl/config.yaml`. Created on first run.
78
+
79
+ ### Minimal example
80
+
81
+ ```yaml
82
+ profiles:
83
+ - name: Claude
84
+ providers: [claude]
85
+ ```
86
+
87
+ That's it. One profile, one provider. Run `omoctl update` and you're done.
88
+
89
+ ### Full example
90
+
91
+ ```yaml
92
+ active_profile: no-copilot
93
+
94
+ defaults:
95
+ disabled_hooks:
96
+ - context-window-monitor
97
+
98
+ patches:
99
+ - source: { provider: google }
100
+ target: { provider: proxy }
101
+
102
+ profiles:
103
+ - name: Claude
104
+ providers: [claude]
105
+
106
+ - name: Claude & OpenAI
107
+ providers: [claude, openai]
108
+
109
+ - name: No Copilot
110
+ providers: [claude, gemini, openai]
111
+ patches:
112
+ - source: { provider: google, model: gemini-3.1-pro-preview }
113
+ target: { provider: proxy, model: gemini-3-1-pro-xhigh, variant: null }
114
+ overrides:
115
+ disabled_hooks:
116
+ - context-window-monitor
117
+ - some-other-hook
118
+ ```
119
+
120
+ ### Fields
121
+
122
+ | Field | Type | Description |
123
+ |---|---|---|
124
+ | `active_profile` | string | Profile to auto-activate after `update`. Optional |
125
+ | `defaults` | dict | OMO config overrides applied to all profiles |
126
+ | `patches` | list | Global patches applied to all profiles (see [Patches](#patches)) |
127
+ | `profiles` | list | Profile definitions (at least one required) |
128
+
129
+ ### Profile fields
130
+
131
+ | Field | Type | Description |
132
+ |---|---|---|
133
+ | `name` | string | **Required.** Display name. Also determines the alias (e.g. `"No Copilot"` -> `no-copilot`) |
134
+ | `providers` | list | **Required.** OMO providers to enable. Run `omoctl validate` to see available providers |
135
+ | `patches` | list | Profile-specific patches. Take priority over global patches |
136
+ | `overrides` | dict | OMO config overrides. Deep-merged on top of `defaults` |
137
+
138
+ ## Patches
139
+
140
+ Patches rewrite models in the OMO config before saving. A patch has a `source` (what to match) and a `target` (what to replace it with).
141
+
142
+ ### Source
143
+
144
+ The source specifies what to match. All fields are optional but at least one must be set.
145
+
146
+ | Field | Type | Description |
147
+ |---|---|---|
148
+ | `provider` | string | Match models from this provider (e.g. `google`, `anthropic`) |
149
+ | `model` | string, list, or dict | Filter which models to match (see [Model Filters](#model-filters)) |
150
+ | `agent` | string | Match a specific agent (e.g. `sisyphus`, `oracle`) |
151
+ | `category` | string | Match a specific category (e.g. `deep`, `quick`) |
152
+
153
+ These combine: `{ agent: sisyphus, provider: google }` matches sisyphus only when it uses a google model.
154
+
155
+ ### Target
156
+
157
+ | Field | Type | Description |
158
+ |---|---|---|
159
+ | `provider` | string | Target provider. Falls back to source provider if omitted |
160
+ | `model` | string, list, or dict | Target model (see [Model Filters](#model-filters)) |
161
+ | `variant` | string or null | `"max"` sets variant, `null` removes it, omit to keep existing |
162
+
163
+ ### Examples
164
+
165
+ ```yaml
166
+ patches:
167
+ # Redirect all google models to a proxy provider
168
+ - source: { provider: google }
169
+ target: { provider: proxy }
170
+
171
+ # Redirect a specific model to a specific target
172
+ - source: { provider: google, model: gemini-3.1-pro-preview }
173
+ target: { provider: proxy, model: gemini-3-1-pro-xhigh, variant: null }
174
+
175
+ # Override a specific agent
176
+ - source: { agent: sisyphus }
177
+ target: { provider: anthropic, model: claude-opus-4-7, variant: max }
178
+
179
+ # Override a category
180
+ - source: { category: ultrabrain }
181
+ target: { provider: openai, model: gpt-5.4, variant: xhigh }
182
+ ```
183
+
184
+ ### Priority
185
+
186
+ 1. Profile patches are checked before global patches
187
+ 2. Agent/category patches take priority over provider-only patches
188
+ 3. Exact model matches beat filter matches
189
+ 4. More specific filters beat less specific ones
190
+
191
+ ## Model Filters
192
+
193
+ The `model` field in source/target accepts three formats:
194
+
195
+ **Exact match** — a string:
196
+ ```yaml
197
+ model: gemini-3.1-pro-preview
198
+ ```
199
+
200
+ **Keyword filter** — a list of terms that must all match:
201
+ ```yaml
202
+ model: [gemini, pro]
203
+ ```
204
+
205
+ **Include/exclude filter** — fine-grained control:
206
+ ```yaml
207
+ model:
208
+ include: [gemini, pro]
209
+ exclude: [flash]
210
+ ```
211
+
212
+ Model IDs are split into words and numbers (e.g. `claude-opus-4-7` -> words: `[claude, opus]`, numbers: `[4, 7]`). Filters match against these parts.
213
+
214
+ ## File Layout
215
+
216
+ ```
217
+ ~/.config/omoctl/
218
+ config.yaml # your config
219
+ active # current active profile alias
220
+ profiles/
221
+ claude.json # stored OMO config per profile
222
+ no-copilot.json
223
+
224
+ ~/.config/opencode/
225
+ oh-my-openagent.jsonc # active profile config (plain JSON, read by oh-my-openagent plugin)
226
+ ```
227
+
228
+ ## Validation
229
+
230
+ `omoctl validate` checks your config against live data:
231
+
232
+ - Patch source/target **providers** exist in the model cache
233
+ - Patch source/target **models** exist in their provider
234
+ - Patch **agent** names exist in the OMO config
235
+ - Patch **category** names exist in the OMO config
236
+
237
+ On failure, it prints each error with available options:
238
+
239
+ ```
240
+ Validation failed with 2 error(s):
241
+
242
+ • global patch [0]: source provider 'nonexistent' not found.
243
+ • profile 'Test' patch [0]: source agent 'fake' not found. Available: atlas, explore, ...
244
+ ```
omoctl-0.1.0/README.md ADDED
@@ -0,0 +1,217 @@
1
+ # omoctl
2
+
3
+ CLI tool for managing [oh-my-openagent](https://github.com/code-yeongyu/oh-my-openagent) profiles in [OpenCode](https://opencode.ai).
4
+
5
+ Define profiles, patch models across providers, and switch between configurations with a single command.
6
+
7
+ ## Run
8
+
9
+ No install needed — run on demand with `uvx`:
10
+
11
+ ```bash
12
+ uvx omoctl --help
13
+ uvx omoctl update
14
+ uvx omoctl switch claude
15
+ ```
16
+
17
+ > The first run downloads the package; subsequent runs are cached.
18
+
19
+ ### Prerequisites
20
+
21
+ - Python 3.11+
22
+ - [bun](https://bun.sh) (for fetching OMO configs via `oh-my-opencode`)
23
+ - OpenCode installed with a populated model cache (`~/.cache/opencode/models.json`)
24
+
25
+ ## Quick Start
26
+
27
+ ```bash
28
+ omoctl update # fetch & build all profiles
29
+ omoctl list # see what's available
30
+ omoctl switch claude # activate a profile
31
+ omoctl # show active profile
32
+ omoctl validate # check config against available models/agents
33
+ ```
34
+
35
+ ## Commands
36
+
37
+ | Command | Description |
38
+ |---|---|
39
+ | `omoctl` | Show active profile |
40
+ | `omoctl list` | List all profiles |
41
+ | `omoctl switch <profile>` | Switch to a profile (by name or alias) |
42
+ | `omoctl update [profile]` | Fetch fresh OMO configs, apply patches, save. All profiles if omitted |
43
+ | `omoctl remove <profile>` | Remove a stored profile |
44
+ | `omoctl validate` | Validate config against available models, agents, and categories |
45
+ | `omoctl show [-a\|-n\|-j]` | Show active profile: header + JSON by default; `-a` alias only, `-n` name only, `-j` JSON only |
46
+ | `omoctl version` | Print version |
47
+
48
+ ## Config
49
+
50
+ Located at `~/.config/omoctl/config.yaml`. Created on first run.
51
+
52
+ ### Minimal example
53
+
54
+ ```yaml
55
+ profiles:
56
+ - name: Claude
57
+ providers: [claude]
58
+ ```
59
+
60
+ That's it. One profile, one provider. Run `omoctl update` and you're done.
61
+
62
+ ### Full example
63
+
64
+ ```yaml
65
+ active_profile: no-copilot
66
+
67
+ defaults:
68
+ disabled_hooks:
69
+ - context-window-monitor
70
+
71
+ patches:
72
+ - source: { provider: google }
73
+ target: { provider: proxy }
74
+
75
+ profiles:
76
+ - name: Claude
77
+ providers: [claude]
78
+
79
+ - name: Claude & OpenAI
80
+ providers: [claude, openai]
81
+
82
+ - name: No Copilot
83
+ providers: [claude, gemini, openai]
84
+ patches:
85
+ - source: { provider: google, model: gemini-3.1-pro-preview }
86
+ target: { provider: proxy, model: gemini-3-1-pro-xhigh, variant: null }
87
+ overrides:
88
+ disabled_hooks:
89
+ - context-window-monitor
90
+ - some-other-hook
91
+ ```
92
+
93
+ ### Fields
94
+
95
+ | Field | Type | Description |
96
+ |---|---|---|
97
+ | `active_profile` | string | Profile to auto-activate after `update`. Optional |
98
+ | `defaults` | dict | OMO config overrides applied to all profiles |
99
+ | `patches` | list | Global patches applied to all profiles (see [Patches](#patches)) |
100
+ | `profiles` | list | Profile definitions (at least one required) |
101
+
102
+ ### Profile fields
103
+
104
+ | Field | Type | Description |
105
+ |---|---|---|
106
+ | `name` | string | **Required.** Display name. Also determines the alias (e.g. `"No Copilot"` -> `no-copilot`) |
107
+ | `providers` | list | **Required.** OMO providers to enable. Run `omoctl validate` to see available providers |
108
+ | `patches` | list | Profile-specific patches. Take priority over global patches |
109
+ | `overrides` | dict | OMO config overrides. Deep-merged on top of `defaults` |
110
+
111
+ ## Patches
112
+
113
+ Patches rewrite models in the OMO config before saving. A patch has a `source` (what to match) and a `target` (what to replace it with).
114
+
115
+ ### Source
116
+
117
+ The source specifies what to match. All fields are optional but at least one must be set.
118
+
119
+ | Field | Type | Description |
120
+ |---|---|---|
121
+ | `provider` | string | Match models from this provider (e.g. `google`, `anthropic`) |
122
+ | `model` | string, list, or dict | Filter which models to match (see [Model Filters](#model-filters)) |
123
+ | `agent` | string | Match a specific agent (e.g. `sisyphus`, `oracle`) |
124
+ | `category` | string | Match a specific category (e.g. `deep`, `quick`) |
125
+
126
+ These combine: `{ agent: sisyphus, provider: google }` matches sisyphus only when it uses a google model.
127
+
128
+ ### Target
129
+
130
+ | Field | Type | Description |
131
+ |---|---|---|
132
+ | `provider` | string | Target provider. Falls back to source provider if omitted |
133
+ | `model` | string, list, or dict | Target model (see [Model Filters](#model-filters)) |
134
+ | `variant` | string or null | `"max"` sets variant, `null` removes it, omit to keep existing |
135
+
136
+ ### Examples
137
+
138
+ ```yaml
139
+ patches:
140
+ # Redirect all google models to a proxy provider
141
+ - source: { provider: google }
142
+ target: { provider: proxy }
143
+
144
+ # Redirect a specific model to a specific target
145
+ - source: { provider: google, model: gemini-3.1-pro-preview }
146
+ target: { provider: proxy, model: gemini-3-1-pro-xhigh, variant: null }
147
+
148
+ # Override a specific agent
149
+ - source: { agent: sisyphus }
150
+ target: { provider: anthropic, model: claude-opus-4-7, variant: max }
151
+
152
+ # Override a category
153
+ - source: { category: ultrabrain }
154
+ target: { provider: openai, model: gpt-5.4, variant: xhigh }
155
+ ```
156
+
157
+ ### Priority
158
+
159
+ 1. Profile patches are checked before global patches
160
+ 2. Agent/category patches take priority over provider-only patches
161
+ 3. Exact model matches beat filter matches
162
+ 4. More specific filters beat less specific ones
163
+
164
+ ## Model Filters
165
+
166
+ The `model` field in source/target accepts three formats:
167
+
168
+ **Exact match** — a string:
169
+ ```yaml
170
+ model: gemini-3.1-pro-preview
171
+ ```
172
+
173
+ **Keyword filter** — a list of terms that must all match:
174
+ ```yaml
175
+ model: [gemini, pro]
176
+ ```
177
+
178
+ **Include/exclude filter** — fine-grained control:
179
+ ```yaml
180
+ model:
181
+ include: [gemini, pro]
182
+ exclude: [flash]
183
+ ```
184
+
185
+ Model IDs are split into words and numbers (e.g. `claude-opus-4-7` -> words: `[claude, opus]`, numbers: `[4, 7]`). Filters match against these parts.
186
+
187
+ ## File Layout
188
+
189
+ ```
190
+ ~/.config/omoctl/
191
+ config.yaml # your config
192
+ active # current active profile alias
193
+ profiles/
194
+ claude.json # stored OMO config per profile
195
+ no-copilot.json
196
+
197
+ ~/.config/opencode/
198
+ oh-my-openagent.jsonc # active profile config (plain JSON, read by oh-my-openagent plugin)
199
+ ```
200
+
201
+ ## Validation
202
+
203
+ `omoctl validate` checks your config against live data:
204
+
205
+ - Patch source/target **providers** exist in the model cache
206
+ - Patch source/target **models** exist in their provider
207
+ - Patch **agent** names exist in the OMO config
208
+ - Patch **category** names exist in the OMO config
209
+
210
+ On failure, it prints each error with available options:
211
+
212
+ ```
213
+ Validation failed with 2 error(s):
214
+
215
+ • global patch [0]: source provider 'nonexistent' not found.
216
+ • profile 'Test' patch [0]: source agent 'fake' not found. Available: atlas, explore, ...
217
+ ```
@@ -0,0 +1,44 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "omoctl"
7
+ version = "0.1.0"
8
+ description = "CLI tool for managing oh-my-openagent (OMO) profiles"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [
14
+ { name = "André Ferreira", email = "anfreire.dev@gmail.com" },
15
+ ]
16
+ keywords = ["omo", "oh-my-openagent", "opencode", "cli", "profile-manager", "ai-agents"]
17
+ classifiers = [
18
+ "Development Status :: 3 - Alpha",
19
+ "Environment :: Console",
20
+ "Intended Audience :: Developers",
21
+ "Operating System :: POSIX :: Linux",
22
+ "Operating System :: MacOS",
23
+ "Programming Language :: Python :: 3",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ "Programming Language :: Python :: 3.13",
27
+ "Topic :: Software Development",
28
+ "Topic :: Utilities",
29
+ ]
30
+ dependencies = [
31
+ "pyyaml>=6.0",
32
+ "dacite>=1.8",
33
+ ]
34
+
35
+ [project.scripts]
36
+ omoctl = "omoctl.cli:main"
37
+
38
+ [project.urls]
39
+ Homepage = "https://github.com/anfreire/omoctl"
40
+ Repository = "https://github.com/anfreire/omoctl"
41
+ Issues = "https://github.com/anfreire/omoctl/issues"
42
+
43
+ [tool.hatch.build.targets.wheel]
44
+ packages = ["src/omoctl"]
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
@@ -0,0 +1,3 @@
1
+ from omoctl.cli import main
2
+
3
+ main()