saycode-claude-swap 0.27.0b2__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 (109) hide show
  1. saycode_claude_swap-0.27.0b2/.github/FUNDING.yml +1 -0
  2. saycode_claude_swap-0.27.0b2/.github/ISSUE_TEMPLATE/bug_report.yml +47 -0
  3. saycode_claude_swap-0.27.0b2/.github/ISSUE_TEMPLATE/config.yml +5 -0
  4. saycode_claude_swap-0.27.0b2/.github/ISSUE_TEMPLATE/feature_request.yml +16 -0
  5. saycode_claude_swap-0.27.0b2/.github/workflows/ci.yml +82 -0
  6. saycode_claude_swap-0.27.0b2/.github/workflows/publish.yml +67 -0
  7. saycode_claude_swap-0.27.0b2/.gitignore +17 -0
  8. saycode_claude_swap-0.27.0b2/.python-version +1 -0
  9. saycode_claude_swap-0.27.0b2/.vscode/launch.json +60 -0
  10. saycode_claude_swap-0.27.0b2/LICENSE +21 -0
  11. saycode_claude_swap-0.27.0b2/PKG-INFO +434 -0
  12. saycode_claude_swap-0.27.0b2/README.md +403 -0
  13. saycode_claude_swap-0.27.0b2/assets/tui-watch.png +0 -0
  14. saycode_claude_swap-0.27.0b2/pyproject.toml +72 -0
  15. saycode_claude_swap-0.27.0b2/specs/integration-provenance.md +137 -0
  16. saycode_claude_swap-0.27.0b2/specs/token-runtime-contract.md +554 -0
  17. saycode_claude_swap-0.27.0b2/src/claude_swap/__init__.py +9 -0
  18. saycode_claude_swap-0.27.0b2/src/claude_swap/__main__.py +6 -0
  19. saycode_claude_swap-0.27.0b2/src/claude_swap/appearance.py +218 -0
  20. saycode_claude_swap-0.27.0b2/src/claude_swap/autoswitch.py +2376 -0
  21. saycode_claude_swap-0.27.0b2/src/claude_swap/cache.py +44 -0
  22. saycode_claude_swap-0.27.0b2/src/claude_swap/claude_locks.py +187 -0
  23. saycode_claude_swap-0.27.0b2/src/claude_swap/cli.py +1537 -0
  24. saycode_claude_swap-0.27.0b2/src/claude_swap/credentials.py +1849 -0
  25. saycode_claude_swap-0.27.0b2/src/claude_swap/exceptions.py +96 -0
  26. saycode_claude_swap-0.27.0b2/src/claude_swap/fsutil.py +101 -0
  27. saycode_claude_swap-0.27.0b2/src/claude_swap/json_output.py +363 -0
  28. saycode_claude_swap-0.27.0b2/src/claude_swap/launch_agent.py +289 -0
  29. saycode_claude_swap-0.27.0b2/src/claude_swap/locking.py +83 -0
  30. saycode_claude_swap-0.27.0b2/src/claude_swap/logging_config.py +64 -0
  31. saycode_claude_swap-0.27.0b2/src/claude_swap/macos_keychain.py +226 -0
  32. saycode_claude_swap-0.27.0b2/src/claude_swap/mappings.py +142 -0
  33. saycode_claude_swap-0.27.0b2/src/claude_swap/menubar.py +1057 -0
  34. saycode_claude_swap-0.27.0b2/src/claude_swap/migrations.py +537 -0
  35. saycode_claude_swap-0.27.0b2/src/claude_swap/models.py +231 -0
  36. saycode_claude_swap-0.27.0b2/src/claude_swap/oauth.py +812 -0
  37. saycode_claude_swap-0.27.0b2/src/claude_swap/org_probe.py +110 -0
  38. saycode_claude_swap-0.27.0b2/src/claude_swap/pace.py +161 -0
  39. saycode_claude_swap-0.27.0b2/src/claude_swap/paths.py +221 -0
  40. saycode_claude_swap-0.27.0b2/src/claude_swap/poll_policy.py +270 -0
  41. saycode_claude_swap-0.27.0b2/src/claude_swap/printer.py +232 -0
  42. saycode_claude_swap-0.27.0b2/src/claude_swap/process_detection.py +350 -0
  43. saycode_claude_swap-0.27.0b2/src/claude_swap/rotation_owner.py +352 -0
  44. saycode_claude_swap-0.27.0b2/src/claude_swap/session.py +1500 -0
  45. saycode_claude_swap-0.27.0b2/src/claude_swap/settings.py +488 -0
  46. saycode_claude_swap-0.27.0b2/src/claude_swap/snapshot_source.py +100 -0
  47. saycode_claude_swap-0.27.0b2/src/claude_swap/switcher.py +7530 -0
  48. saycode_claude_swap-0.27.0b2/src/claude_swap/token_probe.py +145 -0
  49. saycode_claude_swap-0.27.0b2/src/claude_swap/token_runtime.py +652 -0
  50. saycode_claude_swap-0.27.0b2/src/claude_swap/transfer.py +852 -0
  51. saycode_claude_swap-0.27.0b2/src/claude_swap/tui/__init__.py +43 -0
  52. saycode_claude_swap-0.27.0b2/src/claude_swap/tui/app.py +434 -0
  53. saycode_claude_swap-0.27.0b2/src/claude_swap/tui/autoview.py +331 -0
  54. saycode_claude_swap-0.27.0b2/src/claude_swap/tui/cswap.tcss +205 -0
  55. saycode_claude_swap-0.27.0b2/src/claude_swap/tui/dashboard.py +432 -0
  56. saycode_claude_swap-0.27.0b2/src/claude_swap/tui/data.py +194 -0
  57. saycode_claude_swap-0.27.0b2/src/claude_swap/tui/modals.py +159 -0
  58. saycode_claude_swap-0.27.0b2/src/claude_swap/tui/theme.py +141 -0
  59. saycode_claude_swap-0.27.0b2/src/claude_swap/tui/widgets.py +403 -0
  60. saycode_claude_swap-0.27.0b2/src/claude_swap/update_check.py +184 -0
  61. saycode_claude_swap-0.27.0b2/src/claude_swap/usage_store.py +1325 -0
  62. saycode_claude_swap-0.27.0b2/tests/__init__.py +1 -0
  63. saycode_claude_swap-0.27.0b2/tests/conftest.py +880 -0
  64. saycode_claude_swap-0.27.0b2/tests/fixtures/sample_config.json +10 -0
  65. saycode_claude_swap-0.27.0b2/tests/pack_token_runtime.py +162 -0
  66. saycode_claude_swap-0.27.0b2/tests/rotation_contention_worker.py +204 -0
  67. saycode_claude_swap-0.27.0b2/tests/test_add_account_identity.py +720 -0
  68. saycode_claude_swap-0.27.0b2/tests/test_api_key_accounts.py +1108 -0
  69. saycode_claude_swap-0.27.0b2/tests/test_appearance.py +331 -0
  70. saycode_claude_swap-0.27.0b2/tests/test_autoswitch.py +6896 -0
  71. saycode_claude_swap-0.27.0b2/tests/test_cache.py +72 -0
  72. saycode_claude_swap-0.27.0b2/tests/test_claude_locks.py +199 -0
  73. saycode_claude_swap-0.27.0b2/tests/test_cli.py +1816 -0
  74. saycode_claude_swap-0.27.0b2/tests/test_colour_cache_isolation.py +196 -0
  75. saycode_claude_swap-0.27.0b2/tests/test_config_cli.py +270 -0
  76. saycode_claude_swap-0.27.0b2/tests/test_credentials.py +273 -0
  77. saycode_claude_swap-0.27.0b2/tests/test_fsutil.py +286 -0
  78. saycode_claude_swap-0.27.0b2/tests/test_json_output.py +803 -0
  79. saycode_claude_swap-0.27.0b2/tests/test_launch_agent.py +396 -0
  80. saycode_claude_swap-0.27.0b2/tests/test_locking.py +162 -0
  81. saycode_claude_swap-0.27.0b2/tests/test_logging_config.py +59 -0
  82. saycode_claude_swap-0.27.0b2/tests/test_macos_keychain.py +212 -0
  83. saycode_claude_swap-0.27.0b2/tests/test_macos_keychain_contract.py +1202 -0
  84. saycode_claude_swap-0.27.0b2/tests/test_mappings.py +185 -0
  85. saycode_claude_swap-0.27.0b2/tests/test_menubar.py +619 -0
  86. saycode_claude_swap-0.27.0b2/tests/test_migrations.py +596 -0
  87. saycode_claude_swap-0.27.0b2/tests/test_move_accounts.py +578 -0
  88. saycode_claude_swap-0.27.0b2/tests/test_oauth.py +1534 -0
  89. saycode_claude_swap-0.27.0b2/tests/test_org_probe.py +167 -0
  90. saycode_claude_swap-0.27.0b2/tests/test_pace.py +169 -0
  91. saycode_claude_swap-0.27.0b2/tests/test_paths.py +316 -0
  92. saycode_claude_swap-0.27.0b2/tests/test_poll_policy.py +315 -0
  93. saycode_claude_swap-0.27.0b2/tests/test_printer.py +308 -0
  94. saycode_claude_swap-0.27.0b2/tests/test_process_detection.py +650 -0
  95. saycode_claude_swap-0.27.0b2/tests/test_real_store_guard.py +912 -0
  96. saycode_claude_swap-0.27.0b2/tests/test_rotation_contention.py +98 -0
  97. saycode_claude_swap-0.27.0b2/tests/test_rotation_owner.py +598 -0
  98. saycode_claude_swap-0.27.0b2/tests/test_session.py +2863 -0
  99. saycode_claude_swap-0.27.0b2/tests/test_settings.py +357 -0
  100. saycode_claude_swap-0.27.0b2/tests/test_swap_accounts.py +542 -0
  101. saycode_claude_swap-0.27.0b2/tests/test_switcher.py +12498 -0
  102. saycode_claude_swap-0.27.0b2/tests/test_theme.py +67 -0
  103. saycode_claude_swap-0.27.0b2/tests/test_token_probe.py +137 -0
  104. saycode_claude_swap-0.27.0b2/tests/test_token_runtime.py +390 -0
  105. saycode_claude_swap-0.27.0b2/tests/test_transfer.py +2470 -0
  106. saycode_claude_swap-0.27.0b2/tests/test_tui.py +1712 -0
  107. saycode_claude_swap-0.27.0b2/tests/test_update_check.py +351 -0
  108. saycode_claude_swap-0.27.0b2/tests/test_usage_store.py +1847 -0
  109. saycode_claude_swap-0.27.0b2/uv.lock +361 -0
