agent-toggle 0.13.1__tar.gz → 0.14.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 (91) hide show
  1. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/PKG-INFO +42 -25
  2. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/README.md +41 -24
  3. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/__init__.py +1 -1
  4. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/locales/zh_tw_picker.py +12 -7
  5. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/ui/menu.py +17 -1
  6. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/ui/picker.py +240 -54
  7. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/ui/theme.py +4 -3
  8. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle.egg-info/PKG-INFO +42 -25
  9. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_menu.py +9 -0
  10. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_mods.py +28 -17
  11. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_picker.py +294 -76
  12. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_same_name.py +2 -2
  13. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_theme.py +2 -2
  14. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/LICENSE +0 -0
  15. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/__main__.py +0 -0
  16. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/backends/__init__.py +0 -0
  17. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/backends/flag_json.py +0 -0
  18. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/backends/mcp_json.py +0 -0
  19. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/backends/mcp_toml.py +0 -0
  20. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/backends/plugin_cli.py +0 -0
  21. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/cli.py +0 -0
  22. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/companions.py +0 -0
  23. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/completions.py +0 -0
  24. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/config.py +0 -0
  25. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/cost.py +0 -0
  26. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/doctor.py +0 -0
  27. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/fs.py +0 -0
  28. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/harnesses.py +0 -0
  29. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/help.py +0 -0
  30. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/i18n.py +0 -0
  31. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/locales/__init__.py +0 -0
  32. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/locales/zh_tw_cli.py +0 -0
  33. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/locales/zh_tw_common.py +0 -0
  34. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/locales/zh_tw_config.py +0 -0
  35. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/mechanisms.py +0 -0
  36. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/ops.py +0 -0
  37. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/output.py +0 -0
  38. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/profiles.py +0 -0
  39. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/settings.py +0 -0
  40. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/shims/claude.md.tmpl +0 -0
  41. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/shims/generic.md.tmpl +0 -0
  42. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/spinner.py +0 -0
  43. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/store.py +0 -0
  44. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/toml_check.py +0 -0
  45. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/ui/__init__.py +0 -0
  46. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/ui/config_menu.py +0 -0
  47. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/ui/logo.py +0 -0
  48. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/ui/model.py +0 -0
  49. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/undo.py +0 -0
  50. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/update_check.py +0 -0
  51. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle/update_prompt.py +0 -0
  52. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle.egg-info/SOURCES.txt +0 -0
  53. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle.egg-info/dependency_links.txt +0 -0
  54. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle.egg-info/entry_points.txt +0 -0
  55. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle.egg-info/requires.txt +0 -0
  56. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/agent_toggle.egg-info/top_level.txt +0 -0
  57. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/pyproject.toml +0 -0
  58. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/setup.cfg +0 -0
  59. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_alias.py +0 -0
  60. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_all_type.py +0 -0
  61. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_backends.py +0 -0
  62. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_cli.py +0 -0
  63. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_cli_surface.py +0 -0
  64. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_color.py +0 -0
  65. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_companions.py +0 -0
  66. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_completions.py +0 -0
  67. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_config.py +0 -0
  68. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_config_menu.py +0 -0
  69. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_conformance.py +0 -0
  70. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_containment.py +0 -0
  71. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_cost.py +0 -0
  72. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_crash.py +0 -0
  73. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_doctor.py +0 -0
  74. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_encoding.py +0 -0
  75. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_fs.py +0 -0
  76. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_harnesses.py +0 -0
  77. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_help.py +0 -0
  78. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_i18n.py +0 -0
  79. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_install.py +0 -0
  80. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_logo.py +0 -0
  81. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_mechanisms.py +0 -0
  82. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_platform.py +0 -0
  83. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_profiles.py +0 -0
  84. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_project.py +0 -0
  85. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_safety.py +0 -0
  86. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_secret_audit.py +0 -0
  87. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_settings.py +0 -0
  88. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_store.py +0 -0
  89. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_undo.py +0 -0
  90. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_update_check.py +0 -0
  91. {agent_toggle-0.13.1 → agent_toggle-0.14.0}/tests/test_update_prompt.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-toggle
