agent-toggle 0.6.0__tar.gz → 0.6.2__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 (85) hide show
  1. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/PKG-INFO +1 -1
  2. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/__init__.py +1 -1
  3. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/cost.py +3 -2
  4. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/profiles.py +4 -0
  5. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/ui/model.py +5 -2
  6. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle.egg-info/PKG-INFO +1 -1
  7. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_alias.py +3 -2
  8. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_crash.py +4 -1
  9. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_doctor.py +1 -1
  10. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_install.py +1 -1
  11. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_picker.py +2 -1
  12. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_project.py +3 -3
  13. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_theme.py +1 -1
  14. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_update_check.py +3 -1
  15. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/LICENSE +0 -0
  16. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/README.md +0 -0
  17. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/__main__.py +0 -0
  18. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/backends/__init__.py +0 -0
  19. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/backends/flag_json.py +0 -0
  20. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/backends/mcp_json.py +0 -0
  21. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/backends/mcp_toml.py +0 -0
  22. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/backends/plugin_cli.py +0 -0
  23. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/cli.py +0 -0
  24. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/companions.py +0 -0
  25. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/config.py +0 -0
  26. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/doctor.py +0 -0
  27. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/fs.py +0 -0
  28. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/harnesses.py +0 -0
  29. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/help.py +0 -0
  30. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/i18n.py +0 -0
  31. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/locales/__init__.py +0 -0
  32. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/locales/zh_tw_cli.py +0 -0
  33. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/locales/zh_tw_common.py +0 -0
  34. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/locales/zh_tw_config.py +0 -0
  35. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/locales/zh_tw_picker.py +0 -0
  36. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/mechanisms.py +0 -0
  37. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/ops.py +0 -0
  38. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/output.py +0 -0
  39. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/settings.py +0 -0
  40. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/shims/claude.md.tmpl +0 -0
  41. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/shims/generic.md.tmpl +0 -0
  42. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/spinner.py +0 -0
  43. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/store.py +0 -0
  44. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/toml_check.py +0 -0
  45. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/ui/__init__.py +0 -0
  46. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/ui/config_menu.py +0 -0
  47. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/ui/menu.py +0 -0
  48. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/ui/picker.py +2 -2
  49. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/ui/theme.py +0 -0
  50. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/undo.py +0 -0
  51. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/update_check.py +0 -0
  52. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle/update_prompt.py +0 -0
  53. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle.egg-info/SOURCES.txt +0 -0
  54. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle.egg-info/dependency_links.txt +0 -0
  55. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle.egg-info/entry_points.txt +0 -0
  56. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle.egg-info/requires.txt +0 -0
  57. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/agent_toggle.egg-info/top_level.txt +0 -0
  58. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/pyproject.toml +0 -0
  59. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/setup.cfg +0 -0
  60. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_backends.py +0 -0
  61. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_cli.py +0 -0
  62. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_cli_surface.py +0 -0
  63. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_color.py +0 -0
  64. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_companions.py +0 -0
  65. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_completions.py +0 -0
  66. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_config.py +0 -0
  67. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_config_menu.py +0 -0
  68. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_conformance.py +0 -0
  69. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_containment.py +0 -0
  70. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_cost.py +0 -0
  71. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_encoding.py +0 -0
  72. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_fs.py +0 -0
  73. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_harnesses.py +0 -0
  74. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_help.py +0 -0
  75. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_i18n.py +0 -0
  76. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_mechanisms.py +0 -0
  77. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_menu.py +0 -0
  78. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_platform.py +0 -0
  79. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_profiles.py +0 -0
  80. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_safety.py +0 -0
  81. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_secret_audit.py +0 -0
  82. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_settings.py +0 -0
  83. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_store.py +0 -0
  84. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/tests/test_undo.py +0 -0
  85. {agent_toggle-0.6.0 → agent_toggle-0.6.2}/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.6.0
3
+ Version: 0.6.2
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
@@ -1,3 +1,3 @@
1
1
  """agent-toggle: temporarily disable / re-enable AI-agent resources."""
2
2
 
