agent-toggle 0.10.0__tar.gz → 0.11.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.
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/PKG-INFO +11 -5
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/README.md +10 -4
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/__init__.py +1 -1
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/cli.py +7 -2
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/help.py +9 -1
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/locales/zh_tw_cli.py +3 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/shims/claude.md.tmpl +2 -2
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/shims/generic.md.tmpl +1 -1
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/update_check.py +4 -2
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/update_prompt.py +47 -8
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle.egg-info/PKG-INFO +11 -5
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_cli.py +5 -4
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_cli_surface.py +1 -1
- agent_toggle-0.11.0/tests/test_update_prompt.py +78 -0
- agent_toggle-0.10.0/tests/test_update_prompt.py +0 -41
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/LICENSE +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/__main__.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/backends/__init__.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/backends/flag_json.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/backends/mcp_json.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/backends/mcp_toml.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/backends/plugin_cli.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/companions.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/config.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/cost.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/doctor.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/fs.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/harnesses.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/i18n.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/locales/__init__.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/locales/zh_tw_common.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/locales/zh_tw_config.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/locales/zh_tw_picker.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/mechanisms.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/ops.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/output.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/profiles.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/settings.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/spinner.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/store.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/toml_check.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/ui/__init__.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/ui/config_menu.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/ui/logo.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/ui/menu.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/ui/model.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/ui/picker.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/ui/theme.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle/undo.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle.egg-info/SOURCES.txt +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle.egg-info/dependency_links.txt +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle.egg-info/entry_points.txt +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle.egg-info/requires.txt +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/agent_toggle.egg-info/top_level.txt +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/pyproject.toml +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/setup.cfg +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_alias.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_all_type.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_backends.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_color.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_companions.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_completions.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_config.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_config_menu.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_conformance.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_containment.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_cost.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_crash.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_doctor.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_encoding.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_fs.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_harnesses.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_help.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_i18n.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_install.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_logo.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_mechanisms.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_menu.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_mods.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_picker.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_platform.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_profiles.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_project.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_safety.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_same_name.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_secret_audit.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_settings.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_store.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_theme.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.0}/tests/test_undo.py +0 -0
- {agent_toggle-0.10.0 → agent_toggle-0.11.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.
|
|
3
|
+
Version: 0.11.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
|
|
@@ -195,6 +195,7 @@ agent-toggle <command> [args] # or: python3 agent_toggle.py <command> [
|
|
|
195
195
|
| `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`) |
|
|
196
196
|
| `list [type]` | what is currently disabled (`--project <dir>` filters to one project) |
|
|
197
197
|
| `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) |
|
|
198
|
+
| `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 |
|
|
198
199
|
| `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)) |
|
|
199
200
|
| `disable <type> <name>...` | park one or more items (`--dry-run` shows the plan; `--project <dir>` for a repo's own `.claude/` and `.mcp.json`) |
|
|
200
201
|
| `enable <type> <name>...` | put them back (`--dry-run` shows the plan; `--project <dir>` likewise) |
|
|
@@ -472,10 +473,15 @@ before the command and is offered after it, on every exit path; it overlaps the
|
|
|
472
473
|
|
|
473
474
|
- **On a terminal** (stdin and stderr are TTYs, no `--json`): a panel on stderr, "agent-toggle X
|
|
474
475
|
is available (you have Y)", with `Update now`, `Skip` and `Skip until next version`, a release
|
|
475
|
-
notes link, and `↑` `↓` `Enter` `q`. `Update now` runs
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
`
|
|
476
|
+
notes link, and `↑` `↓` `Enter` `q`. `Update now` runs, in order and stopping at the first
|
|
477
|
+
failure: `uv tool upgrade agent-toggle` (`pipx upgrade agent-toggle` for a pipx install), then,
|
|
478
|
+
when the Claude Code plugin is installed, `claude plugin marketplace update agent-toggle` and
|
|
479
|
+
`claude plugin update agent-toggle@agent-toggle`, then `agent-toggle install-shims`. A failure
|
|
480
|
+
gives a yellow warning listing the commands to run yourself.
|
|
481
|
+
- **Windows**: a running `agent-toggle.exe` cannot be replaced, so there is no `Update now`; the
|
|
482
|
+
commands are printed instead (as below), to run once agent-toggle has exited.
|
|
483
|
+
- **Off a terminal** (or `--json`): a stderr line, `agent-toggle X is available (you have Y)`,
|
|
484
|
+
then those commands, one per line.
|
|
479
485
|
- It never changes the exit code or stdout, so `--json` output stays one document.
|
|
480
486
|
|
|
481
487
|
It is one `GET https://pypi.org/pypi/agent-toggle/json` on a background thread with a 0.8 s
|
|
@@ -163,6 +163,7 @@ agent-toggle <command> [args] # or: python3 agent_toggle.py <command> [
|
|
|
163
163
|
| `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`) |
|
|
164
164
|
| `list [type]` | what is currently disabled (`--project <dir>` filters to one project) |
|
|
165
165
|
| `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) |
|
|
166
|
+
| `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 |
|
|
166
167
|
| `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)) |
|
|
167
168
|
| `disable <type> <name>...` | park one or more items (`--dry-run` shows the plan; `--project <dir>` for a repo's own `.claude/` and `.mcp.json`) |
|
|
168
169
|
| `enable <type> <name>...` | put them back (`--dry-run` shows the plan; `--project <dir>` likewise) |
|
|
@@ -440,10 +441,15 @@ before the command and is offered after it, on every exit path; it overlaps the
|
|
|
440
441
|
|
|
441
442
|
- **On a terminal** (stdin and stderr are TTYs, no `--json`): a panel on stderr, "agent-toggle X
|
|
442
443
|
is available (you have Y)", with `Update now`, `Skip` and `Skip until next version`, a release
|
|
443
|
-
notes link, and `↑` `↓` `Enter` `q`. `Update now` runs
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
`
|
|
444
|
+
notes link, and `↑` `↓` `Enter` `q`. `Update now` runs, in order and stopping at the first
|
|
445
|
+
failure: `uv tool upgrade agent-toggle` (`pipx upgrade agent-toggle` for a pipx install), then,
|
|
446
|
+
when the Claude Code plugin is installed, `claude plugin marketplace update agent-toggle` and
|
|
447
|
+
`claude plugin update agent-toggle@agent-toggle`, then `agent-toggle install-shims`. A failure
|
|
448
|
+
gives a yellow warning listing the commands to run yourself.
|
|
449
|
+
- **Windows**: a running `agent-toggle.exe` cannot be replaced, so there is no `Update now`; the
|
|
450
|
+
commands are printed instead (as below), to run once agent-toggle has exited.
|
|
451
|
+
- **Off a terminal** (or `--json`): a stderr line, `agent-toggle X is available (you have Y)`,
|
|
452
|
+
then those commands, one per line.
|
|
447
453
|
- It never changes the exit code or stdout, so `--json` output stays one document.
|
|
448
454
|
|
|
449
455
|
It is one `GET https://pypi.org/pypi/agent-toggle/json` on a background thread with a 0.8 s
|
|
@@ -558,7 +558,7 @@ def _stale_ignore_lines(h, args: argparse.Namespace, out: Result) -> None:
|
|
|
558
558
|
|
|
559
559
|
|
|
560
560
|
COMMANDS = ("ui", "pick", "status", "list", "cost", "migrate", "disable", "enable",
|
|
561
|
-
"install-shims", "profile", "undo", "doctor", "config")
|
|
561
|
+
"install-shims", "profile", "undo", "doctor", "config", "update")
|
|
562
562
|
_GLOBAL_FLAGS = ("--json", "-v", "--verbose")
|
|
563
563
|
|
|
564
564
|
|
|
@@ -693,6 +693,8 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
693
693
|
pp.add_argument("--project", metavar="dir", help=PROJECT_HELP)
|
|
694
694
|
sub.add_parser("doctor", parents=[common],
|
|
695
695
|
help="check harness layouts and state against disk; offers y/n fixes")
|
|
696
|
+
sub.add_parser("update", parents=[common],
|
|
697
|
+
help="check for a newer release; show the commands that update everything")
|
|
696
698
|
cf = sub.add_parser("config", parents=[common],
|
|
697
699
|
help="Telegram settings for the CI failure alerts (needs the telegram extra)")
|
|
698
700
|
cf.add_argument("action", nargs="?", choices=("test", "sync-ci"),
|
|
@@ -727,7 +729,8 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
727
729
|
language, color = "en", "auto"
|
|
728
730
|
i18n.set_language(language)
|
|
729
731
|
color = scan_color(argv, color) # --color > env > setting > auto
|
|
730
|
-
|
|
732
|
+
i = _command_index(argv)
|
|
733
|
+
started = None if argv[i:i + 1] == ["update"] else update_prompt.start() # `update` is the check
|
|
731
734
|
try:
|
|
732
735
|
return _main(argv, color)
|
|
733
736
|
except KeyboardInterrupt:
|
|
@@ -795,6 +798,8 @@ def _main(argv: list[str], color: str) -> int:
|
|
|
795
798
|
fixes = doctor.cmd_doctor(args.harness, out)
|
|
796
799
|
if fixes and _interactive(out):
|
|
797
800
|
doctor.offer_fixes(fixes, out)
|
|
801
|
+
elif cmd == "update":
|
|
802
|
+
update_prompt.cmd_update(out)
|
|
798
803
|
elif cmd == "config":
|
|
799
804
|
config.cmd_config(args, out)
|
|
800
805
|
elif cmd == "undo":
|
|
@@ -135,6 +135,14 @@ def commands(default_harness: str = "claude") -> dict[str, Cmd]:
|
|
|
135
135
|
("--dry-run", t("help.opt.config_dry_run",
|
|
136
136
|
"sync-ci: show the plan; set nothing"))],
|
|
137
137
|
examples=["config", "config test", "config sync-ci --dry-run"]),
|
|
138
|
+
"update": Cmd(
|
|
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"]),
|
|
138
146
|
"install-shims": Cmd(
|
|
139
147
|
"install-shims [--dry-run] [--gitignore | --no-gitignore]",
|
|
140
148
|
t("help.install_shims.summary", "write the skill shim into every installed harness"),
|
|
@@ -166,7 +174,7 @@ def commands(default_harness: str = "claude") -> dict[str, Cmd]:
|
|
|
166
174
|
def groups() -> list[tuple[str, list[str]]]:
|
|
167
175
|
return [(t("help.group.toggle", "Toggle"), ["ui", "disable", "enable", "undo", "profile"]),
|
|
168
176
|
(t("help.group.inspect", "Inspect"), ["status", "list", "cost", "doctor"]),
|
|
169
|
-
(t("help.group.setup", "Setup"), ["config", "install-shims", "migrate"])]
|
|
177
|
+
(t("help.group.setup", "Setup"), ["config", "install-shims", "update", "migrate"])]
|
|
170
178
|
|
|
171
179
|
|
|
172
180
|
def _table(rows: list[tuple[str, str]], color: bool, role: str = "choice") -> list[str]:
|
|
@@ -27,6 +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
33
|
"help.doctor.summary": "檢查 harness 目錄結構與狀態",
|
|
31
34
|
"help.doctor.desc": "比對狀態檔與磁碟;有問題時結束碼為 1。在終端機上,可自動修復的問題"
|
|
32
35
|
"(chmod、過期項目、孤立檔案、中斷的操作)會逐一詢問 y/n;否則不做任何變更。",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: agent-toggle
|
|
3
3
|
description: Temporarily disable or restore AI-agent resources — skills, agents, commands, rules, plugins, MCP servers — across Claude Code, Codex, Grok CLI, OpenCode, OpenClaw, Copilot and Vibe. Nothing is deleted; everything is parked and reversible. Use when the user wants to turn something off, trim startup context cost, re-enable something previously disabled, or asks "how do I disable X" / "turn off this MCP" / "add it back". Also when they ask to open, close or toggle the agent-toggle pane ("open agent-toggle" / "close the agent-toggle pane"), and to save, switch, rename or delete a profile ("save my setup as work" / "switch to my work profile").
|
|
4
|
-
argument-hint: "[status | list | cost | disable|enable <type> <name>... | undo | profile | doctor | open | close]"
|
|
4
|
+
argument-hint: "[status | list | cost | disable|enable <type> <name>... | undo | profile | doctor | update | open | close]"
|
|
5
5
|
---
|
|
6
6
|
<!-- agent-toggle shim: written by `agent-toggle install-shims`; re-running it overwrites this file -->
|
|
7
7
|
|
|
@@ -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), `list [type]`,
|
|
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]`,
|
|
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), `list [type]`, `cost [--type T]` (startup
|
|
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
|
|
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` /
|
|
@@ -143,8 +143,10 @@ def check(
|
|
|
143
143
|
timeout: float = DEFAULT_TIMEOUT,
|
|
144
144
|
now: float | None = None,
|
|
145
145
|
fetch: Callable[[str, float], str] | None = None,
|
|
146
|
+
ignore_skip: bool = False,
|
|
146
147
|
) -> UpdateAvailable | None:
|
|
147
|
-
"""Return an update when the latest release is newer than *current*, else None.
|
|
148
|
+
"""Return an update when the latest release is newer than *current*, else None.
|
|
149
|
+
*ignore_skip* reports a "skip this version" release too (an explicit ask)."""
|
|
148
150
|
current_parts = _numeric_tuple(current)
|
|
149
151
|
if current_parts is None:
|
|
150
152
|
return None
|
|
@@ -166,7 +168,7 @@ def check(
|
|
|
166
168
|
# offline costs one timeout per TTL, not one per command.
|
|
167
169
|
latest = fetched or latest
|
|
168
170
|
_save_cache(cache_path, stamp, latest)
|
|
169
|
-
if not isinstance(latest, str) or latest == _valid_version(cached.get("skipped")):
|
|
171
|
+
if not isinstance(latest, str) or (not ignore_skip and latest == _valid_version(cached.get("skipped"))):
|
|
170
172
|
return None
|
|
171
173
|
latest_parts = _numeric_tuple(latest)
|
|
172
174
|
if latest_parts is None or latest_parts <= current_parts:
|
|
@@ -18,12 +18,16 @@ from collections.abc import Iterable, Iterator
|
|
|
18
18
|
from pathlib import Path
|
|
19
19
|
|
|
20
20
|
from . import __version__, fs, settings, update_check
|
|
21
|
+
from .backends import plugin_cli
|
|
21
22
|
from .i18n import t
|
|
22
23
|
from .output import use_color
|
|
23
24
|
from .ui import theme
|
|
24
25
|
|
|
25
26
|
DIST = "agent-toggle"
|
|
26
|
-
|
|
27
|
+
PLUGIN = "agent-toggle@agent-toggle"
|
|
28
|
+
# shortcut: pipx is spotted by "pipx" in the venv path (its default homes); a custom PIPX_HOME gets uv
|
|
29
|
+
UPGRADE = (["pipx", "upgrade", DIST] if "pipx" in Path(sys.prefix).parts
|
|
30
|
+
else ["uv", "tool", "upgrade", DIST])
|
|
27
31
|
RELEASE_NOTES = "https://github.com/weskao/agent-toggle/releases/tag/v%s"
|
|
28
32
|
ANSWERS = (update_check.UPDATE_NOW, update_check.SKIP, update_check.SKIP_VERSION)
|
|
29
33
|
|
|
@@ -36,6 +40,16 @@ def _clean(text: str) -> str:
|
|
|
36
40
|
return _CONTROL.sub("", text)
|
|
37
41
|
|
|
38
42
|
|
|
43
|
+
def upgrade_steps() -> list[list[str]]:
|
|
44
|
+
"""Upgrade the CLI, then the Claude Code plugin when it is installed, then refresh the shims."""
|
|
45
|
+
steps = [UPGRADE]
|
|
46
|
+
# bare "claude": what a person pastes; offer()'s run() resolves it, as claude is often off PATH
|
|
47
|
+
if plugin_cli.claude_bin() and any(p["id"] == PLUGIN for p in plugin_cli.list_plugins(lambda _msg: None)):
|
|
48
|
+
steps += [["claude", "plugin", "marketplace", "update", DIST],
|
|
49
|
+
["claude", "plugin", "update", PLUGIN]]
|
|
50
|
+
return [*steps, [DIST, "install-shims"]]
|
|
51
|
+
|
|
52
|
+
|
|
39
53
|
def cache_path() -> Path:
|
|
40
54
|
return fs.state_dir() / "update-check.json"
|
|
41
55
|
|
|
@@ -64,9 +78,10 @@ def _available(found: update_check.UpdateAvailable) -> str:
|
|
|
64
78
|
|
|
65
79
|
|
|
66
80
|
def hint(found: update_check.UpdateAvailable) -> str:
|
|
67
|
-
"""The `ask` off a terminal (CI, pipes, --json):
|
|
81
|
+
"""The `ask` off a terminal (CI, pipes, --json): the headline, then one command per line."""
|
|
68
82
|
print(_available(found), file=sys.stderr)
|
|
69
|
-
|
|
83
|
+
for step in upgrade_steps():
|
|
84
|
+
print(" " + " ".join(step), file=sys.stderr)
|
|
70
85
|
return update_check.SKIP
|
|
71
86
|
|
|
72
87
|
|
|
@@ -182,20 +197,44 @@ def offer(started: update_check.Started | None, *, json_mode: bool = False,
|
|
|
182
197
|
stderr only and never raises, so neither stdout nor the exit code can change."""
|
|
183
198
|
if started is None:
|
|
184
199
|
return None
|
|
185
|
-
|
|
200
|
+
# Windows cannot replace the agent-toggle.exe that is running: print the commands, run none
|
|
201
|
+
if is_interactive() and not json_mode and not fs.WIN:
|
|
186
202
|
def asker(found):
|
|
187
203
|
return ask(found, color_mode)
|
|
188
204
|
else:
|
|
189
205
|
asker = hint
|
|
190
|
-
def run(cmd, **kw): #
|
|
191
|
-
|
|
192
|
-
|
|
206
|
+
def run(cmd, **kw): # their output must never reach stdout; no step may prompt
|
|
207
|
+
if cmd[0] == "claude":
|
|
208
|
+
cmd = [plugin_cli.claude_bin() or "claude", *cmd[1:]]
|
|
209
|
+
return subprocess.run(cmd, stdout=sys.stderr, stdin=subprocess.DEVNULL, **kw)
|
|
210
|
+
steps: list[list[str]] = []
|
|
211
|
+
def upgrade(_found):
|
|
212
|
+
steps.extend(upgrade_steps())
|
|
213
|
+
return steps
|
|
214
|
+
answer = update_check.offer(started, asker, cache_path=cache_path(), upgrade=upgrade, run=run)
|
|
193
215
|
if answer == update_check.UPGRADE_FAILED:
|
|
194
216
|
try:
|
|
195
217
|
mark = theme.ansi("warning", "⚠" if theme.get_glyphs(sys.stderr).unicode else "!",
|
|
196
218
|
use_color(sys.stderr, color_mode))
|
|
197
219
|
print(mark + " " + t("update.failed", "upgrade did not finish — run it yourself: %s",
|
|
198
|
-
" ".join
|
|
220
|
+
"\n ".join(map(" ".join, steps or [UPGRADE]))), file=sys.stderr)
|
|
199
221
|
except Exception: # noqa: BLE001, S110 - a hint must never fail the command
|
|
200
222
|
pass
|
|
201
223
|
return answer
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def cmd_update(out) -> None:
|
|
227
|
+
"""`update`: check PyPI now (cache and "skipped" ignored), show what would be updated and
|
|
228
|
+
the commands for it. Runs none of them: the picker's Update now does that."""
|
|
229
|
+
found = update_check.check(DIST, __version__, cache_path=cache_path(), ttl_seconds=0,
|
|
230
|
+
timeout=5.0, ignore_skip=True)
|
|
231
|
+
if found is None:
|
|
232
|
+
out.row(None, "cli", DIST, "update", "ok", f"no newer release found (you have {__version__})",
|
|
233
|
+
current=__version__, latest=None, commands=[])
|
|
234
|
+
return
|
|
235
|
+
steps = [" ".join(s) for s in upgrade_steps()]
|
|
236
|
+
out.row(None, "cli", DIST, "update", "ok", f"{found.current} -> {found.latest}", show=False,
|
|
237
|
+
current=found.current, latest=found.latest, commands=steps)
|
|
238
|
+
out.say(_available(found) + ". Run, in order:")
|
|
239
|
+
for step in steps:
|
|
240
|
+
out.say(" " + step)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-toggle
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.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
|
|
@@ -195,6 +195,7 @@ agent-toggle <command> [args] # or: python3 agent_toggle.py <command> [
|
|
|
195
195
|
| `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`) |
|
|
196
196
|
| `list [type]` | what is currently disabled (`--project <dir>` filters to one project) |
|
|
197
197
|
| `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) |
|
|
198
|
+
| `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 |
|
|
198
199
|
| `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)) |
|
|
199
200
|
| `disable <type> <name>...` | park one or more items (`--dry-run` shows the plan; `--project <dir>` for a repo's own `.claude/` and `.mcp.json`) |
|
|
200
201
|
| `enable <type> <name>...` | put them back (`--dry-run` shows the plan; `--project <dir>` likewise) |
|
|
@@ -472,10 +473,15 @@ before the command and is offered after it, on every exit path; it overlaps the
|
|
|
472
473
|
|
|
473
474
|
- **On a terminal** (stdin and stderr are TTYs, no `--json`): a panel on stderr, "agent-toggle X
|
|
474
475
|
is available (you have Y)", with `Update now`, `Skip` and `Skip until next version`, a release
|
|
475
|
-
notes link, and `↑` `↓` `Enter` `q`. `Update now` runs
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
`
|
|
476
|
+
notes link, and `↑` `↓` `Enter` `q`. `Update now` runs, in order and stopping at the first
|
|
477
|
+
failure: `uv tool upgrade agent-toggle` (`pipx upgrade agent-toggle` for a pipx install), then,
|
|
478
|
+
when the Claude Code plugin is installed, `claude plugin marketplace update agent-toggle` and
|
|
479
|
+
`claude plugin update agent-toggle@agent-toggle`, then `agent-toggle install-shims`. A failure
|
|
480
|
+
gives a yellow warning listing the commands to run yourself.
|
|
481
|
+
- **Windows**: a running `agent-toggle.exe` cannot be replaced, so there is no `Update now`; the
|
|
482
|
+
commands are printed instead (as below), to run once agent-toggle has exited.
|
|
483
|
+
- **Off a terminal** (or `--json`): a stderr line, `agent-toggle X is available (you have Y)`,
|
|
484
|
+
then those commands, one per line.
|
|
479
485
|
- It never changes the exit code or stdout, so `--json` output stays one document.
|
|
480
486
|
|
|
481
487
|
It is one `GET https://pypi.org/pypi/agent-toggle/json` on a background thread with a 0.8 s
|
|
@@ -181,7 +181,8 @@ class UpdateCheckTest(CliCase):
|
|
|
181
181
|
def test_hint_on_stderr_off_a_tty(self) -> None:
|
|
182
182
|
rc, out, err = self.run_cli("status")
|
|
183
183
|
self.assertEqual(rc, 0)
|
|
184
|
-
self.assertEqual(err.splitlines()[-
|
|
184
|
+
self.assertEqual(err.splitlines()[-3:], [HINT, " uv tool upgrade agent-toggle",
|
|
185
|
+
" agent-toggle install-shims"])
|
|
185
186
|
self.assertNotIn("99.0.0", out)
|
|
186
187
|
|
|
187
188
|
def test_opt_out_by_env_and_by_setting(self) -> None:
|
|
@@ -233,10 +234,10 @@ class UpdateCheckTest(CliCase):
|
|
|
233
234
|
rc, out, err = self.run_cli("--version") # runs no other subprocess
|
|
234
235
|
self.assertEqual((rc, out.strip()), (0, f"agent-toggle {__version__}"))
|
|
235
236
|
run.assert_called_once_with(["uv", "tool", "upgrade", "agent-toggle"], check=False,
|
|
236
|
-
stdout=mock.ANY)
|
|
237
|
+
stdout=mock.ANY, stdin=subprocess.DEVNULL)
|
|
237
238
|
self.assertIn("did not finish", run.call_args.kwargs["stdout"].getvalue()) # = stderr
|
|
238
|
-
self.assertIn("upgrade did not finish — run it yourself: uv tool upgrade agent-toggle"
|
|
239
|
-
err)
|
|
239
|
+
self.assertIn("upgrade did not finish — run it yourself: uv tool upgrade agent-toggle"
|
|
240
|
+
"\n agent-toggle install-shims", err)
|
|
240
241
|
|
|
241
242
|
def test_tty_skip_version_and_ctrl_c(self) -> None:
|
|
242
243
|
update_prompt.is_interactive.return_value = True
|
|
@@ -250,7 +250,7 @@ class SpellingTest(CliCase):
|
|
|
250
250
|
["profile", "list"], ["profile", "diff", "work"],
|
|
251
251
|
["profile", "apply", "work", "--dry-run"],
|
|
252
252
|
["undo", "--dry-run"], ["enable", "--all", "--dry-run"],
|
|
253
|
-
["doctor"], ["doctor", "--harness", "claude"],
|
|
253
|
+
["doctor"], ["doctor", "--harness", "claude"], ["update"],
|
|
254
254
|
["disable", "skill", "demo-skill", "--project", str(proj), "--dry-run"],
|
|
255
255
|
)
|
|
256
256
|
for argv in cases:
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""The update prompt flushes typeahead around cbreak mode."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import io
|
|
5
|
+
import sys
|
|
6
|
+
import unittest
|
|
7
|
+
from unittest import mock
|
|
8
|
+
|
|
9
|
+
from agent_toggle import update_check, update_prompt
|
|
10
|
+
from agent_toggle.ui import theme
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ReleaseNotesTest(unittest.TestCase):
|
|
14
|
+
def test_a_v_prefixed_version_is_not_doubled_in_the_url(self) -> None:
|
|
15
|
+
for latest in ("10.0", "v10.0"):
|
|
16
|
+
found = update_check.UpdateAvailable(latest=latest, current="1.0")
|
|
17
|
+
text = "\n".join(update_prompt._lines(found, 1, False, theme.ASCII))
|
|
18
|
+
self.assertIn("/releases/tag/v10.0", text, latest)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class UpgradeStepsTest(unittest.TestCase):
|
|
22
|
+
def steps(self, claude, plugins):
|
|
23
|
+
with mock.patch.object(update_prompt.plugin_cli, "claude_bin", return_value=claude), \
|
|
24
|
+
mock.patch.object(update_prompt.plugin_cli, "list_plugins",
|
|
25
|
+
return_value=[{"id": i} for i in plugins]):
|
|
26
|
+
return update_prompt.upgrade_steps()
|
|
27
|
+
|
|
28
|
+
def test_the_plugin_is_updated_only_when_it_is_installed(self) -> None:
|
|
29
|
+
cli, shims = update_prompt.UPGRADE, ["agent-toggle", "install-shims"]
|
|
30
|
+
self.assertEqual(self.steps("claude", ["agent-toggle@agent-toggle"]), [
|
|
31
|
+
cli, ["claude", "plugin", "marketplace", "update", "agent-toggle"],
|
|
32
|
+
["claude", "plugin", "update", "agent-toggle@agent-toggle"], shims])
|
|
33
|
+
self.assertEqual(self.steps("claude", ["other@x"]), [cli, shims])
|
|
34
|
+
self.assertEqual(self.steps(None, []), [cli, shims])
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class CmdUpdateTest(unittest.TestCase):
|
|
38
|
+
def run_update(self, latest):
|
|
39
|
+
out = mock.Mock()
|
|
40
|
+
found = update_check.UpdateAvailable("0.1", latest) if latest else None
|
|
41
|
+
with mock.patch.object(update_check, "check", return_value=found) as check, \
|
|
42
|
+
mock.patch.object(update_prompt, "upgrade_steps", return_value=[["a", "b"], ["c"]]), \
|
|
43
|
+
mock.patch.object(update_prompt, "cache_path", return_value="x"):
|
|
44
|
+
update_prompt.cmd_update(out)
|
|
45
|
+
return out, check
|
|
46
|
+
|
|
47
|
+
def test_a_newer_release_lists_every_command_and_ignores_cache_and_skip(self) -> None:
|
|
48
|
+
out, check = self.run_update("0.2")
|
|
49
|
+
row = out.row.call_args.kwargs
|
|
50
|
+
self.assertEqual((row["latest"], row["commands"]), ("0.2", ["a b", "c"]))
|
|
51
|
+
self.assertEqual((check.call_args.kwargs["ttl_seconds"], check.call_args.kwargs["ignore_skip"]), (0, True))
|
|
52
|
+
|
|
53
|
+
def test_nothing_newer_lists_no_command(self) -> None:
|
|
54
|
+
out, _ = self.run_update(None)
|
|
55
|
+
self.assertEqual(out.row.call_args.kwargs["commands"], [])
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class FlushTest(unittest.TestCase):
|
|
59
|
+
def test_input_is_flushed_after_entering_and_after_leaving_cbreak(self) -> None:
|
|
60
|
+
termios = mock.MagicMock()
|
|
61
|
+
calls: list[str] = []
|
|
62
|
+
termios.tcgetattr.return_value = []
|
|
63
|
+
termios.tcflush.side_effect = lambda *_a: calls.append("flush")
|
|
64
|
+
termios.tcsetattr.side_effect = lambda *_a: calls.append("restore")
|
|
65
|
+
tty = mock.MagicMock()
|
|
66
|
+
tty.setcbreak.side_effect = lambda *_a: calls.append("cbreak")
|
|
67
|
+
stdin = mock.MagicMock()
|
|
68
|
+
stdin.fileno.return_value = 0
|
|
69
|
+
found = update_check.UpdateAvailable(current="1.0", latest="2.0")
|
|
70
|
+
with (mock.patch.dict(sys.modules, {"termios": termios, "tty": tty}),
|
|
71
|
+
mock.patch.object(sys, "stdin", stdin), mock.patch.object(sys, "stderr", io.StringIO()),
|
|
72
|
+
mock.patch.object(update_prompt, "_posix_keys", lambda _fd: iter(["quit"]))):
|
|
73
|
+
self.assertEqual(update_prompt.ask(found), update_check.SKIP)
|
|
74
|
+
self.assertEqual(calls, ["cbreak", "flush", "restore", "flush"])
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
if __name__ == "__main__":
|
|
78
|
+
unittest.main()
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
"""The update prompt flushes typeahead around cbreak mode."""
|
|
2
|
-
from __future__ import annotations
|
|
3
|
-
|
|
4
|
-
import io
|
|
5
|
-
import sys
|
|
6
|
-
import unittest
|
|
7
|
-
from unittest import mock
|
|
8
|
-
|
|
9
|
-
from agent_toggle import update_check, update_prompt
|
|
10
|
-
from agent_toggle.ui import theme
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class ReleaseNotesTest(unittest.TestCase):
|
|
14
|
-
def test_a_v_prefixed_version_is_not_doubled_in_the_url(self) -> None:
|
|
15
|
-
for latest in ("10.0", "v10.0"):
|
|
16
|
-
found = update_check.UpdateAvailable(latest=latest, current="1.0")
|
|
17
|
-
text = "\n".join(update_prompt._lines(found, 1, False, theme.ASCII))
|
|
18
|
-
self.assertIn("/releases/tag/v10.0", text, latest)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class FlushTest(unittest.TestCase):
|
|
22
|
-
def test_input_is_flushed_after_entering_and_after_leaving_cbreak(self) -> None:
|
|
23
|
-
termios = mock.MagicMock()
|
|
24
|
-
calls: list[str] = []
|
|
25
|
-
termios.tcgetattr.return_value = []
|
|
26
|
-
termios.tcflush.side_effect = lambda *_a: calls.append("flush")
|
|
27
|
-
termios.tcsetattr.side_effect = lambda *_a: calls.append("restore")
|
|
28
|
-
tty = mock.MagicMock()
|
|
29
|
-
tty.setcbreak.side_effect = lambda *_a: calls.append("cbreak")
|
|
30
|
-
stdin = mock.MagicMock()
|
|
31
|
-
stdin.fileno.return_value = 0
|
|
32
|
-
found = update_check.UpdateAvailable(current="1.0", latest="2.0")
|
|
33
|
-
with (mock.patch.dict(sys.modules, {"termios": termios, "tty": tty}),
|
|
34
|
-
mock.patch.object(sys, "stdin", stdin), mock.patch.object(sys, "stderr", io.StringIO()),
|
|
35
|
-
mock.patch.object(update_prompt, "_posix_keys", lambda _fd: iter(["quit"]))):
|
|
36
|
-
self.assertEqual(update_prompt.ask(found), update_check.SKIP)
|
|
37
|
-
self.assertEqual(calls, ["cbreak", "flush", "restore", "flush"])
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if __name__ == "__main__":
|
|
41
|
-
unittest.main()
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|