3
- Version: 0.13.1
3
+ Version: 0.14.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
@@ -316,7 +316,7 @@ agent-toggle # same, on a terminal; elsewhere prints help and exits
316
316
  ● delta codex 10 ▏ │ cost 0 now; ~300 tok if restored
317
317
  │ since 2026-09-19 10:00:00
318
318
  5 of 5 shown · 2/5
319
- Space toggle Enter apply Esc cancel ? help / search ←→ harness t type s sort p profile a all
319
+ Tab toggle Enter apply Esc cancel ? help / search ←→ harness t type s sort p profile a all
320
320
  ```
321
321
 
322
322
  What is on screen:
@@ -344,38 +344,54 @@ What is on screen:
344
344
 
345
345
  | key | action |
346
346
  |---|---|
347
- | `Space` | toggle the highlighted row (live / parked) and stay on it (while searching, the first `Space` separates search terms and a second one toggles) |
348
- | `Tab` | toggle the highlighted row and advance to the next |
347
+ | `Tab` | toggle the highlighted row (live / parked) and stay on it, including while searching |
348
+ | `Space` | enter a space in the search (separates search terms) |
349
349
  | `Enter` | apply every staged change (with `--dry-run`: show the plan) |
350
- | `Esc` / `Ctrl-C` | cancel; nothing is applied |
350
+ | `Esc` | leave; if changes are staged, review them in a centered Save changes? dialog |
351
+ | `Ctrl-C` | discard staged changes and leave |
351
352
  | `↑` `↓` `PgUp` `PgDn` `Home` `End`, `Ctrl-P` / `Ctrl-N` | move |
352
- | `/` | start a search; any other non-command letter starts one too. Terms are ANDed, case-insensitive, and match the harness/type/name, the group (`mod` or `mods` finds every mod) and the path; when nothing matches, letters-in-order (fuzzy) is tried: `ctxmd` finds `context-md`, and the bar says `≈ fuzzy match` |
353
+ | `/` | start a search; any letter or digit starts one too. Terms are ANDed, case-insensitive, and match the harness/type/name, the group (`mod` or `mods` finds every mod) and the path; when nothing matches, letters-in-order (fuzzy) is tried: `ctxmd` finds `context-md`, and the bar says `≈ fuzzy match` |
353
354
  | `Backspace` / `Ctrl-U` | delete one character / clear the filter |
354
- | `←` `→` | switch harness tab (also while typing a filter); `0` = All, `1`-`9` = that tab, `h` = next tab |
355
- | `t` | cycle the type filter |
356
- | `s` | cycle sort: name, cost (biggest first); applies within each type group |
357
- | `p` | profiles: stage a saved one, or save the live state (see below) |
355
+ | `←` `→` | switch harness tab (also while typing a filter) |
356
+ | `Ctrl-T` | cycle the type filter |
357
+ | `Ctrl-S` | cycle sort: name, cost (biggest first); applies within each type group |
358
+ | `Ctrl-O` | profiles: stage a saved one, or save the live state (see below) |
358
359
  | `?` | show the key list |
359
- | `a` / `Ctrl-A` | toggle every visible row |
360
- | `n` | toggle every row on this row's harness with the same name, of any type (skill, command, MCP, ...), shown or filtered out; like `disable all <name>` |
360
+ | `Ctrl-A` | toggle every visible row |
361
+ | `Ctrl-E` | toggle every row on this row's harness with the same name, of any type (skill, command, MCP, ...), shown or filtered out; like `disable all <name>` |
361
362
 
362
- The command keys (`t`, `s`, `p`, `h`, `a`, `n`, `0`-`9`, `?`) act only while no filter is active.
363
- Press `/` first to type a filter that begins with one of them; once the filter is non-empty,
364
- every letter just types.
363
+ Commands are `Ctrl` chords, so every letter and digit types into the search, and the chords
364
+ work while a filter is active too. `?` and `/` act only while no filter is active. The picker
365
+ reads keys in raw mode, so `Ctrl-S`, `Ctrl-T` and `Ctrl-O` reach it instead of the terminal
366
+ (flow control, `SIGINFO`, discard).
365
367
 
366
368
  The start view (sort, harness tab, type filter) comes from the `picker_sort`, `picker_harness`
367
369
  and `picker_type` [settings](#settings--config-menu). A profile skips harnesses that are hidden
368
370
  and counts those items as `hidden by settings`; `ui --harness <name>` shows that harness even
369
371
  when it is switched off.
370
372
 
371
- `p` opens a prompt over the list of saved profiles: type a number or name (or
373
+ `Ctrl-O` opens a prompt over the list of saved profiles: type a number or name (or
372
374
  `apply <name>`) and Enter to **stage** that profile's changes (only the items it
373
375
  mentions; ones this machine lacks are skipped), then Enter in the picker applies
374
376
  them like any other change, so `ui --dry-run` previews a profile and a stray
375
- `p` changes nothing. `save <name>` writes the live state (not your staged changes) as
377
+ `Ctrl-O` changes nothing. `save <name>` writes the live state (not your staged changes) as
376
378
  a profile, with the same name rules and project scope as `profile save`; it is
377
379
  off under `--dry-run`. Esc closes the prompt.
378
380
 
381
+ The exit dialog lists all staged changes, including rows hidden by a filter. Choose
382
+ **Yes** to apply and leave, or **No** to discard and leave. Use Left/Right
383
+ to select, Enter to confirm, Y/N shortcuts, or click either button (hovering selects it and underlines it). Esc in the
384
+ dialog returns to editing. Up/Down, PgUp/PgDn, Home/End and the mouse wheel scroll
385
+ a long change list. Its height is capped at 80% of the terminal (up to 24 lines,
386
+ with a compact layout on tiny terminals); the confirmation buttons stay visible.
387
+ Up/Down and the mouse wheel move the selected row, just like the main picker;
388
+ the list scrolls when the selection reaches its edge. Tab or a click toggles the
389
+ row's staged enabled/disabled state. The picker stays visible behind the opaque
390
+ dialog; both are painted together in one refresh to prevent background flashes.
391
+ Reverted rows stay in the review so they can be toggled again; Yes applies only
392
+ the remaining changes. The dialog follows the selected language and color mode;
393
+ with `--dry-run`, Yes only shows the plan.
394
+
379
395
  Nothing happens while the picker is open. Changes are staged, the screen is
380
396
  torn down, and only then do the real operations run, so their output (which
381
397
  companion files moved, which were kept because they are shared) is readable
@@ -390,7 +406,8 @@ numbers (`1 3 5-7`) to tick or untick, `n <number>` to toggle every type with th
390
406
  row's name, `/text` to filter (`/` alone clears it),
391
407
  `s` / `h` / `t` to sort and cycle the harness and type filters, `p` to list profiles,
392
408
  `p <number|name>` to stage one and `p save <name>` to save the live state, `a` to
393
- apply, `q` (or end of input) to cancel.
409
+ apply, `q` to review staged changes with a `y/n` save prompt (no changes: exit
410
+ immediately). End of input discards changes.
394
411
 
395
412
  ### Mods
396
413
 
@@ -408,7 +425,7 @@ plugin: it ships skills, commands, agents, MCP servers or hooks and draws nothin
408
425
  grep -l modules ~/.claude/plugins/cache/*/*/*/hooks/hooks.json
409
426
  ```