3
- __version__ = "0.6.0"
3
+ __version__ = "0.6.2"
@@ -314,8 +314,9 @@ def inventory(state: dict, table: dict, warn: Callable[[str], None] = lambda m:
314
314
  n = full_plugin_id(n, listed) or n # ambiguous: keep it as parked
315
315
  else:
316
316
  parked = e.get("parked_at")
317
- est = lambda t=t, n=n, p=parked: file_estimate(
318
- t, n, Path(p) if isinstance(p, str) and p else None)
317
+
318
+ def est(t=t, n=n, p=parked):
319
+ return file_estimate(t, n, Path(p) if isinstance(p, str) and p else None)
319
320
  shared = e.get("shared_with")
320
321
  add(h, t, n, False, est,
321
322
  [x for x in shared if isinstance(x, str)] if isinstance(shared, list) else ())
@@ -39,6 +39,10 @@ def _stored_path(name: str) -> Path:
39
39
  validate_name(name)
40
40
  if ":" in name or name.split(".")[0].upper() in WIN_RESERVED or name.endswith("."):
41
41
  die(f"invalid profile name {name!r}", 2)
42
+ try:
43
+ name.encode("utf-8") # a lone surrogate is a legal NTFS file name
44
+ except UnicodeEncodeError:
45
+ die(f"invalid profile name {name!r}", 2)
42
46
  return fs.profiles_dir() / f"{name}.json"
43
47
 
44
48
 
@@ -124,8 +124,11 @@ def fmt_tokens(n: int) -> str:
124
124
 
125
125
  def short_path(path: str) -> str:
126
126
  """`path` with the home dir spelled `~`."""
127
- home = str(fs.home())
128
- return "~" + path[len(home):] if path == home or path.startswith(home + "/") else path
127
+ try:
128
+ rel = Path(path).relative_to(fs.home())
129
+ except ValueError:
130
+ return path
131
+ return "~/" + rel.as_posix() if rel.parts else "~"
129
132
 
130
133
 
131
134
  def _hay(r: Row) -> str:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-toggle
3
- Version: 0.6.0
3
+ Version: 0.6.2
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
@@ -176,14 +176,15 @@ class OpencodeTableTest(AliasBase):
176
176
 
177
177
  def test_skill_dirs_default_and_additive_and_survive_bad_json(self) -> None:
178
178
  claude, agents = str(self.tmp / ".claude/skills"), str(self.tmp / ".agents/skills")
179
+ absdir = str(self.tmp / "abs" / "dir")
179
180
  default = ("skills", "skill", claude, agents) # T1: OpenCode 2.0.22 scans all four
180
181
  self.assertEqual(self.dirs(), default)
181
182
  self.write_cfg("{ not json")
182
183
  self.assertEqual(self.dirs(), default)
183
184
  self.write_cfg(json.dumps({"skills": {"paths": [
184
- "~/.codex/skills", "rel", "~other/x", 7, "~/.codex/skills", "/abs/dir"]}}))
185
+ "~/.codex/skills", "rel", "~other/x", 7, "~/.codex/skills", absdir]}}))
185
186
  # redirects first (install-shims target), own dirs stay scanned, relative skipped
186
- self.assertEqual(self.dirs(), (str(self.tmp / ".codex/skills"), "/abs/dir", *default))
187
+ self.assertEqual(self.dirs(), (str(self.tmp / ".codex/skills"), absdir, *default))
187
188
  (self.oc / "opencode.json").write_bytes(b"\xff\xfe{") # not UTF-8: still no crash
188
189
  self.assertEqual(self.dirs(), default)
189
190
  shutil.rmtree(self.oc) # not installed: no other harness's dirs
@@ -10,8 +10,10 @@ import contextlib
10
10
  import errno
11
11
  import json
12
12
  import os
13
+ import shlex
13
14
  import shutil
14
15
  import stat
16
+ import unittest
15
17
  from pathlib import Path
16
18
  from unittest import mock
17
19
 
@@ -131,6 +133,7 @@ class FlagCrashTest(CrashCase):
131
133
  self.assertEqual(self.file.read_bytes(), self.before)
132
134
  self.assertEqual(self.raw_state(), {"version": 3, "disabled": {}})
133
135
 
136
+ @unittest.skipIf(os.name == "nt", "lock liveness is POSIX-only (fs._holder_alive)")
134
137
  def test_status_does_not_call_a_live_runs_op_interrupted(self) -> None:
135
138
  with self.killed("agent_toggle.mechanisms.set_flag", side_effect=self.write_then_die):
136
139
  self.run_cli("disable", *self.ARGS)
@@ -252,7 +255,7 @@ class MoveCrashTest(CrashCase):
252
255
  rc, env = self.run_json("doctor")
253
256
  stuck = [r for r in env["results"] if r.get("pending") == "stuck"]
254
257
  self.assertEqual(len(stuck), 1, env)
255
- self.assertIn(f"rm -rf {self.pskill.resolve()}", stuck[0]["detail"])
258
+ self.assertIn(f"rm -rf {shlex.quote(str(self.pskill.resolve()))}", stuck[0]["detail"])
256
259
  self.p("disable", "agent", "demo-agent") # an unrelated change
257
260
  self.assertIn(self.key, self.pending()) # never resolved by deleting
258
261
  self.assertEqual(file_bytes(self.pskill), self.files)
@@ -363,7 +363,7 @@ class FixTest(DoctorCase):
363
363
  self.write("skills-disabled/solo/SKILL.md")
364
364
  before = snapshot(self.tmp)
365
365
  asked, _ = self.fixes(answer=False)
366
- self.assertEqual(len(asked), 2, asked)
366
+ self.assertEqual(len(asked), 1 if os.name == "nt" else 2, asked) # no loose-mode fix on Windows
367
367
  self.assertEqual(snapshot(self.tmp), before)
368
368
 
369
369
  @unittest.skipIf(os.name == "nt", "no POSIX modes")
@@ -87,7 +87,7 @@ class InstallShimsCase(SandboxCase):
87
87
  self.assertTrue(shim(oc).is_file())
88
88
  shutil.rmtree(oc / "skills")
