agent-toggle 0.14.0__tar.gz → 0.15.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 (93) hide show
  1. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/PKG-INFO +31 -27
  2. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/README.md +29 -26
  3. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/__init__.py +1 -1
  4. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/cli.py +12 -11
  5. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/help.py +7 -6
  6. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/locales/zh_tw_cli.py +3 -15
  7. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/shims/claude.md.tmpl +1 -1
  8. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/shims/generic.md.tmpl +1 -1
  9. agent_toggle-0.15.0/agent_toggle/update_prompt.py +80 -0
  10. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle.egg-info/PKG-INFO +31 -27
  11. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle.egg-info/SOURCES.txt +0 -2
  12. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle.egg-info/requires.txt +1 -0
  13. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/pyproject.toml +1 -1
  14. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_cli.py +54 -95
  15. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_cli_surface.py +7 -1
  16. agent_toggle-0.15.0/tests/test_update_prompt.py +104 -0
  17. agent_toggle-0.14.0/agent_toggle/update_check.py +0 -361
  18. agent_toggle-0.14.0/agent_toggle/update_prompt.py +0 -242
  19. agent_toggle-0.14.0/tests/test_update_check.py +0 -423
  20. agent_toggle-0.14.0/tests/test_update_prompt.py +0 -90
  21. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/LICENSE +0 -0
  22. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/__main__.py +0 -0
  23. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/backends/__init__.py +0 -0
  24. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/backends/flag_json.py +0 -0
  25. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/backends/mcp_json.py +0 -0
  26. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/backends/mcp_toml.py +0 -0
  27. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/backends/plugin_cli.py +0 -0
  28. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/companions.py +0 -0
  29. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/completions.py +0 -0
  30. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/config.py +0 -0
  31. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/cost.py +0 -0
  32. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/doctor.py +0 -0
  33. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/fs.py +0 -0
  34. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/harnesses.py +0 -0
  35. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/i18n.py +0 -0
  36. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/locales/__init__.py +0 -0
  37. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/locales/zh_tw_common.py +0 -0
  38. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/locales/zh_tw_config.py +0 -0
  39. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/locales/zh_tw_picker.py +0 -0
  40. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/mechanisms.py +0 -0
  41. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/ops.py +0 -0
  42. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/output.py +0 -0
  43. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/profiles.py +0 -0
  44. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/settings.py +0 -0
  45. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/spinner.py +0 -0
  46. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/store.py +0 -0
  47. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/toml_check.py +0 -0
  48. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/ui/__init__.py +0 -0
  49. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/ui/config_menu.py +0 -0
  50. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/ui/logo.py +0 -0
  51. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/ui/menu.py +0 -0
  52. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/ui/model.py +0 -0
  53. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/ui/picker.py +0 -0
  54. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/ui/theme.py +0 -0
  55. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle/undo.py +0 -0
  56. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle.egg-info/dependency_links.txt +0 -0
  57. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle.egg-info/entry_points.txt +0 -0
  58. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/agent_toggle.egg-info/top_level.txt +0 -0
  59. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/setup.cfg +0 -0
  60. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_alias.py +0 -0
  61. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_all_type.py +0 -0
  62. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_backends.py +0 -0
  63. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_color.py +0 -0
  64. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_companions.py +0 -0
  65. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_completions.py +0 -0
  66. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_config.py +0 -0
  67. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_config_menu.py +0 -0
  68. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_conformance.py +0 -0
  69. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_containment.py +0 -0
  70. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_cost.py +0 -0
  71. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_crash.py +0 -0
  72. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_doctor.py +0 -0
  73. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_encoding.py +0 -0
  74. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_fs.py +0 -0
  75. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_harnesses.py +0 -0
  76. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_help.py +0 -0
  77. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_i18n.py +0 -0
  78. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_install.py +0 -0
  79. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_logo.py +0 -0
  80. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_mechanisms.py +0 -0
  81. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_menu.py +0 -0
  82. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_mods.py +0 -0
  83. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_picker.py +0 -0
  84. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_platform.py +0 -0
  85. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_profiles.py +0 -0
  86. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_project.py +0 -0
  87. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_safety.py +0 -0
  88. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_same_name.py +0 -0
  89. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_secret_audit.py +0 -0
  90. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_settings.py +0 -0
  91. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_store.py +0 -0
  92. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_theme.py +0 -0
  93. {agent_toggle-0.14.0 → agent_toggle-0.15.0}/tests/test_undo.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-toggle
3
- Version: 0.14.0
3
+ Version: 0.15.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
@@ -25,6 +25,7 @@ Requires-Python: >=3.10
25
25
  Description-Content-Type: text/markdown
26
26
  License-File: LICENSE
27
27
  Requires-Dist: telegram-kit<0.3,>=0.2.2
28
+ Requires-Dist: update-kit<0.2,>=0.1
28
29
  Provides-Extra: windows
29
30
  Requires-Dist: windows-curses; sys_platform == "win32" and extra == "windows"
30
31
  Provides-Extra: telegram
@@ -202,7 +203,7 @@ agent-toggle <command> [args] # or: python3 agent_toggle.py <command> [
202
203
  | `status` | health check: harnesses found, types each supports, parked counts, gitignore, untracked parked items, stale live twins, shared dirs, and state entries whose parked item is gone (a `stale` row with the fix command; read-only, still exit `0`) |
