devin-switch 0.1.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 (40) hide show
  1. devin_switch-0.1.0/LICENSE +21 -0
  2. devin_switch-0.1.0/PKG-INFO +214 -0
  3. devin_switch-0.1.0/README.md +201 -0
  4. devin_switch-0.1.0/pyproject.toml +29 -0
  5. devin_switch-0.1.0/setup.cfg +4 -0
  6. devin_switch-0.1.0/src/devin_switch/__init__.py +12 -0
  7. devin_switch-0.1.0/src/devin_switch/cli.py +320 -0
  8. devin_switch-0.1.0/src/devin_switch/engine.py +256 -0
  9. devin_switch-0.1.0/src/devin_switch/health.py +255 -0
  10. devin_switch-0.1.0/src/devin_switch/journal.py +82 -0
  11. devin_switch-0.1.0/src/devin_switch/jsonc.py +60 -0
  12. devin_switch-0.1.0/src/devin_switch/paths.py +145 -0
  13. devin_switch-0.1.0/src/devin_switch/plan.py +220 -0
  14. devin_switch-0.1.0/src/devin_switch/profiles/README.md +63 -0
  15. devin_switch-0.1.0/src/devin_switch/profiles/corporate/User/settings.json +11 -0
  16. devin_switch-0.1.0/src/devin_switch/profiles/corporate/config.json +67 -0
  17. devin_switch-0.1.0/src/devin_switch/profiles/corporate/mcp_config.json +15 -0
  18. devin_switch-0.1.0/src/devin_switch/profiles/corporate/profile.json +8 -0
  19. devin_switch-0.1.0/src/devin_switch/profiles/lab/User/settings.json +11 -0
  20. devin_switch-0.1.0/src/devin_switch/profiles/lab/config.json +50 -0
  21. devin_switch-0.1.0/src/devin_switch/profiles/lab/mcp_config.json +10 -0
  22. devin_switch-0.1.0/src/devin_switch/profiles/lab/profile.json +12 -0
  23. devin_switch-0.1.0/src/devin_switch/profiles/personal/User/settings.json +11 -0
  24. devin_switch-0.1.0/src/devin_switch/profiles/personal/config.json +82 -0
  25. devin_switch-0.1.0/src/devin_switch/profiles/personal/mcp_config.json +27 -0
  26. devin_switch-0.1.0/src/devin_switch/profiles/personal/profile.json +8 -0
  27. devin_switch-0.1.0/src/devin_switch/profiles.py +96 -0
  28. devin_switch-0.1.0/src/devin_switch/redact.py +152 -0
  29. devin_switch-0.1.0/src/devin_switch.egg-info/PKG-INFO +214 -0
  30. devin_switch-0.1.0/src/devin_switch.egg-info/SOURCES.txt +38 -0
  31. devin_switch-0.1.0/src/devin_switch.egg-info/dependency_links.txt +1 -0
  32. devin_switch-0.1.0/src/devin_switch.egg-info/entry_points.txt +2 -0
  33. devin_switch-0.1.0/src/devin_switch.egg-info/requires.txt +3 -0
  34. devin_switch-0.1.0/src/devin_switch.egg-info/top_level.txt +1 -0
  35. devin_switch-0.1.0/tests/test_cli.py +111 -0
  36. devin_switch-0.1.0/tests/test_lab_profile.py +143 -0
  37. devin_switch-0.1.0/tests/test_offline_core.py +67 -0
  38. devin_switch-0.1.0/tests/test_profiles.py +110 -0
  39. devin_switch-0.1.0/tests/test_redact.py +180 -0
  40. devin_switch-0.1.0/tests/test_switch.py +223 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Icaro0310
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.
@@ -0,0 +1,214 @@
1
+ Metadata-Version: 2.4
2
+ Name: devin-switch
3
+ Version: 0.1.0
4
+ Summary: Switch between Devin configuration profiles (hooks, MCP, models) with snapshot and verification.
5
+ Author: Icaro0310
6
+ License: MIT
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Provides-Extra: dev
11
+ Requires-Dist: pytest>=8; extra == "dev"
12
+ Dynamic: license-file
13
+
14
+ <div align="center">
15
+
16
+ # devin-switch — MOVED
17
+
18
+ **This repository was absorbed into the
19
+ [`devin-control`](https://github.com/Icaro0310/devin-control) monorepo.**
20
+
21
+ The code now lives at `packages/switch/`. `pip install devin-switch` / `uv tool install devin-switch` still installs the same package, now released from devin-control.
22
+
23
+ ```bash
24
+ # development moved
25
+ git clone https://github.com/Icaro0310/devin-control
26
+ cd devin-control/packages/switch
27
+ ```
28
+
29
+ The repository is archived; open issues and PRs belong to devin-control.
30
+ History remains readable here for reference.
31
+
32
+ </div>
33
+
34
+ ---
35
+
36
+ <details>
37
+ <summary>Original README (pre-archive)</summary>
38
+
39
+ <div align="center">
40
+
41
+ <a href="https://github.com/Icaro0310/devin-switch/actions/workflows/ci.yml"><img src="https://github.com/Icaro0310/devin-switch/actions/workflows/ci.yml/badge.svg" alt="ci"/></a>
42
+
43
+ <a href="https://scorecard.dev/viewer/?uri=github.com/Icaro0310/devin-switch"><img src="https://api.scorecard.dev/projects/github.com/Icaro0310/devin-switch/badge" alt="OpenSSF Scorecard"/></a>
44
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="License: MIT"/></a>
45
+ <a href="https://www.python.org/"><img src="https://img.shields.io/badge/python-3.10%2B-blue" alt="Python 3.10+"/></a>
46
+ <a href="https://github.com/Icaro0310/devin-switch"><img src="https://img.shields.io/github/stars/Icaro0310/devin-switch" alt="GitHub stars"/></a>
47
+ <a href="https://github.com/Icaro0310/devin-switch/commits/main"><img src="https://img.shields.io/github/last-commit/Icaro0310/devin-switch" alt="Last commit"/></a>
48
+ <a href="https://github.com/Icaro0310/awesome-devin"><img src="https://img.shields.io/badge/part%20of-devin--*-ecosystem-7c3aed" alt="devin-* ecosystem"/></a>
49
+ <a href="https://github.com/Icaro0310/devin-switch/issues"><img src="https://img.shields.io/badge/PRs-welcome-brightgreen" alt="PRs welcome"/></a>
50
+ </div>
51
+
52
+ <!-- DEVIN-ECO:BEGIN -->
53
+ > **Part of the [DEVIN ecosystem](https://github.com/Icaro0310/awesome-devin)**
54
+ > Track: Control · Nature: product
55
+ > For: Operations, Maintainers
56
+ > Interface: CLI
57
+ <!-- DEVIN-ECO:END -->
58
+
59
+
60
+ # devin-switch
61
+
62
+ > **Unofficial community project.** Not affiliated with, endorsed by, or
63
+ > sponsored by Cognition AI. "Devin" is a trademark of Cognition AI.
64
+
65
+ **[Linux](README.linux.md)** · **[Personal Windows](README.windows.md)** · **[Corporate Windows](README.corporate-windows.md)**
66
+
67
+ Part of the [awesome-devin](https://github.com/Icaro0310/awesome-devin) ecosystem: the curated hub for the devin-* tools.
68
+
69
+ `devin-switch` swaps between named **Devin configuration profiles** —
70
+ hooks, MCP servers, models, cascade rules, UI settings — with a verified
71
+ snapshot before every write and a journal you can roll back from.
72
+ **Dry-run by default**: nothing is ever written unless you pass
73
+ `--apply`.
74
+
75
+ ## The problem
76
+
77
+ Devin's behaviour lives in a handful of JSONC files spread across two
78
+ roots: `config.json` + `mcp_config.json` under the **data dir**
79
+ (`~/.config/devin`, `%APPDATA%/devin`) and `User/settings.json` under the
80
+ **UI config dir** (`~/.config/Devin`, `%APPDATA%/Devin`). Wanting a
81
+ locked-down "work" setup and a permissive "personal" setup means editing
82
+ the same files back and forth — by hand, with no undo.
83
+
84
+ `devin-switch` treats each variant as a declarative overlay directory and
85
+ does the swap safely.
86
+
87
+ ## Safety model
88
+
89
+ - **Dry-run by default** — `use` and `rollback` print a masked plan and
90
+ write *nothing* (not even the journal) without `--apply`.
91
+ - **Verified snapshot** — before any write, every file about to change is
92
+ copied into `.devin-ecosystem/switch-backups/<ts>/` with a sha256
93
+ manifest. The copies are re-hashed *and* compared against the live
94
+ sources; any mismatch aborts before a single byte is written.
95
+ - **Atomic writes** — sibling tmp file + `os.replace`; no half-written
96
+ config.
97
+ - **`credentials.toml` is never touched** — not read, not written, not
98
+ even opened for a hash. A profile that ships one gets a `skip`.
99
+ - **Secrets masked in all output** — credential-named files
100
+ (`.env*`, `*.pem`, `*secret*`, …) never have contents printed; inside
101
+ printable files, values under sensitive key names (`token`, `secret`,
102
+ `password`, `api_key`, `auth`…) or that look like tokens (long alnum
103
+ blobs, JWTs, `sk-`/`ghp_`/`xox` prefixes) render as `<redacted>`.
104
+ - **No network** — stdlib only, Python ≥ 3.10.
105
+
106
+ ## Install
107
+
108
+ Requires Python ≥ 3.10 and `pipx` or `uv`. Per-OS setup lives in the platform guides: [Linux](README.linux.md) · [Personal Windows](README.windows.md) · [Corporate Windows](README.corporate-windows.md).
109
+
110
+ <!-- DIST-STATUS:BEGIN — generated from devin-powerups/registry.json -->
111
+ > **Source-only distribution.** This tool is not yet published to PyPI.
112
+ > Install from source:
113
+ >
114
+ > ```bash
115
+ > pipx install git+https://github.com/Icaro0310/devin-switch.git
116
+ > # or
117
+ > uv tool install git+https://github.com/Icaro0310/devin-switch.git
118
+ > ```
119
+ <!-- DIST-STATUS:END -->
120
+
121
+ ## Usage
122
+
123
+ ```bash
124
+ devin-switch list # discovered profiles
125
+ devin-switch show personal # what a profile manages (keys, not values)
126
+ devin-switch diff corporate personal # masked diff between two profiles
127
+ devin-switch use personal # DRY-RUN: masked per-file plan
128
+ devin-switch use personal --apply # snapshot → verify → write → journal
129
+ devin-switch rollback # DRY-RUN: what would be restored
130
+ devin-switch rollback --apply # restore the last verified backup
131
+ devin-switch doctor # config sanity + closest profile
132
+ ```
133
+
134
+ Every command accepts `--data-dir`, `--config-dir` and `--profiles-dir`
135
+ to override the platform defaults (`--config-dir` falls back to
136
+ `--data-dir` for single-root layouts). The profiles dir resolves in
137
+ order: `--profiles-dir` → `$DEVIN_SWITCH_PROFILES_DIR` → `./profiles` →
138
+ the bundled examples.
139
+
140
+ Exit code is `0` on success/clean dry-run and `1` on errors — and for
141
+ `doctor`, on any `FAIL` check (like `devin-doctor`).
142
+
143
+ ## Profiles
144
+
145
+ A profile is a directory under `profiles/` whose files map 1:1 onto the
146
+ managed config space — see [`profiles/README.md`](profiles/README.md):
147
+
148
+ ```
149
+ profiles/
150
+ corporate/
151
+ profile.json # metadata only — never copied
152
+ config.json # → <data-dir>/config.json
153
+ mcp_config.json # → <data-dir>/mcp_config.json
154
+ User/settings.json # → <config-dir>/User/settings.json
155
+ personal/
156
+ ...
157
+ ```
158
+
159
+ Overlays are **whole-file replacements**, not merges — what the profile
160
+ ships is what the file becomes. Files are JSONC: `//` and `/* */`
161
+ comments allowed, trailing commas not.
162
+
163
+ ### The `lab` profile (G3 A/B runs)
164
+
165
+ `profiles/lab/` is a hermetic config for A/B skill evaluation: a pinned
166
+ model — `SWE-2-High` (free tier as of 2026-10-04), the same id in
167
+ `config.json`, `User/settings.json` and `profile.json`, **no hooks
168
+ and no MCP servers at all** — deliberate, since learning-loop /
169
+ prompt-logging hooks and the memory MCP would let attempt n learn from
170
+ attempt n-1 — plus `autoGenerateMemories` off. Label sessions `g3-ab`.
171
+ Caveat: it is still unconfirmed whether Devin can isolate the config dir
172
+ per workspace; if it can't, run attempts **serially** and let
173
+ `devin-switch` swap `lab` in/out between them (`rollback` restores the
174
+ previous bytes exactly). See `profiles/README.md`.
175
+
176
+ ## What happens on `--apply`
177
+
178
+ 1. **Plan** — per file: `create` / `modify` / `unchanged` / `skip`
179
+ (`credentials.toml`, unsafe paths).
180
+ 2. **Snapshot** — pre-switch bytes copied to
181
+ `~/.config/Devin/.devin-ecosystem/switch-backups/<timestamp>/` with a
182
+ `manifest.json` recording sha256 per file (`existed: false` for files
183
+ the switch would create).
184
+ 3. **Verify** — every copy must hash to its recorded sha256 *and* still
185
+ match the live source (catches a mid-switch edit). Any problem →
186
+ abort, nothing written, backup kept as evidence.
187
+ 4. **Write** — atomic tmp+rename per file.
188
+ 5. **Journal** — one JSON line appended to
189
+ `.devin-ecosystem/switch-journal.jsonl`:
190
+ `{ts, action, profile, files_changed, backup_dir}`.
191
+
192
+ `rollback` reads the newest `use` entry whose backup still exists,
193
+ restores the snapshot (and deletes files the switch created), then
194
+ appends a `rollback` entry — the backup is *not* consumed, so you can
195
+ switch forward again afterwards.
196
+
197
+ ## Development
198
+
199
+ ```bash
200
+ python -m pytest # 62 tests, all on synthetic tmp-dir fixtures
201
+ python -m devin_switch.cli doctor --profiles-dir profiles
202
+ ```
203
+
204
+ The test suite runs entirely against fake Devin roots — no real
205
+ installation is touched, and `credentials.toml` fixtures are only ever
206
+ checked by digest.
207
+
208
+ ## Related
209
+
210
+ - [`devin-doctor`](https://github.com/Icaro0310/devin-explore) — diagnoses
211
+ a Devin installation (read-only, always); `devin-switch doctor` covers
212
+ the config-file corner of that space, inline, with no dependency.
213
+
214
+ </details>
@@ -0,0 +1,201 @@
1
+ <div align="center">
2
+
3
+ # devin-switch — MOVED
4
+
5
+ **This repository was absorbed into the
6
+ [`devin-control`](https://github.com/Icaro0310/devin-control) monorepo.**
7
+
8
+ The code now lives at `packages/switch/`. `pip install devin-switch` / `uv tool install devin-switch` still installs the same package, now released from devin-control.
9
+
10
+ ```bash
11
+ # development moved
12
+ git clone https://github.com/Icaro0310/devin-control
13
+ cd devin-control/packages/switch
14
+ ```
15
+
16
+ The repository is archived; open issues and PRs belong to devin-control.
17
+ History remains readable here for reference.
18
+
19
+ </div>
20
+
21
+ ---
22
+
23
+ <details>
24
+ <summary>Original README (pre-archive)</summary>
25
+
26
+ <div align="center">
27
+
28
+ <a href="https://github.com/Icaro0310/devin-switch/actions/workflows/ci.yml"><img src="https://github.com/Icaro0310/devin-switch/actions/workflows/ci.yml/badge.svg" alt="ci"/></a>
29
+
30
+ <a href="https://scorecard.dev/viewer/?uri=github.com/Icaro0310/devin-switch"><img src="https://api.scorecard.dev/projects/github.com/Icaro0310/devin-switch/badge" alt="OpenSSF Scorecard"/></a>
31
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="License: MIT"/></a>
32
+ <a href="https://www.python.org/"><img src="https://img.shields.io/badge/python-3.10%2B-blue" alt="Python 3.10+"/></a>
33
+ <a href="https://github.com/Icaro0310/devin-switch"><img src="https://img.shields.io/github/stars/Icaro0310/devin-switch" alt="GitHub stars"/></a>
34
+ <a href="https://github.com/Icaro0310/devin-switch/commits/main"><img src="https://img.shields.io/github/last-commit/Icaro0310/devin-switch" alt="Last commit"/></a>
35
+ <a href="https://github.com/Icaro0310/awesome-devin"><img src="https://img.shields.io/badge/part%20of-devin--*-ecosystem-7c3aed" alt="devin-* ecosystem"/></a>
36
+ <a href="https://github.com/Icaro0310/devin-switch/issues"><img src="https://img.shields.io/badge/PRs-welcome-brightgreen" alt="PRs welcome"/></a>
37
+ </div>
38
+
39
+ <!-- DEVIN-ECO:BEGIN -->
40
+ > **Part of the [DEVIN ecosystem](https://github.com/Icaro0310/awesome-devin)**
41
+ > Track: Control · Nature: product
42
+ > For: Operations, Maintainers
43
+ > Interface: CLI
44
+ <!-- DEVIN-ECO:END -->
45
+
46
+
47
+ # devin-switch
48
+
49
+ > **Unofficial community project.** Not affiliated with, endorsed by, or
50
+ > sponsored by Cognition AI. "Devin" is a trademark of Cognition AI.
51
+
52
+ **[Linux](README.linux.md)** · **[Personal Windows](README.windows.md)** · **[Corporate Windows](README.corporate-windows.md)**
53
+
54
+ Part of the [awesome-devin](https://github.com/Icaro0310/awesome-devin) ecosystem: the curated hub for the devin-* tools.
55
+
56
+ `devin-switch` swaps between named **Devin configuration profiles** —
57
+ hooks, MCP servers, models, cascade rules, UI settings — with a verified
58
+ snapshot before every write and a journal you can roll back from.
59
+ **Dry-run by default**: nothing is ever written unless you pass
60
+ `--apply`.
61
+
62
+ ## The problem
63
+
64
+ Devin's behaviour lives in a handful of JSONC files spread across two
65
+ roots: `config.json` + `mcp_config.json` under the **data dir**
66
+ (`~/.config/devin`, `%APPDATA%/devin`) and `User/settings.json` under the
67
+ **UI config dir** (`~/.config/Devin`, `%APPDATA%/Devin`). Wanting a
68
+ locked-down "work" setup and a permissive "personal" setup means editing
69
+ the same files back and forth — by hand, with no undo.
70
+
71
+ `devin-switch` treats each variant as a declarative overlay directory and
72
+ does the swap safely.
73
+
74
+ ## Safety model
75
+
76
+ - **Dry-run by default** — `use` and `rollback` print a masked plan and
77
+ write *nothing* (not even the journal) without `--apply`.
78
+ - **Verified snapshot** — before any write, every file about to change is
79
+ copied into `.devin-ecosystem/switch-backups/<ts>/` with a sha256
80
+ manifest. The copies are re-hashed *and* compared against the live
81
+ sources; any mismatch aborts before a single byte is written.
82
+ - **Atomic writes** — sibling tmp file + `os.replace`; no half-written
83
+ config.
84
+ - **`credentials.toml` is never touched** — not read, not written, not
85
+ even opened for a hash. A profile that ships one gets a `skip`.
86
+ - **Secrets masked in all output** — credential-named files
87
+ (`.env*`, `*.pem`, `*secret*`, …) never have contents printed; inside
88
+ printable files, values under sensitive key names (`token`, `secret`,
89
+ `password`, `api_key`, `auth`…) or that look like tokens (long alnum
90
+ blobs, JWTs, `sk-`/`ghp_`/`xox` prefixes) render as `<redacted>`.
91
+ - **No network** — stdlib only, Python ≥ 3.10.
92
+
93
+ ## Install
94
+
95
+ Requires Python ≥ 3.10 and `pipx` or `uv`. Per-OS setup lives in the platform guides: [Linux](README.linux.md) · [Personal Windows](README.windows.md) · [Corporate Windows](README.corporate-windows.md).
96
+
97
+ <!-- DIST-STATUS:BEGIN — generated from devin-powerups/registry.json -->
98
+ > **Source-only distribution.** This tool is not yet published to PyPI.
99
+ > Install from source:
100
+ >
101
+ > ```bash
102
+ > pipx install git+https://github.com/Icaro0310/devin-switch.git
103
+ > # or
104
+ > uv tool install git+https://github.com/Icaro0310/devin-switch.git
105
+ > ```
106
+ <!-- DIST-STATUS:END -->
107
+
108
+ ## Usage
109
+
110
+ ```bash
111
+ devin-switch list # discovered profiles
112
+ devin-switch show personal # what a profile manages (keys, not values)
113
+ devin-switch diff corporate personal # masked diff between two profiles
114
+ devin-switch use personal # DRY-RUN: masked per-file plan
115
+ devin-switch use personal --apply # snapshot → verify → write → journal
116
+ devin-switch rollback # DRY-RUN: what would be restored
117
+ devin-switch rollback --apply # restore the last verified backup
118
+ devin-switch doctor # config sanity + closest profile
119
+ ```
120
+
121
+ Every command accepts `--data-dir`, `--config-dir` and `--profiles-dir`
122
+ to override the platform defaults (`--config-dir` falls back to
123
+ `--data-dir` for single-root layouts). The profiles dir resolves in
124
+ order: `--profiles-dir` → `$DEVIN_SWITCH_PROFILES_DIR` → `./profiles` →
125
+ the bundled examples.
126
+
127
+ Exit code is `0` on success/clean dry-run and `1` on errors — and for
128
+ `doctor`, on any `FAIL` check (like `devin-doctor`).
129
+
130
+ ## Profiles
131
+
132
+ A profile is a directory under `profiles/` whose files map 1:1 onto the
133
+ managed config space — see [`profiles/README.md`](profiles/README.md):
134
+
135
+ ```
136
+ profiles/
137
+ corporate/
138
+ profile.json # metadata only — never copied
139
+ config.json # → <data-dir>/config.json
140
+ mcp_config.json # → <data-dir>/mcp_config.json
141
+ User/settings.json # → <config-dir>/User/settings.json
142
+ personal/
143
+ ...
144
+ ```
145
+
146
+ Overlays are **whole-file replacements**, not merges — what the profile
147
+ ships is what the file becomes. Files are JSONC: `//` and `/* */`
148
+ comments allowed, trailing commas not.
149
+
150
+ ### The `lab` profile (G3 A/B runs)
151
+
152
+ `profiles/lab/` is a hermetic config for A/B skill evaluation: a pinned
153
+ model — `SWE-2-High` (free tier as of 2026-10-04), the same id in
154
+ `config.json`, `User/settings.json` and `profile.json`, **no hooks
155
+ and no MCP servers at all** — deliberate, since learning-loop /
156
+ prompt-logging hooks and the memory MCP would let attempt n learn from
157
+ attempt n-1 — plus `autoGenerateMemories` off. Label sessions `g3-ab`.
158
+ Caveat: it is still unconfirmed whether Devin can isolate the config dir
159
+ per workspace; if it can't, run attempts **serially** and let
160
+ `devin-switch` swap `lab` in/out between them (`rollback` restores the
161
+ previous bytes exactly). See `profiles/README.md`.
162
+
163
+ ## What happens on `--apply`
164
+
165
+ 1. **Plan** — per file: `create` / `modify` / `unchanged` / `skip`
166
+ (`credentials.toml`, unsafe paths).
167
+ 2. **Snapshot** — pre-switch bytes copied to
168
+ `~/.config/Devin/.devin-ecosystem/switch-backups/<timestamp>/` with a
169
+ `manifest.json` recording sha256 per file (`existed: false` for files
170
+ the switch would create).
171
+ 3. **Verify** — every copy must hash to its recorded sha256 *and* still
172
+ match the live source (catches a mid-switch edit). Any problem →
173
+ abort, nothing written, backup kept as evidence.
174
+ 4. **Write** — atomic tmp+rename per file.
175
+ 5. **Journal** — one JSON line appended to
176
+ `.devin-ecosystem/switch-journal.jsonl`:
177
+ `{ts, action, profile, files_changed, backup_dir}`.
178
+
179
+ `rollback` reads the newest `use` entry whose backup still exists,
180
+ restores the snapshot (and deletes files the switch created), then
181
+ appends a `rollback` entry — the backup is *not* consumed, so you can
182
+ switch forward again afterwards.
183
+
184
+ ## Development
185
+
186
+ ```bash
187
+ python -m pytest # 62 tests, all on synthetic tmp-dir fixtures
188
+ python -m devin_switch.cli doctor --profiles-dir profiles
189
+ ```
190
+
191
+ The test suite runs entirely against fake Devin roots — no real
192
+ installation is touched, and `credentials.toml` fixtures are only ever
193
+ checked by digest.
194
+
195
+ ## Related
196
+
197
+ - [`devin-doctor`](https://github.com/Icaro0310/devin-explore) — diagnoses
198
+ a Devin installation (read-only, always); `devin-switch doctor` covers
199
+ the config-file corner of that space, inline, with no dependency.
200
+
201
+ </details>
@@ -0,0 +1,29 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "devin-switch"
7
+ version = "0.1.0"
8
+ description = "Switch between Devin configuration profiles (hooks, MCP, models) with snapshot and verification."
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.10"
12
+ authors = [{ name = "Icaro0310" }]
13
+ dependencies = []
14
+
15
+ [project.optional-dependencies]
16
+ dev = ["pytest>=8"]
17
+
18
+ [project.scripts]
19
+ devin-switch = "devin_switch.cli:main"
20
+
21
+ [tool.setuptools.packages.find]
22
+ where = ["src"]
23
+
24
+ [tool.setuptools.package-data]
25
+ devin_switch = ["profiles/**"]
26
+
27
+ [tool.pytest.ini_options]
28
+ testpaths = ["tests"]
29
+ pythonpath = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,12 @@
1
+ """devin-switch — switch between Devin configuration profiles.
2
+
3
+ Manages the config files in the Devin config/data dirs (``User/settings.json``,
4
+ ``config.json`` hooks, ``mcp_config.json`` MCP entries) as declarative
5
+ profiles, with verified snapshots before any write and a rollback journal.
6
+
7
+ ``credentials.toml`` is never touched — not even read.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ __version__ = "0.1.0"