89
89
  (oc / "opencode.json").write_text(
90
- json.dumps({"skills": ["/no/such/parent/skills"]}), encoding="utf-8")
90
+ json.dumps({"skills": [str(self.tmp / "no" / "such" / "parent" / "skills")]}), encoding="utf-8")
91
91
  self.assertEqual(self.run_cli("--harness", "opencode")[0], 0)
92
92
  self.assertTrue(shim(oc).is_file())
93
93
 
@@ -529,7 +529,8 @@ class LayoutTest(ScreenCase):
529
529
  self.assertTrue(win.attrs[y, 2] & C.A_BOLD) # live glyph: bold
530
530
  self.assertTrue(win.attrs[y, 6] & C.A_REVERSE) # cursor bar
531
531
  beta = win.find("beta")
532
- self.assertTrue(win.attrs[beta, 2] & C.A_DIM) # parked glyph: dim
532
+ if C.A_DIM: # PDCurses defines it as 0
533
+ self.assertTrue(win.attrs[beta, 2] & C.A_DIM) # parked glyph: dim
533
534
 
534
535
  def test_color_draw_uses_pairs_and_no_colors_falls_back(self) -> None:
535
536
  C = picker.curses
@@ -602,7 +602,7 @@ class ProjectMcpTest(ProjectCase):
602
602
  def test_an_old_whole_file_backup_still_restores_exactly(self) -> None:
603
603
  from agent_toggle.backends import mcp_json
604
604
  original = json.dumps({"mcpServers": self.SERVERS}, indent=4) + "\n"
605
- self.mcpfile.write_text(original, encoding="utf-8")
605
+ self.mcpfile.write_bytes(original.encode()) # bytes: write_text would make CRLF on Windows
606
606
  self.p("disable", "mcp", "example-mcp")
607
607
  bp = Path(self.state()[self.mkey]["backup"])
608
608
  # what the pre-G15 disable wrote: the file re-dumped, and both whole texts
@@ -611,9 +611,9 @@ class ProjectMcpTest(ProjectCase):
611
611
  old = json.dumps({"project_json": {"entry": self.SERVERS["example-mcp"],
612
612
  "before": original, "after": old_after}})
613
613
  bp.write_text(old, encoding="utf-8")
614
- self.mcpfile.write_text(old_after, encoding="utf-8")
614
+ self.mcpfile.write_bytes(old_after.encode())
615
615
  self.assertEqual(self.p("enable", "mcp", "example-mcp")[0], 0)
616
- self.assertEqual(self.mcpfile.read_text(encoding="utf-8"), original)
616
+ self.assertEqual(self.mcpfile.read_bytes().decode(), original)
617
617
  self.assertEqual(bp.read_text(encoding="utf-8"), old) # never rewritten
618
618
  # file changed meanwhile: the old merge path, as before
619
619
  self.p("disable", "mcp", "example-mcp")
@@ -89,7 +89,7 @@ class ImportTest(unittest.TestCase):
89
89
 
90
90
  class GlyphTest(unittest.TestCase):
91
91
  def test_unicode_when_the_stream_can_encode_it(self):
92
- self.assertIs(theme.get_glyphs(Enc("utf-8"), {"TERM": "xterm"}), UNICODE)
92
+ self.assertIs(theme.get_glyphs(Enc("utf-8"), {"TERM": "xterm", "WT_SESSION": "1"}), UNICODE)
93
93
 
94
94
  def test_ascii_when_the_encoding_cannot(self):
95
95
  self.assertIs(theme.get_glyphs(Enc("cp1252"), {"TERM": "xterm"}), ASCII)
@@ -9,6 +9,7 @@ from __future__ import annotations
9
9
 
10
10
  import ast
11
11
  import json
12
+ import os
12
13
  import shutil
13
14
  import tempfile
14
15
  import threading
@@ -233,7 +234,8 @@ class SecurityTest(UpdateCheckCase):
233
234
  self.check()
234
235
  self.assertFalse(self.cache.is_symlink())
235
236
  self.assertEqual(target.read_text(encoding="utf-8"), "keep")
236
- self.assertEqual(self.cache.stat().st_mode & 0o777, 0o600)
237
+ if os.name != "nt": # Windows has no POSIX modes
238
+ self.assertEqual(self.cache.stat().st_mode & 0o777, 0o600)
237
239
 
238
240
  def test_fetch_reads_at_most_a_megabyte(self):
239
241
  seen: list = []
File without changes
File without changes
@@ -20,8 +20,8 @@ import sys
20
20
  from pathlib import Path
21
21
 
22
22
  from .. import __version__
23
- from ..spinner import spinner
24
23
  from ..i18n import t
24
+ from ..spinner import spinner
25
25
  from . import theme
26
26
  from .model import (
27
27
  SORTS,
@@ -34,9 +34,9 @@ from .model import (
34
34
  profile_command,
35
35
  profile_listing,
36
36
  short_path,
37
- type_label,
38
37
  strict,
39
38
  subsequence,
39
+ type_label,
40
40
  visible,
41
41
  )
42
42
 
File without changes