410
427
 
411
- - The picker lists mods under **Mods**, right after Plugins. `t` cycles to it, the
428
+ - The picker lists mods under **Mods**, right after Plugins. `Ctrl-T` cycles to it, the
412
429
  `picker_type` setting can open on it, and a search for `mod` or `mods` finds every mod.
413
430
  The detail pane shows its type as `mod (plugin)`.
414
431
  - A mod is still a plugin everywhere else: it toggles through `claude plugin
@@ -989,20 +1006,20 @@ claude --plugin-dir ./mod # then run /agent-toggle inside Claude
989
1006
  and the first and last list lines count the rows out of view (`↑ 4`, `↓ 12`).
990
1007
  - The mouse wheel over the list moves the cursor, as in the terminal picker; no click needed.
991
1008
  - Click the list once so it takes keys (Esc hands the keyboard back to the prompt). Then `↑` `↓`
992
- `PgUp` `PgDn` `Home` `End` (also `Ctrl-P` / `Ctrl-N`) move; `Space` or `Enter` toggles the row,
993
- `Tab` toggles and moves on; `/` or any other letter starts a search (terms ANDed over
994
- `type/name` and the group, letters-in-order if nothing matches; a second `Space` toggles);
995
- `s` sorts by name or cost; `n` toggles every type with this row's name (skill, command, MCP,
1009
+ `PgUp` `PgDn` `Home` `End` (also `Ctrl-P` / `Ctrl-N`) move; `Tab` toggles the row
1010
+ and stays on it, including while searching; `/` or any letter starts a search (terms ANDed over
1011
+ `type/name` and the group, letters-in-order if nothing matches; `Space` separates terms);
1012
+ `Ctrl-S` sorts by name or cost; `Ctrl-E` toggles every type with this row's name (skill, command, MCP,
996
1013
  ...), like `disable all <name>`; `Ctrl-U` clears the search. A click moves the cursor.
997
1014
  - A toggle applies at once (no staging, no Enter to apply); the toast names the target and the state
998
1015
  with an emoji and a bracketed tag (no colour codes, so nothing garbles; on the Linux console and the
999
1016
  legacy Windows console, where emoji draw as boxes, the tag alone): `✅ [ENABLED] skill: foo`, `⛔ [DISABLED] mod: bar (/reload-plugins)`,
1000
- `⛔ [DISABLED] rule: baz (takes effect in a new session)`, `⛔ [DISABLED] all: foo` after `n`, or `❌ [FAILED] disable skill: foo - <error>`.
1017
+ `⛔ [DISABLED] rule: baz (takes effect in a new session)`, `⛔ [DISABLED] all: foo` after `Ctrl-E`, or `❌ [FAILED] disable skill: foo - <error>`.
1001
1018
  The row flips the moment you press (and flips back
1002
1019
  if the CLI fails); the list reloads in the background. While a toggle runs, further keys are ignored.
1003
1020
  - Opening the pane shows the same braille spinner and `Loading resources…` as `agent-toggle ui`
1004
1021
  until the list arrives, and a spinner beside `working…` while a toggle runs.
1005
- - Not in the pane: harness tabs (claude only), and the `t` `p` `a` keys. Where Claude draws no
1022
+ - Not in the pane: harness tabs (claude only), and the `Ctrl-T` `Ctrl-O` `Ctrl-A` keys. Where Claude draws no
1006
1023
  such pane (the mobile app, VS Code) it is a plain list of buttons instead.
1007
1024
  - `/agent-toggle <command> ...` with `disable`, `enable`,
1008
1025
  `undo`, `list`, `status`, `cost` or `doctor` runs the CLI instead of opening the pane and prints
@@ -283,7 +283,7 @@ agent-toggle # same, on a terminal; elsewhere prints help and exits
283
283
  ● delta codex 10 ▏ │ cost 0 now; ~300 tok if restored
284
284
  │ since 2026-09-19 10:00:00
285
285
  5 of 5 shown · 2/5
286
- Space toggle Enter apply Esc cancel ? help / search ←→ harness t type s sort p profile a all
286
+ Tab toggle Enter apply Esc cancel ? help / search ←→ harness t type s sort p profile a all
287
287
  ```