203
204
  | `list [type]` | what is currently disabled (`--project <dir>` filters to one project) |
204
205
  | `cost [--type T]` | estimated startup tokens per item, biggest first (read-only; `--harness H` filters; `--project <dir>` prices a repo's `.claude/` and `.mcp.json` instead of user scope) |
205
- | `update` | check PyPI now (ignores the cache and a skipped version) and print the commands that update the CLI, the Claude Code plugin (when installed) and the shims; runs none of them. `/agent-toggle update` in Claude Code does the same; opening the pane toasts a newer release once |
206
+ | `update` (also `--update`) | install the newest release tag (`uv tool install --force --no-sources git+<repo>@<tag>`, or `pipx install --force` for a pipx install), then, with the new version, update the Claude Code plugin (when installed) and the shims. Never asks. Exit `0` updated or already current, `1` the check or install failed (offline, rate limit), `2` a dev install (use `git pull`) or an install it cannot manage (the manual command is printed). Messages go to stderr; `--json` has no JSON for it. The `/agent-toggle` pane command has no `update`: run it in a terminal |
206
207
  | `install-shims` | write the skill shim into every installed harness that is on in the settings (an off one is skipped unless named with `--harness`); refuses to overwrite a file it did not write (`--dry-run` shows the plan); reports stale legacy `<dir>-disabled/` lines in a harness-home `.gitignore` (`--gitignore` removes them, `--no-gitignore` skips the check); on a terminal, then asks y/n for each problem `doctor` can fix (see [Fixing](#fixing)) |
207
208
  | `disable <type> <name>...` | park one or more items (`--dry-run` shows the plan; `--project <dir>` for a repo's own `.claude/` and `.mcp.json`) |
208
209
  | `enable <type> <name>...` | put them back (`--dry-run` shows the plan; `--project <dir>` likewise) |
@@ -487,33 +488,37 @@ invalid value in either is ignored with one warning on stderr.
487
488
  - `default_harness` is what `disable` / `enable` act on without `--harness`, even when that
488
489
  harness is switched off (with one warning on stderr);
489
490
  `disable` / `enable --project` without `--harness` still default to `claude`.
490
- - `language` translates the config menu, help, update prompt and picker chrome; command output stays English.
491
+ - `language` translates the config menu, help, update messages and picker chrome; command output stays English.
491
492
  - telegram-kit is a regular dependency now, so reinstalling keeps the Telegram settings working;
492
493
  `agent-toggle[telegram]` still resolves.
493
494
 
494
- ## Update check
495
-
496
- On every invocation (unless disabled) agent-toggle checks PyPI for a newer release. It starts
497
- before the command and is offered after it, on every exit path; it overlaps the command and waits at most 0.8 s afterwards.
498
-
499
- - **On a terminal** (stdin and stderr are TTYs, no `--json`): a panel on stderr, "agent-toggle X
500
- is available (you have Y)", with `Update now`, `Skip` and `Skip until next version`, a release
501
- notes link, and `↑` `↓` `Enter` `q`. `Update now` runs, in order and stopping at the first
502
- failure: `uv tool upgrade agent-toggle` (`pipx upgrade agent-toggle` for a pipx install), then,
503
- when the Claude Code plugin is installed, `claude plugin marketplace update agent-toggle` and
504
- `claude plugin update agent-toggle@agent-toggle`, then `agent-toggle install-shims`. A failure
505
- gives a yellow warning listing the commands to run yourself.
506
- - **Windows**: a running `agent-toggle.exe` cannot be replaced, so there is no `Update now`; the
507
- commands are printed instead (as below), to run once agent-toggle has exited.
508
- - **Off a terminal** (or `--json`): a stderr line, `agent-toggle X is available (you have Y)`,
509
- then those commands, one per line.
510
- - It never changes the exit code or stdout, so `--json` output stays one document.
511
-
512
- It is one `GET https://pypi.org/pypi/agent-toggle/json` on a background thread with a 0.8 s
513
- timeout, cached for 10 minutes in `~/.agent-toggle/update-check.json`. Nothing is sent beyond a
514
- normal HTTP request with the User-Agent `agent-toggle-update-check`. See `SECURITY.md`
515
- for the hardening. To turn it off, set `AGENT_TOGGLE_UPDATE_CHECK=0` or the `update_check` setting
516
- to off (the settings menu's "Check for updates").
495
+ ## Update
496
+
497
+ `agent-toggle update` (or `agent-toggle --update`) is a self-update through
498
+ update-kit (a small stdlib-only helper library): it asks the repository for its newest `vX.Y.Z`
499
+ tag (`git ls-remote --tags`), and when that is newer than the running version it installs it with
500
+ `uv tool install --force --no-sources git+<repo>@<tag>` (`pipx install --force` for a pipx install).
501
+ It then runs, with the new binary and in order: when the Claude Code plugin is installed,
502
+ `claude plugin marketplace update agent-toggle` and `claude plugin update agent-toggle@agent-toggle`,
503
+ then `agent-toggle install-shims`. A failing step prints a warning and makes the exit code `1`.
504
+ It never prompts and writes to stderr only.
505
+
506
+ | Exit | Meaning |
507
+ |---|---|
508
+ | `0` | updated, or already up to date |
509
+ | `1` | could not check (offline, rate limit) or the install failed |
510
+ | `2` | a dev install (`git pull` instead), or an install kind it cannot manage (the manual command is printed) |
511
+
512
+ On Windows a running `agent-toggle.exe` cannot be replaced, so the install runs detached after
513
+ agent-toggle exits (log in `%USERPROFILE%\.update-kit\`).
514
+
515
+ **The hint.** After a command, on an interactive terminal only (stderr is a TTY, never under
516
+ `--json`, pipes, launchd or timers), agent-toggle prints one stderr line when a newer release
517
+ exists, `agent-toggle vX.Y.Z is available (you have A) — run `agent-toggle update``. The check is
518
+ `git ls-remote` with a 1.5 s timeout, cached for 24 hours (failures too) in
519
+ `~/.update-kit/state.json`. It never changes the exit code or stdout. To turn it off, set
520
+ `AGENT_TOGGLE_UPDATE_CHECK=0` or the `update_check` setting to off (the settings menu's
521
+ "Check for updates").
517
522
 
518
523
  ## Help
519
524
 
@@ -905,7 +910,6 @@ bookkeeping.
905
910
  | `parked/<sha8>/` | items parked by `--project` (`<sha8>` = first 8 hex of the SHA-1 of the resolved project dir) |
906
911
  | `profiles/` | `<name>.json` profiles (dir `0700`, files `0600`) |
907
912
  | `config.json` | [settings](#settings--config-menu) and the Telegram chat id (mode `0600`, atomic write, unknown keys kept) |
908
- | `update-check.json` | the [update check](#update-check)'s 10-minute cache (mode `0600`, atomic write) |
909
913
 
910
914
  A killed run loses at most the one item in flight. If that was a flag write or a
911
915
  dir move (with its companions), its `pending` record (the full entry, a flag's
@@ -169,7 +169,7 @@ agent-toggle <command> [args] # or: python3 agent_toggle.py <command> [
169
169
  | `status` | health check: harnesses found, types each supports, parked counts, gitignore, untracked parked items, stale live twins, shared dirs, and state entries whose parked item is gone (a `stale` row with the fix command; read-only, still exit `0`) |
170
170
  | `list [type]` | what is currently disabled (`--project <dir>` filters to one project) |
171
171
  | `cost [--type T]` | estimated startup tokens per item, biggest first (read-only; `--harness H` filters; `--project <dir>` prices a repo's `.claude/` and `.mcp.json` instead of user scope) |
172
- | `update` | check PyPI now (ignores the cache and a skipped version) and print the commands that update the CLI, the Claude Code plugin (when installed) and the shims; runs none of them. `/agent-toggle update` in Claude Code does the same; opening the pane toasts a newer release once |
172
+ | `update` (also `--update`) | install the newest release tag (`uv tool install --force --no-sources git+<repo>@<tag>`, or `pipx install --force` for a pipx install), then, with the new version, update the Claude Code plugin (when installed) and the shims. Never asks. Exit `0` updated or already current, `1` the check or install failed (offline, rate limit), `2` a dev install (use `git pull`) or an install it cannot manage (the manual command is printed). Messages go to stderr; `--json` has no JSON for it. The `/agent-toggle` pane command has no `update`: run it in a terminal |
173
173
  | `install-shims` | write the skill shim into every installed harness that is on in the settings (an off one is skipped unless named with `--harness`); refuses to overwrite a file it did not write (`--dry-run` shows the plan); reports stale legacy `<dir>-disabled/` lines in a harness-home `.gitignore` (`--gitignore` removes them, `--no-gitignore` skips the check); on a terminal, then asks y/n for each problem `doctor` can fix (see [Fixing](#fixing)) |
174
174
  | `disable <type> <name>...` | park one or more items (`--dry-run` shows the plan; `--project <dir>` for a repo's own `.claude/` and `.mcp.json`) |
175
175
  | `enable <type> <name>...` | put them back (`--dry-run` shows the plan; `--project <dir>` likewise) |
@@ -454,33 +454,37 @@ invalid value in either is ignored with one warning on stderr.
454
454
  - `default_harness` is what `disable` / `enable` act on without `--harness`, even when that
455
455
  harness is switched off (with one warning on stderr);
456
456
  `disable` / `enable --project` without `--harness` still default to `claude`.
457
- - `language` translates the config menu, help, update prompt and picker chrome; command output stays English.
457
+ - `language` translates the config menu, help, update messages and picker chrome; command output stays English.
458
458
  - telegram-kit is a regular dependency now, so reinstalling keeps the Telegram settings working;
459
459
  `agent-toggle[telegram]` still resolves.
460
460
 
461
- ## Update check
462
-
463
- On every invocation (unless disabled) agent-toggle checks PyPI for a newer release. It starts
464
- before the command and is offered after it, on every exit path; it overlaps the command and waits at most 0.8 s afterwards.
465
-
466
- - **On a terminal** (stdin and stderr are TTYs, no `--json`): a panel on stderr, "agent-toggle X
467
- is available (you have Y)", with `Update now`, `Skip` and `Skip until next version`, a release
468
- notes link, and `↑` `↓` `Enter` `q`. `Update now` runs, in order and stopping at the first
469
- failure: `uv tool upgrade agent-toggle` (`pipx upgrade agent-toggle` for a pipx install), then,
470
- when the Claude Code plugin is installed, `claude plugin marketplace update agent-toggle` and
471
- `claude plugin update agent-toggle@agent-toggle`, then `agent-toggle install-shims`. A failure
472
- gives a yellow warning listing the commands to run yourself.
473
- - **Windows**: a running `agent-toggle.exe` cannot be replaced, so there is no `Update now`; the
474
- commands are printed instead (as below), to run once agent-toggle has exited.
475
- - **Off a terminal** (or `--json`): a stderr line, `agent-toggle X is available (you have Y)`,
476
- then those commands, one per line.
477
- - It never changes the exit code or stdout, so `--json` output stays one document.
478
-
479
- It is one `GET https://pypi.org/pypi/agent-toggle/json` on a background thread with a 0.8 s
480
- timeout, cached for 10 minutes in `~/.agent-toggle/update-check.json`. Nothing is sent beyond a
481
- normal HTTP request with the User-Agent `agent-toggle-update-check`. See `SECURITY.md`
482
- for the hardening. To turn it off, set `AGENT_TOGGLE_UPDATE_CHECK=0` or the `update_check` setting
483
- to off (the settings menu's "Check for updates").
461
+ ## Update
462
+
463
+ `agent-toggle update` (or `agent-toggle --update`) is a self-update through
464
+ update-kit (a small stdlib-only helper library): it asks the repository for its newest `vX.Y.Z`
465
+ tag (`git ls-remote --tags`), and when that is newer than the running version it installs it with
466
+ `uv tool install --force --no-sources git+<repo>@<tag>` (`pipx install --force` for a pipx install).
467
+ It then runs, with the new binary and in order: when the Claude Code plugin is installed,
468
+ `claude plugin marketplace update agent-toggle` and `claude plugin update agent-toggle@agent-toggle`,
469
+ then `agent-toggle install-shims`. A failing step prints a warning and makes the exit code `1`.
470
+ It never prompts and writes to stderr only.
471
+
472
+ | Exit | Meaning |
473
+ |---|---|
474
+ | `0` | updated, or already up to date |
475
+ | `1` | could not check (offline, rate limit) or the install failed |
476
+ | `2` | a dev install (`git pull` instead), or an install kind it cannot manage (the manual command is printed) |
477
+
478
+ On Windows a running `agent-toggle.exe` cannot be replaced, so the install runs detached after
479
+ agent-toggle exits (log in `%USERPROFILE%\.update-kit\`).
480
+
481
+ **The hint.** After a command, on an interactive terminal only (stderr is a TTY, never under
482
+ `--json`, pipes, launchd or timers), agent-toggle prints one stderr line when a newer release
483
+ exists, `agent-toggle vX.Y.Z is available (you have A) — run `agent-toggle update``. The check is
484
+ `git ls-remote` with a 1.5 s timeout, cached for 24 hours (failures too) in
485
+ `~/.update-kit/state.json`. It never changes the exit code or stdout. To turn it off, set
486
+ `AGENT_TOGGLE_UPDATE_CHECK=0` or the `update_check` setting to off (the settings menu's
487
+ "Check for updates").
484
488
 
485
489
  ## Help
486
490
 
@@ -872,7 +876,6 @@ bookkeeping.
872
876
  | `parked/<sha8>/` | items parked by `--project` (`<sha8>` = first 8 hex of the SHA-1 of the resolved project dir) |
873
877
  | `profiles/` | `<name>.json` profiles (dir `0700`, files `0600`) |
874
878
  | `config.json` | [settings](#settings--config-menu) and the Telegram chat id (mode `0600`, atomic write, unknown keys kept) |
875
- | `update-check.json` | the [update check](#update-check)'s 10-minute cache (mode `0600`, atomic write) |
876
879
 
877
880
  A killed run loses at most the one item in flight. If that was a flag write or a
878
881
  dir move (with its companions), its `pending` record (the full entry, a flag's
@@ -1,3 +1,3 @@
1
1
  """agent-toggle: temporarily disable / re-enable AI-agent resources."""
2
2
 
3
- __version__ = "0.14.0"
3
+ __version__ = "0.15.0"
@@ -36,7 +36,7 @@ Usage:
36
36
  failure, 2 usage error, 3 locked, 4 unsupported pair / harness missing.
37
37
  Settings: $HOME/.agent-toggle/config.json (see `config`); env overrides
38
38
  AGENT_TOGGLE_UPDATE_CHECK / _COLOR / _LANG / _DEFAULT_HARNESS.
39
- Update check: on by default, stderr only; AGENT_TOGGLE_UPDATE_CHECK=0 turns it off.
39
+ Update hint: on by default, a terminal's stderr only; AGENT_TOGGLE_UPDATE_CHECK=0 turns it off.
40
40
  """
41
41
  from __future__ import annotations
42
42
 
@@ -695,7 +695,7 @@ def build_parser() -> argparse.ArgumentParser:
695
695
  sub.add_parser("doctor", parents=[common],
696
696
  help="check harness layouts and state against disk; offers y/n fixes")
697
697
  sub.add_parser("update", parents=[common],
698
- help="check for a newer release; show the commands that update everything")
698
+ help="install the newest release, then refresh the plugin and shims")
699
699
  cf = sub.add_parser("config", parents=[common],
700
700
  help="Telegram settings for the CI failure alerts")
701
701
  cf.add_argument("action", nargs="?", choices=("test", "sync-ci"),
@@ -723,9 +723,9 @@ def build_parser() -> argparse.ArgumentParser:
723
723
  def main(argv: list[str] | None = None) -> int:
724
724
  """Run one command and return its exit code (the process exit code).
725
725
 
726
- Settings apply first; the update check starts before dispatch and is offered in a
727
- `finally`, so every way out (a command, --help/--version, a usage error, Locked,
728
- CliError) gets it. It writes to stderr only and never changes the exit code."""
726
+ Settings apply first; the update hint is printed in a `finally`, so every way out
727
+ (a command, --help/--version, a usage error, Locked, CliError) gets it. It writes
728
+ to stderr only and never changes the exit code."""
729
729
  argv = normalize_argv(sys.argv[1:] if argv is None else argv)
730
730
  try:
731
731
  language, color = settings.get("language"), settings.get("color")
@@ -734,16 +734,17 @@ def main(argv: list[str] | None = None) -> int:
734
734
  i18n.set_language(language)
735
735
  color = scan_color(argv, color) # --color > env > setting > auto
736
736
  i = _command_index(argv)
737
- # `update` is the check; `completion` may run on every shell start (eval "$(...)")
738
- started = None if argv[i:i + 1] in (["update"], ["completion"]) else update_prompt.start()
737
+ hint = True
739
738
  try:
740
739
  return _main(argv, color)
741
740
  except KeyboardInterrupt:
742
- started = None # never prompt after the user hit Ctrl-C
741
+ hint = False # never ask anything after the user hit Ctrl-C
743
742
  raise
744
743
  finally:
745
- with contextlib.suppress(KeyboardInterrupt): # Ctrl-C while waiting on the check
746
- update_prompt.offer(started, json_mode="--json" in argv, color_mode=color)
744
+ # `update` is the update; `completion` may run on every shell start (eval "$(...)")
745
+ if hint and argv[i:i + 1] not in (["update"], ["completion"]):
746
+ with contextlib.suppress(KeyboardInterrupt):
747
+ update_prompt.hint(json_mode="--json" in argv)
747
748
 
748
749
 
749
750
  def _main(argv: list[str], color: str) -> int:
@@ -804,7 +805,7 @@ def _main(argv: list[str], color: str) -> int:
804
805
  if fixes and _interactive(out):
805
806
  doctor.offer_fixes(fixes, out)
806
807
  elif cmd == "update":
807
- update_prompt.cmd_update(out)
808
+ return update_prompt.cmd_update()
808
809
  elif cmd == "config":
809
810
  config.cmd_config(args, out)
810
811
  elif cmd == "completion":
@@ -137,12 +137,13 @@ def commands(default_harness: str = "claude") -> dict[str, Cmd]:
137
137
  examples=["config", "config test", "config sync-ci --dry-run"]),
138
138
  "update": Cmd(
139
139
  "update",
140
- t("help.update.summary", "check for a newer release; show how to update"),
141
- t("help.update.desc", "Ask PyPI now (ignoring the cache and a skipped version) and, "
142
- "when a newer release exists, print the commands that update "
143
- "the CLI, the Claude Code plugin (if installed) and the shims. "
144
- "Runs none of them."),
145
- examples=["update", "update --json"]),
140
+ t("help.update.summary", "install the newest release; refresh the plugin and shims"),
141
+ t("help.update.desc", "Install the newest release tag (uv tool or pipx), then, with the "
142
+ "new version, update the Claude Code plugin (if installed) and "
143
+ "reinstall the shims. Never asks; `--update` is the same. A dev "
144
+ "install says to git pull (exit 2). Exit 1 when the check or "
145
+ "the install fails."),
146
+ examples=["update"]),
146
147
  "completion": Cmd(
147
148
  "completion bash|zsh|fish|powershell",
148
149
  t("help.completion.summary", "print the shell completion script"),
@@ -27,9 +27,9 @@ CATALOG: dict[str, str] = {
27
27
  "help.cost.summary": "每個項目的預估啟動 token 數,由大到小",
28
28
  "help.cost.desc": "估算每個項目啟動時載入多少 token(字元數/4,±25%),"
29
29
  "以及暫存項目已省下多少。",
30
- "help.update.summary": "檢查新版本;顯示更新方式",
31
- "help.update.desc": "立即向 PyPI 查詢(忽略快取與已略過的版本);有新版本時,印出更新 CLI、"
32
- "Claude Code 外掛(若已安裝)與 shim 的指令。不會執行任何指令。",
30
+ "help.update.summary": "安裝最新版本,並更新外掛與 shim",
31
+ "help.update.desc": "安裝最新的發行標籤(uv tool / pipx),完成後以新版更新 Claude Code 外掛(若已安裝)"
32
+ "並重新安裝 shim。不會提問;`--update` 等同 `update`。開發版安裝請改用 git pull(結束碼 2)。",
33
33
  "help.completion.summary": "印出 shell 自動補完腳本",
34
34
  "help.completion.desc": "在 stdout 印出某個 shell 的 Tab 補完腳本;"
35
35
  "存到該 shell 載入補完的位置(見 README「Shell completion」)。",
@@ -85,16 +85,4 @@ CATALOG: dict[str, str] = {
85
85
  "help.opt.help": "顯示說明(`help <command>` 看單一指令)",
86
86
  "help.arguments": "參數",
87
87
  "help.options": "選項",
88
- # update prompt
89
- "update.available": "agent-toggle %s 已推出(目前版本 %s)",
90
- "update.keys": "↑↓ 選擇 · ⏎ 確認 · q/Ctrl+C 略過",
91
- "update.keys_ascii": "上/下 選擇,Enter 確認,q/Ctrl+C 略過",
92
- "update.choose": "請選擇 1-3(Enter = 1,q/Ctrl+C = 略過):",
93
- "update.now": "立即更新",
94
- "update.skip": "略過",
95
- "update.skip_detail": "下次執行再問",
96
- "update.skip_version": "略過到下一個版本",
97
- "update.skip_version_detail": "有更新的版本時再問",
98
- "update.notes": "版本說明:%s",
99
- "update.failed": "更新未完成 — 請自行執行:%s",
100
88
  }
@@ -17,7 +17,7 @@ agent-toggle <command> --json
17
17
  ```
18
18
  If `agent-toggle` is not on PATH, ask the user to install it (`uv tool install agent-toggle`); do not guess where a checkout lives.
19
19
 
20
- Commands: `status` (run first), `doctor` (read-only health check), `update` (checks for a newer release and lists the commands that update it; run none yourself, relay them), `list [type]`,
20
+ Commands: `status` (run first), `doctor` (read-only health check), `update` (installs the newest release, then refreshes the plugin and shims; run it only when the user asks to update), `list [type]`,
21
21
  `cost [--type T]` (startup token estimates, biggest first), `disable <type> <name>...` (type `all`: every type with that name),
22
22
  `enable <type> <name>...`, `enable --all`, `undo` (reverse the last batch),
23
23
  `profile save|apply|diff|delete <name>`, `profile rename <old> <new>`, `profile list` (see Profiles), `migrate`; add `--harness claude|codex|grok|opencode|openclaw|copilot|vibe|devin|agy`
@@ -20,7 +20,7 @@ You are running in `__HARNESS__`: pass `--harness __HARNESS__` for its own items
20
20
  (the default is `claude`). Other harnesses: `--harness claude|codex|grok|opencode|openclaw|copilot|vibe|devin|agy`.
21
21
 
22
22
  Commands: `status` (run first; shows which types this harness supports),
23
- `doctor` (read-only health check), `update` (checks for a newer release and lists the commands that update it; run none yourself, relay them), `list [type]`, `cost [--type T]` (startup
23
+ `doctor` (read-only health check), `update` (installs the newest release, then refreshes the plugin and shims; run it only when the user asks to update), `list [type]`, `cost [--type T]` (startup
24
24
  token estimates, biggest first), `disable <type> <name>...` (type `all`: every type with that name), `enable <type> <name>...`,
25
25
  `enable --all`, `undo` (reverse the last batch), `profile save|apply|diff|delete <name>`,
26
26
  `profile rename <old> <new>`, `profile list` (see Profiles); add `--dry-run` to preview. `<type>` = `skill` / `agent` /
@@ -0,0 +1,80 @@
1
+ """`update` / `--update` and the one-line "newer release" hint, both through update_kit.
2
+
3
+ cmd_update() # install the newest tag, then refresh the plugin and shims; returns the exit code
4
+ hint() # after a command: one stderr line when a newer release exists (TTY only)
5
+
6
+ No prompts: the old arrow-key panel and its skip-version bookkeeping are gone.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import os
11
+ import subprocess
12
+ import sys
13
+ from collections.abc import Sequence
14
+ from functools import cached_property
15
+
16
+ from . import __version__, i18n, settings
17
+ from .backends import plugin_cli
18
+ from .ui import theme
19
+
20
+ DIST = "agent-toggle"
21
+ REPO = "weskao/agent-toggle"
22
+ PLUGIN = "agent-toggle@agent-toggle"
23
+
24
+
25
+ def after_steps() -> list[list[str]]:
26
+ """Run with the NEW binary once the install is done: the Claude Code plugin when it is
27
+ installed, then the shims."""
28
+ steps: list[list[str]] = []
29
+ # bare "claude": what a person pastes; _run() resolves it, as claude is often off PATH
30
+ if plugin_cli.claude_bin() and any(p["id"] == PLUGIN for p in plugin_cli.list_plugins(lambda _msg: None)):
31
+ steps += [["claude", "plugin", "marketplace", "update", DIST],
32
+ ["claude", "plugin", "update", PLUGIN]]
33
+ return [*steps, [DIST, "install-shims"]]
34
+
35
+
36
+ class _LazySteps(Sequence):
37
+ """after_steps(), computed on first use: update_kit only reads `after` once an install
38
+ will happen, so a dev refusal / "already up to date" skips `claude plugin list` (0.8 s)."""
39
+
40
+ @cached_property
41
+ def _steps(self) -> list[list[str]]:
42
+ return after_steps()
43
+
44
+ def __len__(self) -> int:
45
+ return len(self._steps)
46
+
47
+ def __getitem__(self, i):
48
+ return self._steps[i]
49
+
50
+
51
+ def _run(cmd, **kw):
52
+ """update_kit's subprocess runner: their output stays off stdout and no step may prompt,
53
+ and a step like `install-shims` must not re-check for updates and print the hint."""
54
+ if cmd[0] == "claude":
55
+ cmd = [plugin_cli.claude_bin() or "claude", *cmd[1:]]
56
+ # update_kit passes its own env (no-prompt git vars) for the install step: merge, don't replace
57
+ kw["env"] = {**kw.get("env", os.environ), "AGENT_TOGGLE_UPDATE_CHECK": "0"}
58
+ return subprocess.run(cmd, stdout=sys.stderr, stdin=subprocess.DEVNULL, **kw)
59
+
60
+
61
+ def cmd_update() -> int:
62
+ # imported here, like telegram_kit: a checkout run (`python3 agent_toggle.py`) or a stale env lacks it
63
+ import update_kit
64
+ return update_kit.update(app=DIST, repo=REPO, dist=DIST, lang=i18n.LANGUAGE,
65
+ after=_LazySteps(), out=sys.stderr, run=_run)
66
+
67
+
68
+ def hint(*, json_mode: bool = False) -> None:
69
+ """One stderr line when a newer release exists: only on a terminal, never under --json,
70
+ off with the `update_check` setting (env AGENT_TOGGLE_UPDATE_CHECK wins). Never raises."""
71
+ try:
72
+ import update_kit
73
+ if json_mode or not settings.get("update_check") or not update_kit.should_hint(sys.stderr):
74
+ return
75
+ latest = update_kit.check(repo=REPO, current=__version__)
76
+ if latest:
77
+ print(theme.encodable(update_kit.hint(DIST, latest, __version__, i18n.LANGUAGE), sys.stderr),
78
+ file=sys.stderr)
79
+ except Exception: # noqa: BLE001 - a hint must never fail the command
80
+ pass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-toggle
3
- Version: 0.14.0
3
+ Version: 0.15.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
@@ -25,6 +25,7 @@ Requires-Python: >=3.10
25
25
  Description-Content-Type: text/markdown
26
26
  License-File: LICENSE
27
27
  Requires-Dist: telegram-kit<0.3,>=0.2.2
28
+ Requires-Dist: update-kit<0.2,>=0.1
28
29
  Provides-Extra: windows
29
30
  Requires-Dist: windows-curses; sys_platform == "win32" and extra == "windows"
30
31
  Provides-Extra: telegram
@@ -202,7 +203,7 @@ agent-toggle <command> [args] # or: python3 agent_toggle.py <command> [
202
203
  | `status` | health check: harnesses found, types each supports, parked counts, gitignore, untracked parked items, stale live twins, shared dirs, and state entries whose parked item is gone (a `stale` row with the fix command; read-only, still exit `0`) |
203
204
  | `list [type]` | what is currently disabled (`--project <dir>` filters to one project) |
204
205
  | `cost [--type T]` | estimated startup tokens per item, biggest first (read-only; `--harness H` filters; `--project <dir>` prices a repo's `.claude/` and `.mcp.json` instead of user scope) |
205
- | `update` | check PyPI now (ignores the cache and a skipped version) and print the commands that update the CLI, the Claude Code plugin (when installed) and the shims; runs none of them. `/agent-toggle update` in Claude Code does the same; opening the pane toasts a newer release once |
206
+ | `update` (also `--update`) | install the newest release tag (`uv tool install --force --no-sources git+<repo>@<tag>`, or `pipx install --force` for a pipx install), then, with the new version, update the Claude Code plugin (when installed) and the shims. Never asks. Exit `0` updated or already current, `1` the check or install failed (offline, rate limit), `2` a dev install (use `git pull`) or an install it cannot manage (the manual command is printed). Messages go to stderr; `--json` has no JSON for it. The `/agent-toggle` pane command has no `update`: run it in a terminal |
206
207
  | `install-shims` | write the skill shim into every installed harness that is on in the settings (an off one is skipped unless named with `--harness`); refuses to overwrite a file it did not write (`--dry-run` shows the plan); reports stale legacy `<dir>-disabled/` lines in a harness-home `.gitignore` (`--gitignore` removes them, `--no-gitignore` skips the check); on a terminal, then asks y/n for each problem `doctor` can fix (see [Fixing](#fixing)) |
207
208
  | `disable <type> <name>...` | park one or more items (`--dry-run` shows the plan; `--project <dir>` for a repo's own `.claude/` and `.mcp.json`) |
208
209
  | `enable <type> <name>...` | put them back (`--dry-run` shows the plan; `--project <dir>` likewise) |
@@ -487,33 +488,37 @@ invalid value in either is ignored with one warning on stderr.
487
488
  - `default_harness` is what `disable` / `enable` act on without `--harness`, even when that
488
489
  harness is switched off (with one warning on stderr);
489
490
  `disable` / `enable --project` without `--harness` still default to `claude`.
490
- - `language` translates the config menu, help, update prompt and picker chrome; command output stays English.
491
+ - `language` translates the config menu, help, update messages and picker chrome; command output stays English.
491
492
  - telegram-kit is a regular dependency now, so reinstalling keeps the Telegram settings working;
492
493
  `agent-toggle[telegram]` still resolves.
493
494
 
494
- ## Update check
495
-
496
- On every invocation (unless disabled) agent-toggle checks PyPI for a newer release. It starts
497
- before the command and is offered after it, on every exit path; it overlaps the command and waits at most 0.8 s afterwards.
498
-
499
- - **On a terminal** (stdin and stderr are TTYs, no `--json`): a panel on stderr, "agent-toggle X
500
- is available (you have Y)", with `Update now`, `Skip` and `Skip until next version`, a release
501
- notes link, and `↑` `↓` `Enter` `q`. `Update now` runs, in order and stopping at the first
502
- failure: `uv tool upgrade agent-toggle` (`pipx upgrade agent-toggle` for a pipx install), then,
503
- when the Claude Code plugin is installed, `claude plugin marketplace update agent-toggle` and
504
- `claude plugin update agent-toggle@agent-toggle`, then `agent-toggle install-shims`. A failure
505
- gives a yellow warning listing the commands to run yourself.
506
- - **Windows**: a running `agent-toggle.exe` cannot be replaced, so there is no `Update now`; the
507
- commands are printed instead (as below), to run once agent-toggle has exited.
508
- - **Off a terminal** (or `--json`): a stderr line, `agent-toggle X is available (you have Y)`,
509
- then those commands, one per line.
510
- - It never changes the exit code or stdout, so `--json` output stays one document.
511
-
512
- It is one `GET https://pypi.org/pypi/agent-toggle/json` on a background thread with a 0.8 s
513
- timeout, cached for 10 minutes in `~/.agent-toggle/update-check.json`. Nothing is sent beyond a
514
- normal HTTP request with the User-Agent `agent-toggle-update-check`. See `SECURITY.md`
515
- for the hardening. To turn it off, set `AGENT_TOGGLE_UPDATE_CHECK=0` or the `update_check` setting
516
- to off (the settings menu's "Check for updates").
495
+ ## Update
496
+
497
+ `agent-toggle update` (or `agent-toggle --update`) is a self-update through
498
+ update-kit (a small stdlib-only helper library): it asks the repository for its newest `vX.Y.Z`
499
+ tag (`git ls-remote --tags`), and when that is newer than the running version it installs it with
500
+ `uv tool install --force --no-sources git+<repo>@<tag>` (`pipx install --force` for a pipx install).
501
+ It then runs, with the new binary and in order: when the Claude Code plugin is installed,
502
+ `claude plugin marketplace update agent-toggle` and `claude plugin update agent-toggle@agent-toggle`,
503
+ then `agent-toggle install-shims`. A failing step prints a warning and makes the exit code `1`.
504
+ It never prompts and writes to stderr only.
505
+
506
+ | Exit | Meaning |
507
+ |---|---|
508
+ | `0` | updated, or already up to date |
509
+ | `1` | could not check (offline, rate limit) or the install failed |
510
+ | `2` | a dev install (`git pull` instead), or an install kind it cannot manage (the manual command is printed) |
511
+
512
+ On Windows a running `agent-toggle.exe` cannot be replaced, so the install runs detached after
513
+ agent-toggle exits (log in `%USERPROFILE%\.update-kit\`).
514
+
515
+ **The hint.** After a command, on an interactive terminal only (stderr is a TTY, never under
516
+ `--json`, pipes, launchd or timers), agent-toggle prints one stderr line when a newer release
517
+ exists, `agent-toggle vX.Y.Z is available (you have A) — run `agent-toggle update``. The check is
518
+ `git ls-remote` with a 1.5 s timeout, cached for 24 hours (failures too) in
519
+ `~/.update-kit/state.json`. It never changes the exit code or stdout. To turn it off, set
520
+ `AGENT_TOGGLE_UPDATE_CHECK=0` or the `update_check` setting to off (the settings menu's
521
+ "Check for updates").
517
522
 
518
523
  ## Help
519
524
 
@@ -905,7 +910,6 @@ bookkeeping.
905
910
  | `parked/<sha8>/` | items parked by `--project` (`<sha8>` = first 8 hex of the SHA-1 of the resolved project dir) |
906
911
  | `profiles/` | `<name>.json` profiles (dir `0700`, files `0600`) |
907
912
  | `config.json` | [settings](#settings--config-menu) and the Telegram chat id (mode `0600`, atomic write, unknown keys kept) |
908
- | `update-check.json` | the [update check](#update-check)'s 10-minute cache (mode `0600`, atomic write) |
909
913
 
910
914
  A killed run loses at most the one item in flight. If that was a flag write or a
911
915
  dir move (with its companions), its `pending` record (the full entry, a flag's
@@ -22,7 +22,6 @@ agent_toggle/spinner.py
22
22
  agent_toggle/store.py
23
23
  agent_toggle/toml_check.py
24
24
  agent_toggle/undo.py
25
- agent_toggle/update_check.py
26
25
  agent_toggle/update_prompt.py
27
26
  agent_toggle.egg-info/PKG-INFO
28
27
  agent_toggle.egg-info/SOURCES.txt
@@ -85,5 +84,4 @@ tests/test_settings.py
85
84
  tests/test_store.py
86
85
  tests/test_theme.py
87
86
  tests/test_undo.py
88
- tests/test_update_check.py
89
87
  tests/test_update_prompt.py
@@ -1,4 +1,5 @@
1
1
  telegram-kit<0.3,>=0.2.2
2
+ update-kit<0.2,>=0.1
2
3
 
3
4
  [telegram]
4
5
  telegram-kit<0.3,>=0.2.2
@@ -27,7 +27,7 @@ classifiers = [
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
+ dependencies = ["telegram-kit>=0.2.2,<0.3", "update-kit>=0.1,<0.2"]
31
31
 
32
32
  [project.urls]
33
33
  Homepage = "https://github.com/weskao/agent-toggle"