agent-toggle 0.11.4__tar.gz → 0.12.1__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.
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/PKG-INFO +17 -15
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/README.md +15 -14
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/__init__.py +1 -1
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/backends/mcp_json.py +1 -1
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/cli.py +1 -1
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/config.py +4 -4
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/cost.py +1 -1
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/doctor.py +1 -1
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/fs.py +9 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/harnesses.py +8 -3
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/locales/zh_tw_config.py +1 -1
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/mechanisms.py +5 -4
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/ui/config_menu.py +2 -2
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle.egg-info/PKG-INFO +17 -15
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle.egg-info/requires.txt +1 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/pyproject.toml +3 -1
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_backends.py +6 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_config.py +2 -2
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_config_menu.py +3 -3
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_cost.py +10 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_harnesses.py +6 -1
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/LICENSE +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/__main__.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/backends/__init__.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/backends/flag_json.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/backends/mcp_toml.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/backends/plugin_cli.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/companions.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/help.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/i18n.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/locales/__init__.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/locales/zh_tw_cli.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/locales/zh_tw_common.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/locales/zh_tw_picker.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/ops.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/output.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/profiles.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/settings.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/shims/claude.md.tmpl +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/shims/generic.md.tmpl +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/spinner.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/store.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/toml_check.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/ui/__init__.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/ui/logo.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/ui/menu.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/ui/model.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/ui/picker.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/ui/theme.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/undo.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/update_check.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle/update_prompt.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle.egg-info/SOURCES.txt +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle.egg-info/dependency_links.txt +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle.egg-info/entry_points.txt +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/agent_toggle.egg-info/top_level.txt +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/setup.cfg +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_alias.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_all_type.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_cli.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_cli_surface.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_color.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_companions.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_completions.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_conformance.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_containment.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_crash.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_doctor.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_encoding.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_fs.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_help.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_i18n.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_install.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_logo.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_mechanisms.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_menu.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_mods.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_picker.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_platform.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_profiles.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_project.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_safety.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_same_name.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_secret_audit.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_settings.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_store.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_theme.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_undo.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_update_check.py +0 -0
- {agent_toggle-0.11.4 → agent_toggle-0.12.1}/tests/test_update_prompt.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-toggle
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.12.1
|
|
4
4
|
Summary: Temporarily disable / re-enable AI-agent skills, agents, commands, rules, plugins and MCP servers across harnesses (Claude Code, Codex, Grok, OpenCode, OpenClaw, Copilot, Vibe, Devin, Antigravity)
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/weskao/agent-toggle
|
|
@@ -24,6 +24,7 @@ Classifier: Topic :: Utilities
|
|
|
24
24
|
Requires-Python: >=3.10
|
|
25
25
|
Description-Content-Type: text/markdown
|
|
26
26
|
License-File: LICENSE
|
|
27
|
+
Requires-Dist: telegram-kit<0.3,>=0.2.2
|
|
27
28
|
Provides-Extra: windows
|
|
28
29
|
Requires-Dist: windows-curses; sys_platform == "win32" and extra == "windows"
|
|
29
30
|
Provides-Extra: telegram
|
|
@@ -142,21 +143,20 @@ exits `1`. A shim written by a version before the marker counts as foreign;
|
|
|
142
143
|
delete it and re-run. `--dry-run` shows the same plan, refusals included. The
|
|
143
144
|
tool itself stays in one place.
|
|
144
145
|
|
|
145
|
-
**Optional: Telegram alerts for CI** (for maintainers of a fork or clone).
|
|
146
|
-
|
|
147
|
-
(itself stdlib-only),
|
|
146
|
+
**Optional: Telegram alerts for CI** (for maintainers of a fork or clone). The
|
|
147
|
+
Telegram support comes from [telegram-kit](https://pypi.org/project/telegram-kit/)
|
|
148
|
+
(itself stdlib-only), which every install includes, so a reinstall or upgrade keeps
|
|
149
|
+
your settings. Run the setup once; see [CI notifications](#ci-notifications):
|
|
148
150
|
|
|
149
151
|
```sh
|
|
150
|
-
uv tool install "agent-toggle[telegram]" # or: pipx install "agent-toggle[telegram]"
|
|
151
|
-
pip install -e ".[telegram]" # from a checkout
|
|
152
152
|
agent-toggle config # settings menu: bot token + chat id live under Notifications
|
|
153
153
|
agent-toggle config sync-ci # set the two GitHub repository secrets
|
|
154
154
|
```
|
|
155
155
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
156
|
+
The old `agent-toggle[telegram]` extra still resolves and is now a no-op. If
|
|
157
|
+
telegram-kit is missing from a broken install, `config test` and `config sync-ci`
|
|
158
|
+
exit `4`, bare `config` still opens, and the Telegram rows read
|
|
159
|
+
`telegram-kit missing: reinstall agent-toggle`.
|
|
160
160
|
|
|
161
161
|
**Optional: the Claude Code pane mod** (the picker as a pane inside Claude Code, claude harness
|
|
162
162
|
only). It is not part of the PyPI package; with `agent-toggle` already on `PATH` from one of the
|
|
@@ -204,7 +204,7 @@ agent-toggle <command> [args] # or: python3 agent_toggle.py <command> [
|
|
|
204
204
|
| `undo` | reverse the last logged batch (`--dry-run` shows the plan) |
|
|
205
205
|
| `profile save\|apply\|diff\|list\|delete\|rename` | named sets of live items; see [Profiles](#profiles) |
|
|
206
206
|
| `config` | settings menu (curses, else a numbered list); `config --json` lists every setting and the package version; see [Settings & config menu](#settings--config-menu) |
|
|
207
|
-
| `config test\|sync-ci` | send one Telegram test message / set the GitHub CI secrets;
|
|
207
|
+
| `config test\|sync-ci` | send one Telegram test message / set the GitHub CI secrets; see [CI notifications](#ci-notifications) |
|
|
208
208
|
| `help [command]` | styled help; `help ui` = `--help ui` = `ui --help`; see [Help](#help) |
|
|
209
209
|
| `doctor` | check each harness layout and `state.json` against disk; exit `1` only on an `error` row; on a terminal, asks y/n per fix it can run itself |
|
|
210
210
|
| `migrate` | import an older `~/.claude-toggle/` state, and move legacy `<home>/<dir>-disabled/` park dirs into `~/.agent-toggle/parked/user/` (see [Upgrading the park layout](#upgrading-the-park-layout)); safe to re-run |
|
|
@@ -391,6 +391,8 @@ a band above the prompt, buttons) and change Claude Code's behaviour. A plugin w
|
|
|
391
391
|
`hooks.json`, or with only ordinary hooks (shell commands such as `PreToolUse`), is a plain
|
|
392
392
|
plugin: it ships skills, commands, agents, MCP servers or hooks and draws nothing.
|
|
393
393
|
|
|
394
|
+

|
|
395
|
+
|
|
394
396
|
- The check reads the install path `claude plugin list` reports and every cached version
|
|
395
397
|
under `~/.claude/plugins/cache/<marketplace>/<name>/`, so a parked mod is still a mod.
|
|
396
398
|
To list the installed mods by hand:
|
|
@@ -463,8 +465,8 @@ invalid value in either is ignored with one warning on stderr.
|
|
|
463
465
|
harness is switched off (with one warning on stderr);
|
|
464
466
|
`disable` / `enable --project` without `--harness` still default to `claude`.
|
|
465
467
|
- `language` translates the config menu, help, update prompt and picker chrome; command output stays English.
|
|
466
|
-
-
|
|
467
|
-
|
|
468
|
+
- telegram-kit is a regular dependency now, so reinstalling keeps the Telegram settings working;
|
|
469
|
+
`agent-toggle[telegram]` still resolves.
|
|
468
470
|
|
|
469
471
|
## Update check
|
|
470
472
|
|
|
@@ -523,7 +525,7 @@ logo, centred, when the window is tall enough to fit the whole menu beside it; t
|
|
|
523
525
|
| copilot | `~/.copilot` | ✓ | ✓ | — | — | — | ✓ `mcp-config.json` |
|
|
524
526
|
| vibe | `~/.vibe` | ✓ | — | — | — | — | — |
|
|
525
527
|
| devin | `~/.devin` | — | — | — | — | — | — |
|
|
526
|
-
| agy | `~/.
|
|
528
|
+
| agy | `~/.gemini/config` | ✓ | — | — | — | — | ✓ `mcp_config.json` |
|
|
527
529
|
|
|
528
530
|
Per-harness locations, mechanisms and the harness versions this was checked
|
|
529
531
|
against are in [docs/harnesses.md](docs/harnesses.md).
|
|
@@ -1041,7 +1043,7 @@ agent-toggle config sync-ci # set TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID on
|
|
|
1041
1043
|
|
|
1042
1044
|
In the [settings menu](#settings--config-menu) an inline field commits on `Enter`, cancels on
|
|
1043
1045
|
`Esc`, and clears on `-` then `Enter`; the Tools group also has Send test message and Sync CI
|
|
1044
|
-
secrets (the same as `config test` / `config sync-ci
|
|
1046
|
+
secrets (the same as `config test` / `config sync-ci`). The bot token
|
|
1045
1047
|
is kept only in the OS credential store (macOS Keychain, Linux Secret Service, Windows DPAPI)
|
|
1046
1048
|
through telegram-kit, never in a file or on a command line, and is shown masked. With no
|
|
1047
1049
|
credential store it refuses to store the token; set `TG_BOT_TOKEN` in the environment instead
|
|
@@ -110,21 +110,20 @@ exits `1`. A shim written by a version before the marker counts as foreign;
|
|
|
110
110
|
delete it and re-run. `--dry-run` shows the same plan, refusals included. The
|
|
111
111
|
tool itself stays in one place.
|
|
112
112
|
|
|
113
|
-
**Optional: Telegram alerts for CI** (for maintainers of a fork or clone).
|
|
114
|
-
|
|
115
|
-
(itself stdlib-only),
|
|
113
|
+
**Optional: Telegram alerts for CI** (for maintainers of a fork or clone). The
|
|
114
|
+
Telegram support comes from [telegram-kit](https://pypi.org/project/telegram-kit/)
|
|
115
|
+
(itself stdlib-only), which every install includes, so a reinstall or upgrade keeps
|
|
116
|
+
your settings. Run the setup once; see [CI notifications](#ci-notifications):
|
|
116
117
|
|
|
117
118
|
```sh
|
|
118
|
-
uv tool install "agent-toggle[telegram]" # or: pipx install "agent-toggle[telegram]"
|
|
119
|
-
pip install -e ".[telegram]" # from a checkout
|
|
120
119
|
agent-toggle config # settings menu: bot token + chat id live under Notifications
|
|
121
120
|
agent-toggle config sync-ci # set the two GitHub repository secrets
|
|
122
121
|
```
|
|
123
122
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
123
|
+
The old `agent-toggle[telegram]` extra still resolves and is now a no-op. If
|
|
124
|
+
telegram-kit is missing from a broken install, `config test` and `config sync-ci`
|
|
125
|
+
exit `4`, bare `config` still opens, and the Telegram rows read
|
|
126
|
+
`telegram-kit missing: reinstall agent-toggle`.
|
|
128
127
|
|
|
129
128
|
**Optional: the Claude Code pane mod** (the picker as a pane inside Claude Code, claude harness
|
|
130
129
|
only). It is not part of the PyPI package; with `agent-toggle` already on `PATH` from one of the
|
|
@@ -172,7 +171,7 @@ agent-toggle <command> [args] # or: python3 agent_toggle.py <command> [
|
|
|
172
171
|
| `undo` | reverse the last logged batch (`--dry-run` shows the plan) |
|
|
173
172
|
| `profile save\|apply\|diff\|list\|delete\|rename` | named sets of live items; see [Profiles](#profiles) |
|
|
174
173
|
| `config` | settings menu (curses, else a numbered list); `config --json` lists every setting and the package version; see [Settings & config menu](#settings--config-menu) |
|
|
175
|
-
| `config test\|sync-ci` | send one Telegram test message / set the GitHub CI secrets;
|
|
174
|
+
| `config test\|sync-ci` | send one Telegram test message / set the GitHub CI secrets; see [CI notifications](#ci-notifications) |
|
|
176
175
|
| `help [command]` | styled help; `help ui` = `--help ui` = `ui --help`; see [Help](#help) |
|
|
177
176
|
| `doctor` | check each harness layout and `state.json` against disk; exit `1` only on an `error` row; on a terminal, asks y/n per fix it can run itself |
|
|
178
177
|
| `migrate` | import an older `~/.claude-toggle/` state, and move legacy `<home>/<dir>-disabled/` park dirs into `~/.agent-toggle/parked/user/` (see [Upgrading the park layout](#upgrading-the-park-layout)); safe to re-run |
|
|
@@ -359,6 +358,8 @@ a band above the prompt, buttons) and change Claude Code's behaviour. A plugin w
|
|
|
359
358
|
`hooks.json`, or with only ordinary hooks (shell commands such as `PreToolUse`), is a plain
|
|
360
359
|
plugin: it ships skills, commands, agents, MCP servers or hooks and draws nothing.
|
|
361
360
|
|
|
361
|
+

|
|
362
|
+
|
|
362
363
|
- The check reads the install path `claude plugin list` reports and every cached version
|
|
363
364
|
under `~/.claude/plugins/cache/<marketplace>/<name>/`, so a parked mod is still a mod.
|
|
364
365
|
To list the installed mods by hand:
|
|
@@ -431,8 +432,8 @@ invalid value in either is ignored with one warning on stderr.
|
|
|
431
432
|
harness is switched off (with one warning on stderr);
|
|
432
433
|
`disable` / `enable --project` without `--harness` still default to `claude`.
|
|
433
434
|
- `language` translates the config menu, help, update prompt and picker chrome; command output stays English.
|
|
434
|
-
-
|
|
435
|
-
|
|
435
|
+
- telegram-kit is a regular dependency now, so reinstalling keeps the Telegram settings working;
|
|
436
|
+
`agent-toggle[telegram]` still resolves.
|
|
436
437
|
|
|
437
438
|
## Update check
|
|
438
439
|
|
|
@@ -491,7 +492,7 @@ logo, centred, when the window is tall enough to fit the whole menu beside it; t
|
|
|
491
492
|
| copilot | `~/.copilot` | ✓ | ✓ | — | — | — | ✓ `mcp-config.json` |
|
|
492
493
|
| vibe | `~/.vibe` | ✓ | — | — | — | — | — |
|
|
493
494
|
| devin | `~/.devin` | — | — | — | — | — | — |
|
|
494
|
-
| agy | `~/.
|
|
495
|
+
| agy | `~/.gemini/config` | ✓ | — | — | — | — | ✓ `mcp_config.json` |
|
|
495
496
|
|
|
496
497
|
Per-harness locations, mechanisms and the harness versions this was checked
|
|
497
498
|
against are in [docs/harnesses.md](docs/harnesses.md).
|
|
@@ -1009,7 +1010,7 @@ agent-toggle config sync-ci # set TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID on
|
|
|
1009
1010
|
|
|
1010
1011
|
In the [settings menu](#settings--config-menu) an inline field commits on `Enter`, cancels on
|
|
1011
1012
|
`Esc`, and clears on `-` then `Enter`; the Tools group also has Send test message and Sync CI
|
|
1012
|
-
secrets (the same as `config test` / `config sync-ci
|
|
1013
|
+
secrets (the same as `config test` / `config sync-ci`). The bot token
|
|
1013
1014
|
is kept only in the OS credential store (macOS Keychain, Linux Secret Service, Windows DPAPI)
|
|
1014
1015
|
through telegram-kit, never in a file or on a command line, and is shown masked. With no
|
|
1015
1016
|
credential store it refuses to store the token; set `TG_BOT_TOKEN` in the environment instead
|
|
@@ -134,7 +134,7 @@ def read_project_mcp(file: Path) -> tuple[str, dict]:
|
|
|
134
134
|
try:
|
|
135
135
|
if file.stat().st_size > MAX_PROJECT_MCP:
|
|
136
136
|
raise ProjectMcpError(f"{file} is larger than {MAX_PROJECT_MCP >> 20} MiB")
|
|
137
|
-
text =
|
|
137
|
+
text = fs.read_utf8(file) # bytes: CRLF must survive
|
|
138
138
|
except FileNotFoundError:
|
|
139
139
|
raise ProjectMcpError(f"{file} not found") from None
|
|
140
140
|
except (OSError, UnicodeDecodeError) as e:
|
|
@@ -696,7 +696,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
696
696
|
sub.add_parser("update", parents=[common],
|
|
697
697
|
help="check for a newer release; show the commands that update everything")
|
|
698
698
|
cf = sub.add_parser("config", parents=[common],
|
|
699
|
-
help="Telegram settings for the CI failure alerts
|
|
699
|
+
help="Telegram settings for the CI failure alerts")
|
|
700
700
|
cf.add_argument("action", nargs="?", choices=("test", "sync-ci"),
|
|
701
701
|
help="test: send one message | sync-ci: set the GitHub repo secrets")
|
|
702
702
|
cf.add_argument("--repo", metavar="OWNER/REPO", help="sync-ci: repository (default: this one)")
|
|
@@ -5,8 +5,8 @@ numbered list otherwise; `--json` prints every setting). Same shape as aicp's `-
|
|
|
5
5
|
the bot token lives only in the OS credential store (Keychain / Secret Service / DPAPI,
|
|
6
6
|
through telegram-kit, never in a file or an argument vector); the chat id and every other
|
|
7
7
|
setting are ordinary configuration in ~/.agent-toggle/config.json (`settings`).
|
|
8
|
-
telegram-kit is
|
|
9
|
-
|
|
8
|
+
telegram-kit is a dependency, imported only here: `test` and `sync-ci` need it, and the menu
|
|
9
|
+
still works if a broken install lacks it (its Telegram rows say so).
|
|
10
10
|
"""
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
@@ -20,7 +20,7 @@ SERVICE = "agent-toggle"
|
|
|
20
20
|
CHAT_KEY = "telegram_chat_id"
|
|
21
21
|
SECRETS = ("TELEGRAM_BOT_TOKEN", "TELEGRAM_CHAT_ID")
|
|
22
22
|
CHAT_ID_RE = settings.CHAT_ID_RE
|
|
23
|
-
INSTALL_HINT = "
|
|
23
|
+
INSTALL_HINT = "reinstall agent-toggle: uv tool install --reinstall agent-toggle"
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
config_file = settings.config_file
|
|
@@ -43,7 +43,7 @@ def kit():
|
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
def optional_kit():
|
|
46
|
-
"""telegram-kit, or None
|
|
46
|
+
"""telegram-kit, or None when it is missing (the menu degrades instead of exiting 4)."""
|
|
47
47
|
try:
|
|
48
48
|
return kit()
|
|
49
49
|
except CliError:
|
|
@@ -312,7 +312,7 @@ def inventory(state: dict, table: dict, warn: Callable[[str], None] = lambda m:
|
|
|
312
312
|
else (0, None, "flag entry; size unknown"))
|
|
313
313
|
elif type_ == "mcp":
|
|
314
314
|
counts = mcp_tool_counts(h)
|
|
315
|
-
for name in live_mcp(h.home, h.backend):
|
|
315
|
+
for name in live_mcp(h.home, h.backend, h.mcp and h.mcp.file):
|
|
316
316
|
add(hname, type_, name, True, mcp_estimate(hname, counts.get(name)))
|
|
317
317
|
|
|
318
318
|
listed: list[str] = [] # canonical `name@marketplace` ids, for the parked rows
|
|
@@ -94,7 +94,7 @@ def _move_back(p: Path, live: Path) -> None:
|
|
|
94
94
|
def _read_json(file: Path) -> tuple[object, str, str]:
|
|
95
95
|
"""(data, kind, why); kind is ok | missing | jsonc | broken."""
|
|
96
96
|
try:
|
|
97
|
-
text =
|
|
97
|
+
text = fs.read_utf8(file).removeprefix("\ufeff")
|
|
98
98
|
except FileNotFoundError:
|
|
99
99
|
return None, "missing", "is missing"
|
|
100
100
|
except (OSError, UnicodeDecodeError) as e:
|
|
@@ -254,6 +254,15 @@ def lock():
|
|
|
254
254
|
pass
|
|
255
255
|
|
|
256
256
|
|
|
257
|
+
def read_utf8(path: Path) -> str:
|
|
258
|
+
"""Exact text of `path`; a git-crypt-locked file raises OSError naming the fix,
|
|
259
|
+
not an opaque UnicodeDecodeError."""
|
|
260
|
+
raw = path.read_bytes()
|
|
261
|
+
if raw.startswith(b"\x00GITCRYPT\x00"):
|
|
262
|
+
raise OSError("git-crypt encrypted -- run `git-crypt unlock` in its repo")
|
|
263
|
+
return raw.decode("utf-8")
|
|
264
|
+
|
|
265
|
+
|
|
257
266
|
def atomic_write(path: Path, text: str, mode: int = 0o600, newline: str | None = None) -> None:
|
|
258
267
|
"""Write `text` to `path` via a same-dir tmp file created with `mode`.
|
|
259
268
|
`newline=""` writes line endings as given (a user file edited in place)."""
|
|
@@ -173,9 +173,14 @@ def build(home: Path) -> dict[str, Harness]:
|
|
|
173
173
|
# explicit not-applicable row: no dirs, no mechanisms, so every type
|
|
174
174
|
# exits 4 through the ops dispatch.
|
|
175
175
|
Harness("devin", home / ".devin", dirs={}, mechanisms={}),
|
|
176
|
-
# DESIGN s11 q1
|
|
177
|
-
#
|
|
178
|
-
Harness("agy", home / ".
|
|
176
|
+
# DESIGN s11 q1 (answered on 1.3.3): global root `~/.gemini/config/` holds
|
|
177
|
+
# `skills/<name>/SKILL.md` and strict-JSON `mcp_config.json -> mcpServers`.
|
|
178
|
+
Harness("agy", home / ".gemini" / "config",
|
|
179
|
+
dirs={"skill": ("skills",)},
|
|
180
|
+
mechanisms={"skill": "move", "mcp": "remove_backup"},
|
|
181
|
+
mcp=McpSpec("json", home / ".gemini" / "config" / "mcp_config.json",
|
|
182
|
+
("mcpServers",)),
|
|
183
|
+
editable=frozenset({"mcp_config.json"}), verified="1.3.3"),
|
|
179
184
|
)}
|
|
180
185
|
|
|
181
186
|
|
|
@@ -54,7 +54,7 @@ CATALOG: dict[str, str] = {
|
|
|
54
54
|
"config.language": "語言 / Language",
|
|
55
55
|
"config.logo": "標誌",
|
|
56
56
|
"config.more": "還有 %s 項",
|
|
57
|
-
"config.needs_extra": "
|
|
57
|
+
"config.needs_extra": "缺少 telegram-kit:請重新安裝 agent-toggle",
|
|
58
58
|
"config.no_hidden": "此終端機無法隱藏輸入;請改在環境變數設定 TG_BOT_TOKEN",
|
|
59
59
|
"config.no_output": "(沒有輸出)",
|
|
60
60
|
"config.no_store": "這裡沒有作業系統憑證儲存區,權杖未儲存;請改設定 TG_BOT_TOKEN",
|
|
@@ -240,8 +240,9 @@ def live_names(base: Path, type_: str) -> list[str]:
|
|
|
240
240
|
return sorted(set(names))
|
|
241
241
|
|
|
242
242
|
|
|
243
|
-
def live_mcp(home: Path, backend: str | None) -> list[str]:
|
|
244
|
-
"""Names of the MCP servers currently configured for one harness
|
|
243
|
+
def live_mcp(home: Path, backend: str | None, file: Path | None = None) -> list[str]:
|
|
244
|
+
"""Names of the MCP servers currently configured for one harness (`file`: the
|
|
245
|
+
table's McpSpec.file, which the `json` backend reads)."""
|
|
245
246
|
if backend == "claude-json":
|
|
246
247
|
try:
|
|
247
248
|
cfg = json.loads(fs.claude_json().read_text(encoding="utf-8"))
|
|
@@ -260,9 +261,9 @@ def live_mcp(home: Path, backend: str | None) -> list[str]:
|
|
|
260
261
|
except ProjectMcpError:
|
|
261
262
|
return []
|
|
262
263
|
return sorted(n for n in names if n.isprintable() and _valid_name(n))
|
|
263
|
-
if backend == "json":
|
|
264
|
+
if backend == "json" and file: # user-edited text, so filter names
|
|
264
265
|
try:
|
|
265
|
-
names = read_project_mcp(
|
|
266
|
+
names = read_project_mcp(file)[1]["mcpServers"]
|
|
266
267
|
except ProjectMcpError:
|
|
267
268
|
return []
|
|
268
269
|
return sorted(n for n in names if n.isprintable() and _valid_name(n))
|
|
@@ -38,7 +38,7 @@ class Row:
|
|
|
38
38
|
key: str = "" # the settings key (bool / choice / text)
|
|
39
39
|
run: Callable[[Ctx, Result], None] | None = None # action rows
|
|
40
40
|
confirm: Callable[[], str] | None = None # y/n question asked before the action runs
|
|
41
|
-
telegram: bool = False # inert
|
|
41
|
+
telegram: bool = False # inert when telegram-kit is missing
|
|
42
42
|
harness: str = "" # harness rows: the name, for the detected note
|
|
43
43
|
role: str = "title" # headings: each group in its own colour
|
|
44
44
|
|
|
@@ -265,7 +265,7 @@ def capture(fn: Callable[[Result], None]) -> list[Segment]:
|
|
|
265
265
|
|
|
266
266
|
|
|
267
267
|
def needs_extra() -> str:
|
|
268
|
-
return t("config.needs_extra", "
|
|
268
|
+
return t("config.needs_extra", "telegram-kit missing: reinstall agent-toggle")
|
|
269
269
|
|
|
270
270
|
|
|
271
271
|
def value_segments(ctx: Ctx, row: Row, g: theme.Glyphs) -> list[Segment]:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-toggle
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.12.1
|
|
4
4
|
Summary: Temporarily disable / re-enable AI-agent skills, agents, commands, rules, plugins and MCP servers across harnesses (Claude Code, Codex, Grok, OpenCode, OpenClaw, Copilot, Vibe, Devin, Antigravity)
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/weskao/agent-toggle
|
|
@@ -24,6 +24,7 @@ Classifier: Topic :: Utilities
|
|
|
24
24
|
Requires-Python: >=3.10
|
|
25
25
|
Description-Content-Type: text/markdown
|
|
26
26
|
License-File: LICENSE
|
|
27
|
+
Requires-Dist: telegram-kit<0.3,>=0.2.2
|
|
27
28
|
Provides-Extra: windows
|
|
28
29
|
Requires-Dist: windows-curses; sys_platform == "win32" and extra == "windows"
|
|
29
30
|
Provides-Extra: telegram
|
|
@@ -142,21 +143,20 @@ exits `1`. A shim written by a version before the marker counts as foreign;
|
|
|
142
143
|
delete it and re-run. `--dry-run` shows the same plan, refusals included. The
|
|
143
144
|
tool itself stays in one place.
|
|
144
145
|
|
|
145
|
-
**Optional: Telegram alerts for CI** (for maintainers of a fork or clone).
|
|
146
|
-
|
|
147
|
-
(itself stdlib-only),
|
|
146
|
+
**Optional: Telegram alerts for CI** (for maintainers of a fork or clone). The
|
|
147
|
+
Telegram support comes from [telegram-kit](https://pypi.org/project/telegram-kit/)
|
|
148
|
+
(itself stdlib-only), which every install includes, so a reinstall or upgrade keeps
|
|
149
|
+
your settings. Run the setup once; see [CI notifications](#ci-notifications):
|
|
148
150
|
|
|
149
151
|
```sh
|
|
150
|
-
uv tool install "agent-toggle[telegram]" # or: pipx install "agent-toggle[telegram]"
|
|
151
|
-
pip install -e ".[telegram]" # from a checkout
|
|
152
152
|
agent-toggle config # settings menu: bot token + chat id live under Notifications
|
|
153
153
|
agent-toggle config sync-ci # set the two GitHub repository secrets
|
|
154
154
|
```
|
|
155
155
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
156
|
+
The old `agent-toggle[telegram]` extra still resolves and is now a no-op. If
|
|
157
|
+
telegram-kit is missing from a broken install, `config test` and `config sync-ci`
|
|
158
|
+
exit `4`, bare `config` still opens, and the Telegram rows read
|
|
159
|
+
`telegram-kit missing: reinstall agent-toggle`.
|
|
160
160
|
|
|
161
161
|
**Optional: the Claude Code pane mod** (the picker as a pane inside Claude Code, claude harness
|
|
162
162
|
only). It is not part of the PyPI package; with `agent-toggle` already on `PATH` from one of the
|
|
@@ -204,7 +204,7 @@ agent-toggle <command> [args] # or: python3 agent_toggle.py <command> [
|
|
|
204
204
|
| `undo` | reverse the last logged batch (`--dry-run` shows the plan) |
|
|
205
205
|
| `profile save\|apply\|diff\|list\|delete\|rename` | named sets of live items; see [Profiles](#profiles) |
|
|
206
206
|
| `config` | settings menu (curses, else a numbered list); `config --json` lists every setting and the package version; see [Settings & config menu](#settings--config-menu) |
|
|
207
|
-
| `config test\|sync-ci` | send one Telegram test message / set the GitHub CI secrets;
|
|
207
|
+
| `config test\|sync-ci` | send one Telegram test message / set the GitHub CI secrets; see [CI notifications](#ci-notifications) |
|
|
208
208
|
| `help [command]` | styled help; `help ui` = `--help ui` = `ui --help`; see [Help](#help) |
|
|
209
209
|
| `doctor` | check each harness layout and `state.json` against disk; exit `1` only on an `error` row; on a terminal, asks y/n per fix it can run itself |
|
|
210
210
|
| `migrate` | import an older `~/.claude-toggle/` state, and move legacy `<home>/<dir>-disabled/` park dirs into `~/.agent-toggle/parked/user/` (see [Upgrading the park layout](#upgrading-the-park-layout)); safe to re-run |
|
|
@@ -391,6 +391,8 @@ a band above the prompt, buttons) and change Claude Code's behaviour. A plugin w
|
|
|
391
391
|
`hooks.json`, or with only ordinary hooks (shell commands such as `PreToolUse`), is a plain
|
|
392
392
|
plugin: it ships skills, commands, agents, MCP servers or hooks and draws nothing.
|
|
393
393
|
|
|
394
|
+

|
|
395
|
+
|
|
394
396
|
- The check reads the install path `claude plugin list` reports and every cached version
|
|
395
397
|
under `~/.claude/plugins/cache/<marketplace>/<name>/`, so a parked mod is still a mod.
|
|
396
398
|
To list the installed mods by hand:
|
|
@@ -463,8 +465,8 @@ invalid value in either is ignored with one warning on stderr.
|
|
|
463
465
|
harness is switched off (with one warning on stderr);
|
|
464
466
|
`disable` / `enable --project` without `--harness` still default to `claude`.
|
|
465
467
|
- `language` translates the config menu, help, update prompt and picker chrome; command output stays English.
|
|
466
|
-
-
|
|
467
|
-
|
|
468
|
+
- telegram-kit is a regular dependency now, so reinstalling keeps the Telegram settings working;
|
|
469
|
+
`agent-toggle[telegram]` still resolves.
|
|
468
470
|
|
|
469
471
|
## Update check
|
|
470
472
|
|
|
@@ -523,7 +525,7 @@ logo, centred, when the window is tall enough to fit the whole menu beside it; t
|
|
|
523
525
|
| copilot | `~/.copilot` | ✓ | ✓ | — | — | — | ✓ `mcp-config.json` |
|
|
524
526
|
| vibe | `~/.vibe` | ✓ | — | — | — | — | — |
|
|
525
527
|
| devin | `~/.devin` | — | — | — | — | — | — |
|
|
526
|
-
| agy | `~/.
|
|
528
|
+
| agy | `~/.gemini/config` | ✓ | — | — | — | — | ✓ `mcp_config.json` |
|
|
527
529
|
|
|
528
530
|
Per-harness locations, mechanisms and the harness versions this was checked
|
|
529
531
|
against are in [docs/harnesses.md](docs/harnesses.md).
|
|
@@ -1041,7 +1043,7 @@ agent-toggle config sync-ci # set TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID on
|
|
|
1041
1043
|
|
|
1042
1044
|
In the [settings menu](#settings--config-menu) an inline field commits on `Enter`, cancels on
|
|
1043
1045
|
`Esc`, and clears on `-` then `Enter`; the Tools group also has Send test message and Sync CI
|
|
1044
|
-
secrets (the same as `config test` / `config sync-ci
|
|
1046
|
+
secrets (the same as `config test` / `config sync-ci`). The bot token
|
|
1045
1047
|
is kept only in the OS credential store (macOS Keychain, Linux Secret Service, Windows DPAPI)
|
|
1046
1048
|
through telegram-kit, never in a file or on a command line, and is shown masked. With no
|
|
1047
1049
|
credential store it refuses to store the token; set `TG_BOT_TOKEN` in the environment instead
|
|
@@ -26,7 +26,8 @@ classifiers = [
|
|
|
26
26
|
"Topic :: Utilities",
|
|
27
27
|
]
|
|
28
28
|
requires-python = ">=3.10"
|
|
29
|
-
|
|
29
|
+
# telegram-kit is core (stdlib-only) so a plain reinstall can't drop the Telegram settings
|
|
30
|
+
dependencies = ["telegram-kit>=0.2.2,<0.3"]
|
|
30
31
|
|
|
31
32
|
[project.urls]
|
|
32
33
|
Homepage = "https://github.com/weskao/agent-toggle"
|
|
@@ -36,6 +37,7 @@ Changelog = "https://github.com/weskao/agent-toggle/blob/main/CHANGELOG.md"
|
|
|
36
37
|
|
|
37
38
|
[project.optional-dependencies]
|
|
38
39
|
windows = ["windows-curses; sys_platform == 'win32'"]
|
|
40
|
+
# kept so existing `agent-toggle[telegram]` install commands resolve without a warning
|
|
39
41
|
telegram = ["telegram-kit>=0.2.2,<0.3"]
|
|
40
42
|
|
|
41
43
|
[project.scripts]
|
|
@@ -162,6 +162,12 @@ class ProjectMcpJsonTest(SandboxCase):
|
|
|
162
162
|
with self.assertRaises(mcp_json.ProjectMcpError):
|
|
163
163
|
mcp_json.project_mcp_remove(self.put('{"mcpServers": {}}'), "a-mcp")
|
|
164
164
|
|
|
165
|
+
def test_git_crypt_locked_file_names_the_fix(self) -> None:
|
|
166
|
+
f = self.tmp / ".mcp.json"
|
|
167
|
+
f.write_bytes(b"\x00GITCRYPT\x00\xa4\xc7}")
|
|
168
|
+
with self.assertRaisesRegex(mcp_json.ProjectMcpError, "git-crypt unlock"):
|
|
169
|
+
mcp_json.read_project_mcp(f)
|
|
170
|
+
|
|
165
171
|
|
|
166
172
|
class CopilotMcpTest(CliCase):
|
|
167
173
|
"""copilot's user-scope strict-JSON mcp-config.json (backend `json`)."""
|
|
@@ -163,13 +163,13 @@ class ConfigTest(CliCase):
|
|
|
163
163
|
self.assertEqual(self.run_cli("config", "sync-ci", "--dry-run", "--repo", "o/r")[0], 0)
|
|
164
164
|
self.assertEqual(self.gh_calls, [])
|
|
165
165
|
|
|
166
|
-
def
|
|
166
|
+
def test_missing_kit_is_a_clear_exit_4_for_test_and_sync_ci_only(self) -> None:
|
|
167
167
|
mock.patch.stopall()
|
|
168
168
|
with mock.patch.dict(sys.modules, {"telegram_kit": None}):
|
|
169
169
|
for action in ("test", "sync-ci"):
|
|
170
170
|
rc, _, err = self.run_cli("config", action)
|
|
171
171
|
self.assertEqual(rc, 4)
|
|
172
|
-
self.assertIn("agent-toggle
|
|
172
|
+
self.assertIn("reinstall agent-toggle", err)
|
|
173
173
|
with mock.patch.object(sys, "stdin", io.StringIO("")): # never the real TTY
|
|
174
174
|
self.assertEqual(self.run_cli("config")[0], 0) # the menu degrades instead
|
|
175
175
|
|
|
@@ -260,7 +260,7 @@ class CursesMenuTest(MenuCase):
|
|
|
260
260
|
def test_without_telegram_kit_the_rows_are_inert(self) -> None:
|
|
261
261
|
win, rc = self.menu([*to("Telegram chat"), "\n", "r", "q"], telegram=False)
|
|
262
262
|
self.assertEqual(rc, 0)
|
|
263
|
-
self.assertIn("
|
|
263
|
+
self.assertIn("reinstall agent-toggle", win.text())
|
|
264
264
|
self.assertFalse(settings.config_file().exists())
|
|
265
265
|
|
|
266
266
|
def test_tools_run_with_a_fresh_result_in_a_pager(self) -> None:
|
|
@@ -394,7 +394,7 @@ class NumberedTest(MenuCase):
|
|
|
394
394
|
def test_without_telegram_kit_rows_are_degraded(self) -> None:
|
|
395
395
|
rc, out = self.run_numbered(f"{number('Telegram chat')}\nq\n", telegram=False)
|
|
396
396
|
self.assertEqual(rc, 0)
|
|
397
|
-
self.assertIn("
|
|
397
|
+
self.assertIn("reinstall agent-toggle", out)
|
|
398
398
|
self.assertFalse(settings.config_file().exists())
|
|
399
399
|
|
|
400
400
|
def test_actions_confirm_and_print_their_output(self) -> None:
|
|
@@ -431,7 +431,7 @@ class CliConfigMenuTest(CliCase):
|
|
|
431
431
|
mock.patch.object(sys, "stdin", io.StringIO("1\n")):
|
|
432
432
|
rc, out, err = self.run_cli("config")
|
|
433
433
|
self.assertEqual(rc, 0, err)
|
|
434
|
-
self.assertIn("
|
|
434
|
+
self.assertIn("reinstall agent-toggle", out)
|
|
435
435
|
self.assertIs(settings.get("update_check"), False)
|
|
436
436
|
with mock.patch.dict(sys.modules, {"telegram_kit": None}):
|
|
437
437
|
rc, env = self.run_json("config")
|
|
@@ -189,6 +189,16 @@ class CostCommandTest(CliCase):
|
|
|
189
189
|
self.assertTrue(any("plugin list" in w for w in env["warnings"]))
|
|
190
190
|
|
|
191
191
|
|
|
192
|
+
class AgyCostTest(CliCase):
|
|
193
|
+
def test_live_skill_and_mcp_servers_are_listed(self) -> None:
|
|
194
|
+
shutil.copytree(FIXTURES / "agy", self.tmp, dirs_exist_ok=True)
|
|
195
|
+
rc, env = self.run_json("cost", "--harness", "agy")
|
|
196
|
+
self.assertEqual(rc, 0, env)
|
|
197
|
+
live = {(r["type"], r["name"]) for r in env["results"] if r["name"] and r["enabled"]}
|
|
198
|
+
self.assertEqual(live, {("skill", "demo-skill"), ("mcp", "example-mcp"),
|
|
199
|
+
("mcp", "other-mcp")})
|
|
200
|
+
|
|
201
|
+
|
|
192
202
|
class FlagCostTest(CliCase):
|
|
193
203
|
def setUp(self) -> None:
|
|
194
204
|
super().setUp()
|
|
@@ -17,7 +17,8 @@ TODAY = {
|
|
|
17
17
|
} | {("opencode", "skill"), ("opencode", "command"),
|
|
18
18
|
("grok", "skill"), ("grok", "mcp"), ("openclaw", "skill"), ("openclaw", "agent"),
|
|
19
19
|
("openclaw", "plugin"), ("opencode", "mcp"), ("vibe", "skill"),
|
|
20
|
-
("copilot", "skill"), ("copilot", "agent"), ("copilot", "mcp")
|
|
20
|
+
("copilot", "skill"), ("copilot", "agent"), ("copilot", "mcp"),
|
|
21
|
+
("agy", "skill"), ("agy", "mcp")}
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
class HarnessTableTest(SandboxCase):
|
|
@@ -62,6 +63,10 @@ class HarnessTableTest(SandboxCase):
|
|
|
62
63
|
self.assertEqual(t["copilot"].backend, "json")
|
|
63
64
|
self.assertEqual(t["copilot"].mcp.file, self.tmp / ".copilot" / "mcp-config.json")
|
|
64
65
|
self.assertEqual(t["copilot"].editable, {"mcp-config.json"})
|
|
66
|
+
self.assertEqual(t["agy"].home, self.tmp / ".gemini" / "config")
|
|
67
|
+
self.assertEqual(t["agy"].backend, "json")
|
|
68
|
+
self.assertEqual(t["agy"].mcp.file, self.tmp / ".gemini" / "config" / "mcp_config.json")
|
|
69
|
+
self.assertEqual(t["agy"].editable, {"mcp_config.json"})
|
|
65
70
|
|
|
66
71
|
def test_flag_pairs_are_declared_and_editable(self) -> None:
|
|
67
72
|
for n, h in build(self.tmp).items():
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|