288
288
 
289
289
  What is on screen:
@@ -311,38 +311,54 @@ What is on screen:
311
311
 
312
312
  | key | action |
313
313
  |---|---|
314
- | `Space` | toggle the highlighted row (live / parked) and stay on it (while searching, the first `Space` separates search terms and a second one toggles) |
315
- | `Tab` | toggle the highlighted row and advance to the next |
314
+ | `Tab` | toggle the highlighted row (live / parked) and stay on it, including while searching |
315
+ | `Space` | enter a space in the search (separates search terms) |
316
316
  | `Enter` | apply every staged change (with `--dry-run`: show the plan) |
317
- | `Esc` / `Ctrl-C` | cancel; nothing is applied |
317
+ | `Esc` | leave; if changes are staged, review them in a centered Save changes? dialog |
318
+ | `Ctrl-C` | discard staged changes and leave |
318
319
  | `↑` `↓` `PgUp` `PgDn` `Home` `End`, `Ctrl-P` / `Ctrl-N` | move |
319
- | `/` | start a search; any other non-command letter starts one too. Terms are ANDed, case-insensitive, and match the harness/type/name, the group (`mod` or `mods` finds every mod) and the path; when nothing matches, letters-in-order (fuzzy) is tried: `ctxmd` finds `context-md`, and the bar says `≈ fuzzy match` |
320
+ | `/` | start a search; any letter or digit starts one too. Terms are ANDed, case-insensitive, and match the harness/type/name, the group (`mod` or `mods` finds every mod) and the path; when nothing matches, letters-in-order (fuzzy) is tried: `ctxmd` finds `context-md`, and the bar says `≈ fuzzy match` |
320
321
  | `Backspace` / `Ctrl-U` | delete one character / clear the filter |
