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.
- devin_switch-0.1.0/LICENSE +21 -0
- devin_switch-0.1.0/PKG-INFO +214 -0
- devin_switch-0.1.0/README.md +201 -0
- devin_switch-0.1.0/pyproject.toml +29 -0
- devin_switch-0.1.0/setup.cfg +4 -0
- devin_switch-0.1.0/src/devin_switch/__init__.py +12 -0
- devin_switch-0.1.0/src/devin_switch/cli.py +320 -0
- devin_switch-0.1.0/src/devin_switch/engine.py +256 -0
- devin_switch-0.1.0/src/devin_switch/health.py +255 -0
- devin_switch-0.1.0/src/devin_switch/journal.py +82 -0
- devin_switch-0.1.0/src/devin_switch/jsonc.py +60 -0
- devin_switch-0.1.0/src/devin_switch/paths.py +145 -0
- devin_switch-0.1.0/src/devin_switch/plan.py +220 -0
- devin_switch-0.1.0/src/devin_switch/profiles/README.md +63 -0
- devin_switch-0.1.0/src/devin_switch/profiles/corporate/User/settings.json +11 -0
- devin_switch-0.1.0/src/devin_switch/profiles/corporate/config.json +67 -0
- devin_switch-0.1.0/src/devin_switch/profiles/corporate/mcp_config.json +15 -0
- devin_switch-0.1.0/src/devin_switch/profiles/corporate/profile.json +8 -0
- devin_switch-0.1.0/src/devin_switch/profiles/lab/User/settings.json +11 -0
- devin_switch-0.1.0/src/devin_switch/profiles/lab/config.json +50 -0
- devin_switch-0.1.0/src/devin_switch/profiles/lab/mcp_config.json +10 -0
- devin_switch-0.1.0/src/devin_switch/profiles/lab/profile.json +12 -0
- devin_switch-0.1.0/src/devin_switch/profiles/personal/User/settings.json +11 -0
- devin_switch-0.1.0/src/devin_switch/profiles/personal/config.json +82 -0
- devin_switch-0.1.0/src/devin_switch/profiles/personal/mcp_config.json +27 -0
- devin_switch-0.1.0/src/devin_switch/profiles/personal/profile.json +8 -0
- devin_switch-0.1.0/src/devin_switch/profiles.py +96 -0
- devin_switch-0.1.0/src/devin_switch/redact.py +152 -0
- devin_switch-0.1.0/src/devin_switch.egg-info/PKG-INFO +214 -0
- devin_switch-0.1.0/src/devin_switch.egg-info/SOURCES.txt +38 -0
- devin_switch-0.1.0/src/devin_switch.egg-info/dependency_links.txt +1 -0
- devin_switch-0.1.0/src/devin_switch.egg-info/entry_points.txt +2 -0
- devin_switch-0.1.0/src/devin_switch.egg-info/requires.txt +3 -0
- devin_switch-0.1.0/src/devin_switch.egg-info/top_level.txt +1 -0
- devin_switch-0.1.0/tests/test_cli.py +111 -0
- devin_switch-0.1.0/tests/test_lab_profile.py +143 -0
- devin_switch-0.1.0/tests/test_offline_core.py +67 -0
- devin_switch-0.1.0/tests/test_profiles.py +110 -0
- devin_switch-0.1.0/tests/test_redact.py +180 -0
- 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,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"
|