agent-toggle 0.16.2__tar.gz → 0.17.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.
Files changed (92) hide show
  1. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/PKG-INFO +15 -9
  2. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/README.md +14 -8
  3. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/__init__.py +1 -1
  4. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/backends/flag_json.py +1 -1
  5. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/backends/plugin_cli.py +1 -1
  6. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/cli.py +1 -1
  7. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/cost.py +1 -1
  8. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/doctor.py +2 -1
  9. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/harnesses.py +1 -1
  10. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/ui/config_menu.py +1 -1
  11. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/ui/picker.py +1 -1
  12. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/update_prompt.py +7 -4
  13. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle.egg-info/PKG-INFO +15 -9
  14. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle.egg-info/SOURCES.txt +1 -0
  15. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/pyproject.toml +5 -1
  16. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_backends.py +1 -1
  17. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_cli.py +1 -1
  18. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_cost.py +2 -1
  19. agent_toggle-0.17.0/tests/test_mod_version.py +20 -0
  20. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_picker.py +2 -2
  21. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_update_prompt.py +12 -0
  22. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/LICENSE +0 -0
  23. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/__main__.py +0 -0
  24. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/backends/__init__.py +0 -0
  25. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/backends/mcp_json.py +0 -0
  26. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/backends/mcp_toml.py +0 -0
  27. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/companions.py +0 -0
  28. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/completions.py +0 -0
  29. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/config.py +0 -0
  30. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/fs.py +0 -0
  31. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/help.py +0 -0
  32. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/i18n.py +0 -0
  33. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/locales/__init__.py +0 -0
  34. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/locales/zh_tw_cli.py +0 -0
  35. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/locales/zh_tw_common.py +0 -0
  36. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/locales/zh_tw_config.py +0 -0
  37. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/locales/zh_tw_picker.py +0 -0
  38. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/mechanisms.py +0 -0
  39. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/ops.py +0 -0
  40. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/output.py +0 -0
  41. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/profiles.py +0 -0
  42. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/settings.py +0 -0
  43. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/shims/claude.md.tmpl +0 -0
  44. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/shims/generic.md.tmpl +0 -0
  45. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/spinner.py +0 -0
  46. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/store.py +0 -0
  47. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/toml_check.py +0 -0
  48. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/ui/__init__.py +0 -0
  49. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/ui/logo.py +0 -0
  50. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/ui/menu.py +0 -0
  51. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/ui/model.py +0 -0
  52. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/ui/theme.py +0 -0
  53. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/undo.py +0 -0
  54. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle/update_check.py +0 -0
  55. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle.egg-info/dependency_links.txt +0 -0
  56. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle.egg-info/entry_points.txt +0 -0
  57. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle.egg-info/requires.txt +0 -0
  58. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/agent_toggle.egg-info/top_level.txt +0 -0
  59. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/setup.cfg +0 -0
  60. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_alias.py +0 -0
  61. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_all_type.py +0 -0
  62. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_cli_surface.py +0 -0
  63. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_color.py +0 -0
  64. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_companions.py +0 -0
  65. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_completions.py +0 -0
  66. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_config.py +0 -0
  67. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_config_menu.py +0 -0
  68. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_conformance.py +0 -0
  69. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_containment.py +0 -0
  70. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_crash.py +0 -0
  71. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_doctor.py +0 -0
  72. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_encoding.py +0 -0
  73. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_fs.py +0 -0
  74. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_harnesses.py +0 -0
  75. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_help.py +0 -0
  76. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_i18n.py +0 -0
  77. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_install.py +0 -0
  78. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_logo.py +0 -0
  79. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_mechanisms.py +0 -0
  80. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_menu.py +0 -0
  81. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_mods.py +0 -0
  82. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_platform.py +0 -0
  83. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_profiles.py +0 -0
  84. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_project.py +0 -0
  85. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_safety.py +0 -0
  86. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_same_name.py +0 -0
  87. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_secret_audit.py +0 -0
  88. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_settings.py +0 -0
  89. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_store.py +0 -0
  90. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_theme.py +0 -0
  91. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_undo.py +0 -0
  92. {agent_toggle-0.16.2 → agent_toggle-0.17.0}/tests/test_update_check.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-toggle