321
- | `←` `→` | switch harness tab (also while typing a filter); `0` = All, `1`-`9` = that tab, `h` = next tab |
322
- | `t` | cycle the type filter |
323
- | `s` | cycle sort: name, cost (biggest first); applies within each type group |
324
- | `p` | profiles: stage a saved one, or save the live state (see below) |
322
+ | `←` `→` | switch harness tab (also while typing a filter) |
323
+ | `Ctrl-T` | cycle the type filter |
324
+ | `Ctrl-S` | cycle sort: name, cost (biggest first); applies within each type group |
325
+ | `Ctrl-O` | profiles: stage a saved one, or save the live state (see below) |
325
326
  | `?` | show the key list |
326
- | `a` / `Ctrl-A` | toggle every visible row |
327
- | `n` | toggle every row on this row's harness with the same name, of any type (skill, command, MCP, ...), shown or filtered out; like `disable all <name>` |
327
+ | `Ctrl-A` | toggle every visible row |
328
+ | `Ctrl-E` | toggle every row on this row's harness with the same name, of any type (skill, command, MCP, ...), shown or filtered out; like `disable all <name>` |
328
329
 
329
- The command keys (`t`, `s`, `p`, `h`, `a`, `n`, `0`-`9`, `?`) act only while no filter is active.
330
- Press `/` first to type a filter that begins with one of them; once the filter is non-empty,
331
- every letter just types.
330
+ Commands are `Ctrl` chords, so every letter and digit types into the search, and the chords
331
+ work while a filter is active too. `?` and `/` act only while no filter is active. The picker
332
+ reads keys in raw mode, so `Ctrl-S`, `Ctrl-T` and `Ctrl-O` reach it instead of the terminal
333
+ (flow control, `SIGINFO`, discard).
332
334
 
333
335
  The start view (sort, harness tab, type filter) comes from the `picker_sort`, `picker_harness`