@@ -0,0 +1 @@
1
+ github: realiti4
@@ -0,0 +1,47 @@
1
+ name: Bug report
2
+ description: Something in cswap isn't working right
3
+ labels: [bug]
4
+ body:
5
+ - type: textarea
6
+ id: what-happened
7
+ attributes:
8
+ label: What happened?
9
+ description: What did you do, what did you see, and what did you expect instead?
10
+ placeholder: Ran `cswap switch 2`, got ... — expected ...
11
+ validations:
12
+ required: true
13
+ - type: input
14
+ id: version
15
+ attributes:
16
+ label: cswap version and install method
17
+ description: Output of `cswap --version`, and whether you installed with uv, pipx, or from source.
18
+ placeholder: cswap 0.20.0, installed with uv
19
+ validations:
20
+ required: true
21
+ - type: dropdown
22
+ id: os
23
+ attributes:
24
+ label: Operating system
25
+ description: Credentials are stored differently per platform, so this matters a lot.
26
+ options:
27
+ - macOS
28
+ - Linux
29
+ - Windows
30
+ - WSL
31
+ validations:
32
+ required: true
33
+ - type: dropdown
34
+ id: claude-code
35
+ attributes:
36
+ label: How do you run Claude Code?
37
+ multiple: true
38
+ options:
39
+ - CLI (terminal)
40
+ - VS Code extension
41
+ - Desktop app
42
+ - type: textarea
43
+ id: logs
44
+ attributes:
45
+ label: Command output or logs
46
+ description: Paste the failing command's output — `cswap list` for account-state issues, `cswap auto` log lines for auto-switch issues. Formatted automatically, no backticks needed. Please redact emails/account identifiers, and never paste access/refresh tokens, API keys, setup tokens, or credential-file/Keychain contents.
47
+ render: shell
@@ -0,0 +1,5 @@
1
+ blank_issues_enabled: true
2
+ contact_links:
3
+ - name: 📖 Tips & how it works
4
+ url: https://github.com/realiti4/claude-swap#tips
5
+ about: Restart-after-switch behavior, macOS Keychain caching, and other common gotchas are covered in the README.
@@ -0,0 +1,16 @@
1
+ name: Feature request
2
+ description: Suggest an improvement or new capability
3
+ labels: [enhancement]
4
+ body:
5
+ - type: textarea
6
+ id: problem
7
+ attributes:
8
+ label: What problem would this solve?
9
+ description: Describe the situation where you miss this feature — the workflow matters more than the mechanism.
10
+ validations:
11
+ required: true
12
+ - type: textarea
13
+ id: solution
14
+ attributes:
15
+ label: What would you like to happen?
16
+ description: A rough sketch is fine — commands, flags, behavior.
@@ -0,0 +1,82 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+
16
+ # uv (not pip) so the PEP 735 dev dependency-group — pytest,
17
+ # pytest-asyncio — installs from the committed lockfile, same as local
18
+ # development.
19
+ - name: Install uv
20
+ uses: astral-sh/setup-uv@v6
21
+ with:
22
+ python-version: '3.12'
23
+
24
+ - name: Install dependencies
25
+ run: uv sync --locked
26
+
27
+ - name: Run tests
28
+ run: uv run pytest
29
+
30
+ test-windows:
31
+ runs-on: windows-latest
32
+
33
+ steps:
34
+ - uses: actions/checkout@v4
35
+
36
+ - name: Install uv
37
+ uses: astral-sh/setup-uv@v6
38
+ with:
39
+ python-version: '3.12'
40
+
41
+ - name: Install dependencies
42
+ run: uv sync --locked
43
+
44
+ - name: Run tests
45
+ # Includes the TUI tests: the Textual TUI (unlike the old curses one)
46
+ # runs on stock Windows, and this job is the proof.
47
+ run: uv run pytest
48
+
49
+ # The id stays `macos-keychain` although the job now runs everything: it is
50
+ # the name branch protection matches on, and renaming it silently drops the
51
+ # required check.
52
+ macos-keychain:
53
+ runs-on: macos-latest
54
+
55
+ steps:
56
+ - uses: actions/checkout@v4
57
+
58
+ - name: Install uv
59
+ uses: astral-sh/setup-uv@v6
60
+ with:
61
+ python-version: '3.12'
62
+
63
+ - name: Install dependencies
64
+ run: uv sync --locked
65
+
66
+ - name: Run the whole suite (not only the Keychain files)
67
+ # THE NARROW SELECTION WAS A BLIND SPOT, not a saving. Three cases
68
+ # failed on macOS -- one asserting the Linux XDG layout, two because a
69
+ # usable Keychain takes the write and leaves no `.enc` to chmod --
70
+ # while every job stayed green, because this job never ran them. A merge that
71
+ # restores a two-file selection here re-opens that hole invisibly:
72
+ # those cases pass now, they would simply stop running.
73
+ #
74
+ # faulthandler dumps every thread's stack past the budget, the only
75
+ # clue when a `security` call blocks on an invisible SecurityAgent
76
+ # dialog. While pyproject sets no faulthandler keys this `-o` is the
77
+ # only thing setting the budget and the dump does not abort; PR 287
78
+ # adds them repo-wide at the same 600, after which this is a no-op
79
+ # and the dump aborts the worker. `timeout-minutes` is the backstop
80
+ # that holds under either ordering.
81
+ timeout-minutes: 15
82
+ run: uv run pytest -o faulthandler_timeout=600
@@ -0,0 +1,67 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch:
7
+ inputs:
8
+ version:
9
+ description: "Package version to publish (must match pyproject.toml)"
10
+ required: true
11
+ type: string
12
+
13
+ jobs:
14
+ publish:
15
+ runs-on: ubuntu-latest
16
+ environment: pypi
17
+ permissions:
18
+ id-token: write
19
+
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+
23
+ - name: Resolve release version
24
+ id: version
25
+ env:
26
+ RELEASE_TAG: ${{ github.event.release.tag_name }}
27
+ DISPATCH_VERSION: ${{ inputs.version }}
28
+ run: |
29
+ set -eu
30
+ if [ -n "${RELEASE_TAG}" ]; then
31
+ version="${RELEASE_TAG#v}"
32
+ else
33
+ version="${DISPATCH_VERSION}"
34
+ fi
35
+ test -n "${version}"
36
+ echo "value=${version}" >> "${GITHUB_OUTPUT}"
37
+
38
+ - name: Verify package version
39
+ env:
40
+ EXPECTED_VERSION: ${{ steps.version.outputs.value }}
41
+ run: |
42
+ set -eu
43
+ actual="$(python - <<'PY'
44
+ import pathlib, re
45
+
46
+ text = pathlib.Path("pyproject.toml").read_text()
47
+ match = re.search(r'^version\s*=\s*[\"\']([^\"\']+)[\"\']$', text, re.MULTILINE)
48
+ if match is None:
49
+ raise SystemExit("pyproject.toml version is missing")
50
+ print(match.group(1))
51
+ PY
52
+ )"
53
+ test "${actual}" = "${EXPECTED_VERSION}"
54
+
55
+ - name: Set up Python
56
+ uses: actions/setup-python@v5
57
+ with:
58
+ python-version: '3.12'
59
+
60
+ - name: Install build dependencies
61
+ run: pip install build
62
+
63
+ - name: Build package
64
+ run: python -m build
65
+
66
+ - name: Publish to PyPI
67
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,17 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+ # superpowers session planning artifacts (not part of the project)
13
+ docs/superpowers/
14
+
15
+ # Worktree-local dependency/tool caches are not distributable sources
16
+ .uv-cache/
17
+ .ruff_cache/
@@ -0,0 +1 @@
1
+ 3.14
@@ -0,0 +1,60 @@
1
+ {
2
+ "version": "0.2.0",
3
+ "configurations": [
4
+ {
5
+ "name": "cswap --list",
6
+ "type": "debugpy",
7
+ "request": "launch",
8
+ "module": "claude_swap.cli",
9
+ "args": ["--list"],
10
+ "cwd": "${workspaceFolder}",
11
+ "env": {
12
+ "PYTHONPATH": "${workspaceFolder}/src"
13
+ },
14
+ "console": "integratedTerminal"
15
+ },
16
+ {
17
+ "name": "cswap --switch",
18
+ "type": "debugpy",
19
+ "request": "launch",
20
+ "module": "claude_swap.cli",
21
+ "args": ["--switch"],
22
+ "cwd": "${workspaceFolder}",
23
+ "env": {
24
+ "PYTHONPATH": "${workspaceFolder}/src"
25
+ },
26
+ "console": "integratedTerminal"
27
+ },
28
+ {
29
+ "name": "cswap --purge",
30
+ "type": "debugpy",
31
+ "request": "launch",
32
+ "module": "claude_swap.cli",
33
+ "args": ["--purge"],
34
+ "cwd": "${workspaceFolder}",
35
+ "env": {
36
+ "PYTHONPATH": "${workspaceFolder}/src"
37
+ },
38
+ "console": "integratedTerminal"
39
+ },
40
+ {
41
+ "name": "cswap (custom args)",
42
+ "type": "debugpy",
43
+ "request": "launch",
44
+ "module": "claude_swap.cli",
45
+ "args": ["${input:cliArgs}"],
46
+ "cwd": "${workspaceFolder}",
47
+ "env": {
48
+ "PYTHONPATH": "${workspaceFolder}/src"
49
+ },
50
+ "console": "integratedTerminal"
51
+ }
52
+ ],
53
+ "inputs": [
54
+ {
55
+ "id": "cliArgs",
56
+ "type": "promptString",
57
+ "description": "CLI arguments (e.g., --list, --status, --switch)"
58
+ }
59
+ ]
60
+ }
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Onur Cetinkol
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.