3
- Version: 0.16.2
3
+ Version: 0.17.0
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
@@ -168,6 +168,9 @@ installs above, type this at the prompt of a Claude Code terminal session (2.1.2
168
168
  ```
169
169
 
170
170
  Answer `y` to add the marketplace, pick a scope (user is the default), then run `/agent-toggle`.
171
+ The same mod is also listed in the `weskao-agent-plugins` marketplace
172
+ (`claude plugin marketplace add weskao/weskao-agent-plugins`, then
173
+ `claude plugin install agent-toggle@weskao-agent-plugins`); install it from one of the two, not both.
171
174
 
172
175
  See [Claude Code pane mod](#claude-code-pane-mod).
173
176
 
@@ -260,7 +263,7 @@ Flags accepted by every command, before or after the subcommand:
260
263
 
261
264
  Extra row fields: `list` rows carry `at`, `mechanism`, `companions`; `cost` rows
262
265
  carry `enabled`, `tokens`, `would_save`, `chars`, and the final `total` row
263
- carries `total_tokens`, `saved_tokens`, `formula`. Rows for a
266
+ carries `total_tokens`, `saved_tokens`, `formula`, `version`. Rows for a
264
267
  directory shared with another harness (see below) carry `shared_with`, the other
265
268
  harnesses that read it: `disable` / `enable` rows relative to the harness you
266
269
  asked, plus `owner`, the harness whose park dir and state entry hold the item;
@@ -527,9 +530,9 @@ tag (`git ls-remote --tags`) and always installs that tag with
527
530
  `uv tool install --force --no-sources git+<repo>@<tag>` (`pipx install --force` for a pipx install).
528
531
  A dev checkout, a branch or commit install, or an unmanaged install is replaced by the tag too
529
532
  (with uv, else pipx); only an install made from exactly that tag is already up to date.
530
- It then runs, with the new binary and in order: when the Claude Code plugin is installed,
531
- `claude plugin marketplace update agent-toggle` and `claude plugin update agent-toggle@agent-toggle`,
532
- then `agent-toggle install-shims`. A failing step prints a warning and makes the exit code `1`.
533
+ It then runs, with the new binary and in order: when the Claude Code plugin is installed (from any
534
+ marketplace), `claude plugin marketplace update <marketplace>` and
535
+ `claude plugin update agent-toggle@<marketplace>`, then `agent-toggle install-shims`. A failing step prints a warning and makes the exit code `1`.
533
536
  It never prompts and writes to stderr only.
534
537
 
535
538
  | Exit | Meaning |
@@ -1011,8 +1014,9 @@ CLI (`cost --json`, `disable|enable --json`) and works for the claude harness on
1011
1014
  `agent-toggle ui` stays the cross-harness UI.
1012
1015
 
1013
1016
  Install it with `/plugin install agent-toggle --marketplace weskao/agent-toggle` (see
1014
- [Install](#install)); `claude plugin update agent-toggle@agent-toggle` picks up a new release. To
1015
- run your own checkout instead (edits reload as you save):
1017
+ [Install](#install)); `/agent-toggle update` picks up a new release: it runs `agent-toggle update`,
1018
+ which updates the CLI, this mod and the shims together (restart Claude Code to load the new mod).
1019
+ The mod and the CLI share one version. To run your own checkout instead (edits reload as you save):
1016
1020
 
1017
1021
  ```sh
1018
1022
  claude --plugin-dir ./mod # then run /agent-toggle inside Claude
@@ -1144,8 +1148,10 @@ prints a `::notice::` and exits 0, so it never adds a second red X.
1144
1148
  ## Releasing
1145
1149
 
1146
1150
  A release is a git tag `vX.Y.Z` (where `X.Y.Z` must match `agent_toggle.__version__`).
1147
- Bump `version` in `mod/.claude-plugin/plugin.json` to the same value by hand; nothing checks it,
1148
- and without it `claude plugin update` does not hand marketplace installs the new mod.
1151
+ `version` in `mod/.claude-plugin/plugin.json` must be the same value (the mod ships in lockstep;
1152
+ without the bump `claude plugin update` does not hand marketplace installs the new mod).
1153
+ `tests/test_mod_version.py` and the release workflow both fail on a mismatch.
1154
+ The `weskao-agent-plugins` marketplace lists the mod without a version, so it needs no bump there.
1149
1155
  Pushing the tag triggers `.github/workflows/release.yml`:
1150
1156
 
1151
1157
  1. **Build** job checks that the tag matches `__version__`, builds a wheel and
@@ -134,6 +134,9 @@ installs above, type this at the prompt of a Claude Code terminal session (2.1.2
134
134
  ```
135
135
 
136
136
  Answer `y` to add the marketplace, pick a scope (user is the default), then run `/agent-toggle`.
137
+ The same mod is also listed in the `weskao-agent-plugins` marketplace
138
+ (`claude plugin marketplace add weskao/weskao-agent-plugins`, then
139
+ `claude plugin install agent-toggle@weskao-agent-plugins`); install it from one of the two, not both.
137
140
 
138
141
  See [Claude Code pane mod](#claude-code-pane-mod).
139
142
 
@@ -226,7 +229,7 @@ Flags accepted by every command, before or after the subcommand:
226
229
 
227
230
  Extra row fields: `list` rows carry `at`, `mechanism`, `companions`; `cost` rows
228
231
  carry `enabled`, `tokens`, `would_save`, `chars`, and the final `total` row
229
- carries `total_tokens`, `saved_tokens`, `formula`. Rows for a
232
+ carries `total_tokens`, `saved_tokens`, `formula`, `version`. Rows for a
230
233
  directory shared with another harness (see below) carry `shared_with`, the other
231
234
  harnesses that read it: `disable` / `enable` rows relative to the harness you
232
235
  asked, plus `owner`, the harness whose park dir and state entry hold the item;
@@ -493,9 +496,9 @@ tag (`git ls-remote --tags`) and always installs that tag with
493
496
  `uv tool install --force --no-sources git+<repo>@<tag>` (`pipx install --force` for a pipx install).
494
497
  A dev checkout, a branch or commit install, or an unmanaged install is replaced by the tag too
495
498
  (with uv, else pipx); only an install made from exactly that tag is already up to date.
496
- It then runs, with the new binary and in order: when the Claude Code plugin is installed,
497
- `claude plugin marketplace update agent-toggle` and `claude plugin update agent-toggle@agent-toggle`,
498
- then `agent-toggle install-shims`. A failing step prints a warning and makes the exit code `1`.
499
+ It then runs, with the new binary and in order: when the Claude Code plugin is installed (from any
500
+ marketplace), `claude plugin marketplace update <marketplace>` and
501
+ `claude plugin update agent-toggle@<marketplace>`, then `agent-toggle install-shims`. A failing step prints a warning and makes the exit code `1`.
499
502
  It never prompts and writes to stderr only.
500
503
 
501
504
  | Exit | Meaning |
@@ -977,8 +980,9 @@ CLI (`cost --json`, `disable|enable --json`) and works for the claude harness on
977
980
  `agent-toggle ui` stays the cross-harness UI.
978
981
 
979
982
  Install it with `/plugin install agent-toggle --marketplace weskao/agent-toggle` (see
980
- [Install](#install)); `claude plugin update agent-toggle@agent-toggle` picks up a new release. To
981
- run your own checkout instead (edits reload as you save):
983
+ [Install](#install)); `/agent-toggle update` picks up a new release: it runs `agent-toggle update`,
984
+ which updates the CLI, this mod and the shims together (restart Claude Code to load the new mod).
985
+ The mod and the CLI share one version. To run your own checkout instead (edits reload as you save):
982
986
 
983
987
  ```sh
984
988
  claude --plugin-dir ./mod # then run /agent-toggle inside Claude
@@ -1110,8 +1114,10 @@ prints a `::notice::` and exits 0, so it never adds a second red X.
1110
1114
  ## Releasing
1111
1115
 
1112
1116
  A release is a git tag `vX.Y.Z` (where `X.Y.Z` must match `agent_toggle.__version__`).
1113
- Bump `version` in `mod/.claude-plugin/plugin.json` to the same value by hand; nothing checks it,
1114
- and without it `claude plugin update` does not hand marketplace installs the new mod.
1117
+ `version` in `mod/.claude-plugin/plugin.json` must be the same value (the mod ships in lockstep;
1118
+ without the bump `claude plugin update` does not hand marketplace installs the new mod).
1119
+ `tests/test_mod_version.py` and the release workflow both fail on a mismatch.
1120
+ The `weskao-agent-plugins` marketplace lists the mod without a version, so it needs no bump there.
1115
1121
  Pushing the tag triggers `.github/workflows/release.yml`:
1116
1122
 
1117
1123
  1. **Build** job checks that the tag matches `__version__`, builds a wheel and
@@ -1,3 +1,3 @@
1
1
  """agent-toggle: temporarily disable / re-enable AI-agent resources."""
2
2
 
3
- __version__ = "0.16.2"
3
+ __version__ = "0.17.0"
@@ -25,9 +25,9 @@ from __future__ import annotations
25
25
 
26
26
  import json
27
27
  import re
28
+ from collections.abc import Sequence
28
29
  from json.decoder import WHITESPACE, scanstring
29
30
  from pathlib import Path
30
- from typing import Sequence
31
31
 
32
32
  from agent_toggle import fs
33
33
 
@@ -10,8 +10,8 @@ import os
10
10
  import shutil
11
11
  import subprocess
12
12
  import sys
13
+ from collections.abc import Callable
13
14
  from pathlib import Path
14
- from typing import Callable
15
15
 
16
16
  from .. import fs
17
17
 
@@ -298,7 +298,7 @@ def cmd_cost(state: dict, out: Result, harness: str | None = None,
298
298
  if len(shown) > len(items):
299
299
  _say_hidden(out, f"{len(shown) - len(items)} hidden by settings (agent-toggle config)")
300
300
  out.row(harness, type_, None, "cost", "ok", "total", show=False, items=len(items),
301
- total_tokens=live, saved_tokens=saved, formula=cost.FORMULA,
301
+ total_tokens=live, saved_tokens=saved, formula=cost.FORMULA, version=__version__,
302
302
  **({"project": str(proj)} if proj else {}))
303
303
 
304
304
 
@@ -26,10 +26,10 @@ from __future__ import annotations
26
26
  import json
27
27
  import re
28
28
  from collections import Counter
29
+ from collections.abc import Callable
29
30
  from concurrent.futures import ThreadPoolExecutor
30
31
  from dataclasses import dataclass
31
32
  from pathlib import Path
32
- from typing import Callable
33
33
 
34
34
  from . import fs
35
35
  from .backends.flag_json import jsonc_loads
@@ -15,8 +15,9 @@ import filecmp
15
15
  import json
16
16
  import os
17
17
  import shutil
18
+ from collections.abc import Callable
18
19
  from pathlib import Path
19
- from typing import Callable, NamedTuple
20
+ from typing import NamedTuple
20
21
 
21
22
  from . import fs, ops, store
22
23
  from .backends.flag_json import jsonc_loads
@@ -2,10 +2,10 @@
2
2
  from __future__ import annotations
3
3
 
4
4
  import os
5
+ from collections.abc import Mapping
5
6
  from dataclasses import dataclass, field
6
7
  from pathlib import Path
7
8
  from types import MappingProxyType
8
- from typing import Mapping
9
9
 
10
10
  from . import fs
11
11
  from .backends import flag_json
@@ -17,8 +17,8 @@ import contextlib
17
17
  import io
18
18
  import sys
19
19
  import time
20
+ from collections.abc import Callable
20
21
  from dataclasses import dataclass
21
- from typing import Callable
22
22
 
23
23
  from .. import __version__, config, doctor, fs, harnesses, i18n, settings, undo
24
24
  from ..i18n import t
@@ -498,7 +498,7 @@ def ask_save(win, v: View, pal: theme.Palette, g: theme.Glyphs,
498
498
  top = min(top, max(0, len(changed) - max(1, page)))
499
499
  row_y = y0 + (2 if bh >= 7 else 1)
500
500
 
501
- def put(y, segments):
501
+ def put(y, segments, x0=x0, inner=inner):
502
502
  theme.draw_segments(win, y, x0, [(g.v, "accent"),
503
503
  *clip(segments, inner, g.ellipsis), (g.v, "accent")], pal)
504
504
 
@@ -29,7 +29,6 @@ from .ui import theme
29
29
 
30
30
  DIST = "agent-toggle"
31
31
  REPO = update_check.REPO
32
- PLUGIN = "agent-toggle@agent-toggle"
33
32
  UPDATE = [DIST, "update"]
34
33
  RELEASE_NOTES = "https://github.com/weskao/agent-toggle/releases/tag/v%s"
35
34
  ANSWERS = (update_check.UPDATE_NOW, update_check.SKIP, update_check.SKIP_VERSION)
@@ -48,9 +47,13 @@ def after_steps() -> list[list[str]]:
48
47
  installed, then the shims."""
49
48
  steps: list[list[str]] = []
50
49
  # bare "claude": what a person pastes; _run() resolves it, as claude is often off PATH
51
- if plugin_cli.claude_bin() and any(p["id"] == PLUGIN for p in plugin_cli.list_plugins(lambda _msg: None)):
52
- steps += [["claude", "plugin", "marketplace", "update", DIST],
53
- ["claude", "plugin", "update", PLUGIN]]
50
+ if plugin_cli.claude_bin():
51
+ # any marketplace the mod came from; one id lists once per scope
52
+ installed = sorted({p["id"] for p in plugin_cli.list_plugins(lambda _msg: None)
53
+ if p["id"].partition("@")[0] == DIST})
54
+ for pid in installed:
55
+ steps += [["claude", "plugin", "marketplace", "update", pid.partition("@")[2]],
56
+ ["claude", "plugin", "update", pid]]
54
57
  return [*steps, [DIST, "install-shims"]]
55
58
 
56
59
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-toggle
3
- Version: 0.16.2
3
+ Version: 0.17.0
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
@@ -168,6 +168,9 @@ installs above, type this at the prompt of a Claude Code terminal session (2.1.2
168
168
  ```
169
169
 
170
170
  Answer `y` to add the marketplace, pick a scope (user is the default), then run `/agent-toggle`.
171
+ The same mod is also listed in the `weskao-agent-plugins` marketplace
172
+ (`claude plugin marketplace add weskao/weskao-agent-plugins`, then
173
+ `claude plugin install agent-toggle@weskao-agent-plugins`); install it from one of the two, not both.
171
174
 
172
175
  See [Claude Code pane mod](#claude-code-pane-mod).
173
176
 
@@ -260,7 +263,7 @@ Flags accepted by every command, before or after the subcommand:
260
263
 
261
264
  Extra row fields: `list` rows carry `at`, `mechanism`, `companions`; `cost` rows
262
265
  carry `enabled`, `tokens`, `would_save`, `chars`, and the final `total` row
263
- carries `total_tokens`, `saved_tokens`, `formula`. Rows for a
266
+ carries `total_tokens`, `saved_tokens`, `formula`, `version`. Rows for a
264
267
  directory shared with another harness (see below) carry `shared_with`, the other
265
268
  harnesses that read it: `disable` / `enable` rows relative to the harness you
266
269
  asked, plus `owner`, the harness whose park dir and state entry hold the item;
@@ -527,9 +530,9 @@ tag (`git ls-remote --tags`) and always installs that tag with
527
530
  `uv tool install --force --no-sources git+<repo>@<tag>` (`pipx install --force` for a pipx install).
528
531
  A dev checkout, a branch or commit install, or an unmanaged install is replaced by the tag too
529
532
  (with uv, else pipx); only an install made from exactly that tag is already up to date.
530
- It then runs, with the new binary and in order: when the Claude Code plugin is installed,
531
- `claude plugin marketplace update agent-toggle` and `claude plugin update agent-toggle@agent-toggle`,
532
- then `agent-toggle install-shims`. A failing step prints a warning and makes the exit code `1`.
533
+ It then runs, with the new binary and in order: when the Claude Code plugin is installed (from any
534
+ marketplace), `claude plugin marketplace update <marketplace>` and
535
+ `claude plugin update agent-toggle@<marketplace>`, then `agent-toggle install-shims`. A failing step prints a warning and makes the exit code `1`.
533
536
  It never prompts and writes to stderr only.
534
537
 
535
538
  | Exit | Meaning |
@@ -1011,8 +1014,9 @@ CLI (`cost --json`, `disable|enable --json`) and works for the claude harness on
1011
1014
  `agent-toggle ui` stays the cross-harness UI.
1012
1015
 
1013
1016
  Install it with `/plugin install agent-toggle --marketplace weskao/agent-toggle` (see
1014
- [Install](#install)); `claude plugin update agent-toggle@agent-toggle` picks up a new release. To
1015
- run your own checkout instead (edits reload as you save):
1017
+ [Install](#install)); `/agent-toggle update` picks up a new release: it runs `agent-toggle update`,
1018
+ which updates the CLI, this mod and the shims together (restart Claude Code to load the new mod).
1019
+ The mod and the CLI share one version. To run your own checkout instead (edits reload as you save):
1016
1020
 
1017
1021
  ```sh
1018
1022
  claude --plugin-dir ./mod # then run /agent-toggle inside Claude
@@ -1144,8 +1148,10 @@ prints a `::notice::` and exits 0, so it never adds a second red X.
1144
1148
  ## Releasing
1145
1149
 
1146
1150
  A release is a git tag `vX.Y.Z` (where `X.Y.Z` must match `agent_toggle.__version__`).
1147
- Bump `version` in `mod/.claude-plugin/plugin.json` to the same value by hand; nothing checks it,
1148
- and without it `claude plugin update` does not hand marketplace installs the new mod.
1151
+ `version` in `mod/.claude-plugin/plugin.json` must be the same value (the mod ships in lockstep;
1152
+ without the bump `claude plugin update` does not hand marketplace installs the new mod).
1153
+ `tests/test_mod_version.py` and the release workflow both fail on a mismatch.
1154
+ The `weskao-agent-plugins` marketplace lists the mod without a version, so it needs no bump there.
1149
1155
  Pushing the tag triggers `.github/workflows/release.yml`:
1150
1156
 
1151
1157
  1. **Build** job checks that the tag matches `__version__`, builds a wheel and
@@ -73,6 +73,7 @@ tests/test_install.py
73
73
  tests/test_logo.py
74
74
  tests/test_mechanisms.py
75
75
  tests/test_menu.py
76
+ tests/test_mod_version.py
76
77
  tests/test_mods.py
77
78
  tests/test_picker.py
78
79
  tests/test_platform.py
@@ -61,4 +61,8 @@ line-length = 100
61
61
  [tool.ruff.lint]
62
62
  # Pinned instead of ruff's moving default set, so a ruff upgrade cannot turn
63
63
  # CI red on unchanged code.
64
- select = ["E4", "E7", "E9", "F", "I", "W"]
64
+ select = ["E4", "E7", "E9", "F", "I", "W", "UP", "B023", "B904"]
65
+
66
+ [tool.ruff.lint.per-file-ignores]
67
+ # %-format fills JSON fixtures full of braces; f-strings would double every one.
68
+ "tests/*" = ["UP031"]
@@ -345,7 +345,7 @@ class CopilotMcpTest(CliCase):
345
345
 
346
346
  def test_enable_says_to_restore_a_vanished_key_or_file_first(self) -> None:
347
347
  self.disable()
348
- self.cfg.write_bytes('{"other": 1}\n'.encode("utf-8"))
348
+ self.cfg.write_bytes(b'{"other": 1}\n')
349
349
  self.assert_refused(self.enable(), "restore mcp-config.json", b'{"other": 1}\n')
350
350
  self.assertIn("mcpServers", self.enable()[1]["results"][0]["detail"])
351
351
  self.cfg.unlink()
@@ -298,7 +298,7 @@ class UpdateCheckTest(CliCase):
298
298
  for code in (0, 1, 2):
299
299
  seen = {}
300
300
 
301
- def fake(**kw):
301
+ def fake(seen=seen, code=code, **kw):
302
302
  seen.update(kw, after=list(kw["after"]), # lazy: read while after_steps is mocked
303
303
  to_stderr=kw["out"] is sys.stderr) # run_cli swaps stderr
304
304
  kw["out"].write("kit says hi\n")
@@ -13,7 +13,7 @@ from test_cli_surface import CliCase, snapshot
13
13
  from test_conformance import FIXTURES
14
14
  from test_project import ProjectCase
15
15
 
16
- from agent_toggle import cost, fs
16
+ from agent_toggle import __version__, cost, fs
17
17
  from agent_toggle.backends import plugin_cli
18
18
  from agent_toggle.harnesses import build
19
19
 
@@ -119,6 +119,7 @@ class CostCommandTest(CliCase):
119
119
  total = self.results("cost")[-1]
120
120
  self.assertEqual(total["total_tokens"], sum(tokens))
121
121
  self.assertEqual(total["formula"]["chars_per_token"], 4)
122
+ self.assertEqual(total["version"], __version__)
122
123
 
123
124
  def test_parked_costs_zero_and_reports_would_save(self) -> None:
124
125
  self.assertEqual(self.run_cli("disable", "skill", "big")[0], 0)
@@ -0,0 +1,20 @@
1
+ """The pane mod ships with the CLI: one version, so `claude plugin update` fetches the mod on every release."""
2
+ from __future__ import annotations
3
+
4
+ import json
5
+ import unittest
6
+ from pathlib import Path
7
+
8
+ from agent_toggle import __version__
9
+
10
+ PLUGIN_JSON = Path(__file__).resolve().parent.parent / "mod" / ".claude-plugin" / "plugin.json"
11
+
12
+
13
+ class ModVersionTest(unittest.TestCase):
14
+ def test_the_mod_version_is_the_cli_version(self) -> None:
15
+ mod = json.loads(PLUGIN_JSON.read_text(encoding="utf-8"))["version"]
16
+ self.assertEqual(mod, __version__, "bump mod/.claude-plugin/plugin.json with agent_toggle/__init__.py")
17
+
18
+
19
+ if __name__ == "__main__":
20
+ unittest.main()
@@ -414,10 +414,10 @@ class KeysTest(ScreenCase):
414
414
  for label, expected in (("Yes", [("alpha", False)]), ("No", None)):
415
415
  self.rows = demo_rows()
416
416
  win = FakeWin(["\t", "\x1b", picker.curses.KEY_MOUSE, picker.curses.KEY_MOUSE])
417
- def click():
417
+ def click(win=win, label=label):
418
418
  line = next(y for y, text in enumerate(win.screen) if "[Y] Yes" in text)
419
419
  return (0, win.screen[line].index(label), line, 0, picker.curses.BUTTON1_CLICKED)
420
- with mock.patch.object(picker.curses, "getmouse", side_effect=lambda:
420
+ with mock.patch.object(picker.curses, "getmouse", side_effect=lambda win=win:
421
421
  click() if len(win.keys) == 0 else (0, 0, 0, 0, picker.curses.BUTTON1_CLICKED)):
422
422
  out = picker.loop(win, self.rows, glyphs=theme.UNICODE)
423
423
  self.assertEqual(None if out is None else self.names(out), expected)
@@ -37,6 +37,18 @@ class AfterStepsTest(unittest.TestCase):
37
37
  self.assertEqual(self.steps("claude", ["other@x"]), [shims])
38
38
  self.assertEqual(self.steps(None, []), [shims])
39
39
 
40
+ def test_the_mod_is_updated_from_whichever_marketplace_it_was_installed_from(self) -> None:
41
+ shims = ["agent-toggle", "install-shims"]
42
+ self.assertEqual(self.steps("claude", ["agent-toggle@weskao-agent-plugins"]), [
43
+ ["claude", "plugin", "marketplace", "update", "weskao-agent-plugins"],
44
+ ["claude", "plugin", "update", "agent-toggle@weskao-agent-plugins"], shims])
45
+
46
+ def test_one_install_per_scope_is_updated_once_and_other_plugins_are_left_alone(self) -> None:
47
+ ids = ["agent-toggle@agent-toggle", "agent-toggle@agent-toggle", "agent-toggle-x@agent-toggle"]
48
+ self.assertEqual(self.steps("claude", ids), [
49
+ ["claude", "plugin", "marketplace", "update", "agent-toggle"],
50
+ ["claude", "plugin", "update", "agent-toggle@agent-toggle"], ["agent-toggle", "install-shims"]])
51
+
40
52
 
41
53
  class OfferTest(unittest.TestCase):
42
54
  """What the panel's answer does: Update now is `update` (update_kit, newest tag)."""
File without changes
File without changes