334
336
  and `picker_type` [settings](#settings--config-menu). A profile skips harnesses that are hidden
335
337
  and counts those items as `hidden by settings`; `ui --harness <name>` shows that harness even
336
338
  when it is switched off.
337
339
 
338
- `p` opens a prompt over the list of saved profiles: type a number or name (or
340
+ `Ctrl-O` opens a prompt over the list of saved profiles: type a number or name (or
339
341
  `apply <name>`) and Enter to **stage** that profile's changes (only the items it
340
342
  mentions; ones this machine lacks are skipped), then Enter in the picker applies
341
343
  them like any other change, so `ui --dry-run` previews a profile and a stray
342
- `p` changes nothing. `save <name>` writes the live state (not your staged changes) as
344
+ `Ctrl-O` changes nothing. `save <name>` writes the live state (not your staged changes) as
343
345
  a profile, with the same name rules and project scope as `profile save`; it is
344
346
  off under `--dry-run`. Esc closes the prompt.
345
347
 
348
+ The exit dialog lists all staged changes, including rows hidden by a filter. Choose
349
+ **Yes** to apply and leave, or **No** to discard and leave. Use Left/Right
350
+ to select, Enter to confirm, Y/N shortcuts, or click either button (hovering selects it and underlines it). Esc in the
351
+ dialog returns to editing. Up/Down, PgUp/PgDn, Home/End and the mouse wheel scroll
352
+ a long change list. Its height is capped at 80% of the terminal (up to 24 lines,
353
+ with a compact layout on tiny terminals); the confirmation buttons stay visible.
354
+ Up/Down and the mouse wheel move the selected row, just like the main picker;
355
+ the list scrolls when the selection reaches its edge. Tab or a click toggles the
356
+ row's staged enabled/disabled state. The picker stays visible behind the opaque
357
+ dialog; both are painted together in one refresh to prevent background flashes.
358
+ Reverted rows stay in the review so they can be toggled again; Yes applies only
359
+ the remaining changes. The dialog follows the selected language and color mode;
360
+ with `--dry-run`, Yes only shows the plan.
361
+
346
362
  Nothing happens while the picker is open. Changes are staged, the screen is
347
363
  torn down, and only then do the real operations run, so their output (which
348
364
  companion files moved, which were kept because they are shared) is readable
@@ -357,7 +373,8 @@ numbers (`1 3 5-7`) to tick or untick, `n <number>` to toggle every type with th
357
373
  row's name, `/text` to filter (`/` alone clears it),
358
374
  `s` / `h` / `t` to sort and cycle the harness and type filters, `p` to list profiles,
359
375
  `p <number|name>` to stage one and `p save <name>` to save the live state, `a` to
360
- apply, `q` (or end of input) to cancel.
376
+ apply, `q` to review staged changes with a `y/n` save prompt (no changes: exit
377
+ immediately). End of input discards changes.
361
378
 
362
379
  ### Mods
363
380
 
@@ -375,7 +392,7 @@ plugin: it ships skills, commands, agents, MCP servers or hooks and draws nothin
375
392
  grep -l modules ~/.claude/plugins/cache/*/*/*/hooks/hooks.json
376
393
  ```
377
394
 
378
- - The picker lists mods under **Mods**, right after Plugins. `t` cycles to it, the
395
+ - The picker lists mods under **Mods**, right after Plugins. `Ctrl-T` cycles to it, the
379
396
  `picker_type` setting can open on it, and a search for `mod` or `mods` finds every mod.
380
397
  The detail pane shows its type as `mod (plugin)`.
381
398
  - A mod is still a plugin everywhere else: it toggles through `claude plugin
@@ -956,20 +973,20 @@ claude --plugin-dir ./mod # then run /agent-toggle inside Claude
956
973
  and the first and last list lines count the rows out of view (`↑ 4`, `↓ 12`).
957
974
  - The mouse wheel over the list moves the cursor, as in the terminal picker; no click needed.
958
975
  - Click the list once so it takes keys (Esc hands the keyboard back to the prompt). Then `↑` `↓`
959
- `PgUp` `PgDn` `Home` `End` (also `Ctrl-P` / `Ctrl-N`) move; `Space` or `Enter` toggles the row,
960
- `Tab` toggles and moves on; `/` or any other letter starts a search (terms ANDed over
961
- `type/name` and the group, letters-in-order if nothing matches; a second `Space` toggles);
962
- `s` sorts by name or cost; `n` toggles every type with this row's name (skill, command, MCP,
976
+ `PgUp` `PgDn` `Home` `End` (also `Ctrl-P` / `Ctrl-N`) move; `Tab` toggles the row
977
+ and stays on it, including while searching; `/` or any letter starts a search (terms ANDed over
978
+ `type/name` and the group, letters-in-order if nothing matches; `Space` separates terms);
979
+ `Ctrl-S` sorts by name or cost; `Ctrl-E` toggles every type with this row's name (skill, command, MCP,
963
980
  ...), like `disable all <name>`; `Ctrl-U` clears the search. A click moves the cursor.
964
981
  - A toggle applies at once (no staging, no Enter to apply); the toast names the target and the state
965
982
  with an emoji and a bracketed tag (no colour codes, so nothing garbles; on the Linux console and the
966
983
  legacy Windows console, where emoji draw as boxes, the tag alone): `✅ [ENABLED] skill: foo`, `⛔ [DISABLED] mod: bar (/reload-plugins)`,
967
- `⛔ [DISABLED] rule: baz (takes effect in a new session)`, `⛔ [DISABLED] all: foo` after `n`, or `❌ [FAILED] disable skill: foo - <error>`.
984
+ `⛔ [DISABLED] rule: baz (takes effect in a new session)`, `⛔ [DISABLED] all: foo` after `Ctrl-E`, or `❌ [FAILED] disable skill: foo - <error>`.
968
985
  The row flips the moment you press (and flips back
969
986
  if the CLI fails); the list reloads in the background. While a toggle runs, further keys are ignored.
970
987
  - Opening the pane shows the same braille spinner and `Loading resources…` as `agent-toggle ui`
971
988
  until the list arrives, and a spinner beside `working…` while a toggle runs.
972
- - Not in the pane: harness tabs (claude only), and the `t` `p` `a` keys. Where Claude draws no
989
+ - Not in the pane: harness tabs (claude only), and the `Ctrl-T` `Ctrl-O` `Ctrl-A` keys. Where Claude draws no
973
990
  such pane (the mobile app, VS Code) it is a plain list of buttons instead.
974
991
  - `/agent-toggle <command> ...` with `disable`, `enable`,
975
992
  `undo`, `list`, `status`, `cost` or `doctor` runs the CLI instead of opening the pane and prints
@@ -1,3 +1,3 @@
1
1
  """agent-toggle: temporarily disable / re-enable AI-agent resources."""
2
2
 
3
- __version__ = "0.13.1"
3
+ __version__ = "0.14.0"
@@ -25,10 +25,10 @@ CATALOG: dict[str, str] = {
25
25
  "picker.loading": "載入資源中…",
26
26
  "picker.filter.type": "類型:",
27
27
  "picker.filter.sort": "排序:",
28
- "picker.empty": "沒有符合的項目 -- Ctrl-U 清除篩選,Left/Right 換 harness,t 換類型",
28
+ "picker.empty": "沒有符合的項目 -- Ctrl-U 清除篩選,Left/Right 換 harness,Ctrl-T 換類型",
29
29
  "picker.status.shown": "顯示 %s / %s",
30
30
  "picker.status.dry_run": "試跑:Enter 只顯示計畫",
31
- "picker.status.leave": "Ctrl+C 離開 · 自動儲存",
31
+ "picker.chip.leave": "離開/檢視變更",
32
32
  "picker.badge.shared": "+共用",
33
33
  # detail pane
34
34
  "picker.state.live": "啟用",
@@ -49,7 +49,7 @@ CATALOG: dict[str, str] = {
49
49
  # key chips
50
50
  "picker.chip.toggle": "切換",
51
51
  "picker.chip.apply": "套用",
52
- "picker.chip.cancel": "取消",
52
+ "picker.chip.cancel": "放棄",
53
53
  "picker.chip.help": "說明",
54
54
  "picker.chip.filter": "搜尋",
55
55
  "picker.chip.harness": "harness",
@@ -64,12 +64,17 @@ CATALOG: dict[str, str] = {
64
64
  "picker.help.title": "按鍵",
65
65
  "picker.help.move": "移動(也可用 Ctrl-P / Ctrl-N)",
66
66
  "picker.help.toggle": "切換此列(啟用 <-> 停用),停留在該列",
67
- "picker.help.toggle_next": "切換此列,然後移到下一列",
68
67
  "picker.help.apply": "套用暫存的變更(--dry-run:只看計畫)",
69
- "picker.help.cancel": "取消;不做任何變更",
70
- "picker.help.filter": "搜尋名稱 / harness / 路徑(可模糊);直接打其他字母也會開始搜尋",
68
+ "picker.help.cancel": "離開;有暫存變更時先詢問是否儲存",
69
+ "picker.help.discard": "放棄變更並離開",
70
+ "picker.save.title": "儲存變更?",
71
+ "picker.save.count": "%s 項變更",
72
+ "picker.save.yes": "是",
73
+ "picker.save.no": "否",
74
+ "picker.save.keys": "Tab/點擊 切換 · 上下 移動 · Enter 確認 · Esc 返回",
75
+ "picker.help.filter": "搜尋名稱 / harness / 路徑(可模糊);直接打任何字母或數字也會開始搜尋",
71
76
  "picker.help.edit": "編輯 / 清除篩選",
72
- "picker.help.harness": "harness 分頁(0 = 全部)",
77
+ "picker.help.harness": "harness 分頁",
73
78
  "picker.help.type": "循環切換類型篩選",
74
79
  "picker.help.sort": "循環切換排序:名稱 / 成本(由大到小)",
75
80
  "picker.help.profile": "設定檔:暫存一個,或儲存目前啟用的狀態",
@@ -8,6 +8,7 @@ from __future__ import annotations
8
8
  import sys
9
9
  from pathlib import Path
10
10
 
11
+ from ..i18n import t
11
12
  from .model import (
12
13
  SORTS,
13
14
  Row,
@@ -90,7 +91,22 @@ def loop(rows: list[Row], stdin, stdout, project: Path | None = None,
90
91
  cmd = line.strip()
91
92
  low = cmd.lower()
92
93
  if low in ("q", "quit"):
93
- return None
94
+ changed = [r for r in kept if r.changed]
95
+ if not changed:
96
+ return None
97
+ for row in changed:
98
+ state = t("picker.state.live", "live") if row.staged else t("picker.state.parked", "parked")
99
+ stdout.write(encodable(ansi("pending", f"{row.label} -> {state}", color) + "\n", stdout))
100
+ if dry_run:
101
+ stdout.write(encodable(t("picker.status.dry_run", "dry run: Enter only shows the plan") + "\n", stdout))
102
+ while True:
103
+ stdout.write(encodable(t("picker.save.title", "Save changes?") + " [y/n]: ", stdout))
104
+ stdout.flush()
105
+ answer = stdin.readline().strip().lower()
106
+ if answer in ("y", "yes", "是"):
107
+ return changed
108
+ if answer in ("", "n", "no", "否"):
109
+ return None
94
110
  if low in ("a", "apply"):
95
111
  return [r for r in kept if r.changed]
96
112
  if low == "s":