Part of the DEVIN ecosystem
Track: Control · Nature: product
For: Local-first ops, Maintainers, DevOps engineers
Interface: CLI
Path: DevOps engineers · step 3/3 — afterdevin-bridge
devin-switch
Unofficial community project. Not affiliated with, endorsed by, or sponsored by Cognition AI. "Devin" is a trademark of Cognition AI.
Linux · Personal Windows · Corporate Windows
Part of the awesome-devin ecosystem: the curated hub for the devin-* tools.
devin-switch swaps between named Devin configuration profiles —
hooks, MCP servers, models, cascade rules, UI settings — with a verified
snapshot before every write and a journal you can roll back from.
Dry-run by default: nothing is ever written unless you pass
--apply.
The problem
Devin's behaviour lives in a handful of JSONC files spread across two
roots: config.json + mcp_config.json under the data dir
(~/.config/devin, %APPDATA%/devin) and User/settings.json under the
UI config dir (~/.config/Devin, %APPDATA%/Devin). Wanting a
locked-down "work" setup and a permissive "personal" setup means editing
the same files back and forth — by hand, with no undo.
devin-switch treats each variant as a declarative overlay directory and
does the swap safely.
Safety model
- Dry-run by default —
useandrollbackprint a masked plan and write nothing (not even the journal) without--apply. - Verified snapshot — before any write, every file about to change is
copied into
.devin-ecosystem/switch-backups/<ts>/with a sha256 manifest. The copies are re-hashed and compared against the live sources; any mismatch aborts before a single byte is written. - Atomic writes — sibling tmp file +
os.replace; no half-written config. credentials.tomlis never touched — not read, not written, not even opened for a hash. A profile that ships one gets askip.- Secrets masked in all output — credential-named files
(
.env*,*.pem,*secret*, …) never have contents printed; inside printable files, values under sensitive key names (token,secret,password,api_key,auth…) or that look like tokens (long alnum blobs, JWTs,sk-/ghp_/xoxprefixes) render as<redacted>. - Fail-closed imports — profile archives are validated end to end
(schema, per-file sha256, path safety) before anything is written;
importnever overwrites an existing profile without--force. - No network — stdlib only, Python ≥ 3.10.
Install
Requires Python ≥ 3.10 and pipx or uv. Per-OS setup lives in the platform guides: Linux · Personal Windows · Corporate Windows.
Usage
devin-switch list # discovered profiles
devin-switch show personal # what a profile manages (keys, not values)
devin-switch diff corporate personal # masked diff between two profiles
devin-switch use personal # DRY-RUN: masked per-file plan
devin-switch use personal --apply # snapshot → verify → write → journal
devin-switch rollback # DRY-RUN: what would be restored
devin-switch rollback --apply # restore the last verified backup
devin-switch export personal # portable JSON archive to stdout
devin-switch export personal --out personal.json
devin-switch import personal.json # DRY-RUN: validate + preview only
devin-switch import personal.json --apply # create profiles/personal/
devin-switch import personal.json --apply --name copy --force
devin-switch doctor # config sanity + closest profile
Every command accepts --data-dir, --config-dir and --profiles-dir
to override the platform defaults (--config-dir falls back to
--data-dir for single-root layouts). The profiles dir resolves in
order: --profiles-dir → $DEVIN_SWITCH_PROFILES_DIR → ./profiles →
the bundled examples.
Exit code is 0 on success/clean dry-run and 1 on errors — and for
doctor, on any FAIL check (like devin-doctor).
Profiles
A profile is a directory under profiles/ whose files map 1:1 onto the
managed config space — see profiles/README.md:
profiles/
corporate/
profile.json # metadata only — never copied
config.json # → <data-dir>/config.json
mcp_config.json # → <data-dir>/mcp_config.json
User/settings.json # → <config-dir>/User/settings.json
personal/
...
Overlays are whole-file replacements, not merges — what the profile
ships is what the file becomes. Files are JSONC: // and /* */
comments allowed, trailing commas not.
Exporting and importing profiles
export serializes a profile to a single portable JSON document
(format: "devin-switch/profile-archive", schema_version: 1): each
managed file as {path, sha256, data} with base64 bytes, plus the
profile.json metadata as fields. With no --out it goes to stdout;
--out writes a file but never overwrites an existing one.
credentials.toml is excluded without being read — archives carry
secrets in the clear, so keep them private.
import is dry-run by default like use: it validates the archive and
previews the profile that would be created. --apply materializes
profiles/<name>/ (name from the archive or --name) via a staging dir
- rename, and journals an
importentry. Validation is fail-closed: unknownformat/schema_version, bad base64, sha256 mismatch, duplicate paths,../absolute/backslash/hidden/__pycache__path parts,profile.jsonorcredentials.tomlentries are all rejected before a single byte is written — archive paths can never escape the profiles dir. An existing profile is never replaced without--force.
The lab profile (G3 A/B runs)
profiles/lab/ is a hermetic config for A/B skill evaluation: a pinned
model — SWE-2-High (free tier as of 2026-10-04), the same id in
config.json, User/settings.json and profile.json, no hooks
and no MCP servers at all — deliberate, since learning-loop /
prompt-logging hooks and the memory MCP would let attempt n learn from
attempt n-1 — plus autoGenerateMemories off. Label sessions g3-ab.
Caveat: it is still unconfirmed whether Devin can isolate the config dir
per workspace; if it can't, run attempts serially and let
devin-switch swap lab in/out between them (rollback restores the
previous bytes exactly). See profiles/README.md.
What happens on --apply
- Plan — per file:
create/modify/unchanged/skip(credentials.toml, unsafe paths). - Snapshot — pre-switch bytes copied to
~/.config/Devin/.devin-ecosystem/switch-backups/<timestamp>/with amanifest.jsonrecording sha256 per file (existed: falsefor files the switch would create). - Verify — every copy must hash to its recorded sha256 and still match the live source (catches a mid-switch edit). Any problem → abort, nothing written, backup kept as evidence.
- Write — atomic tmp+rename per file.
- Journal — one JSON line appended to
.devin-ecosystem/switch-journal.jsonl:{ts, action, profile, files_changed, backup_dir}.
rollback reads the newest use entry whose backup still exists,
restores the snapshot (and deletes files the switch created), then
appends a rollback entry — the backup is not consumed, so you can
switch forward again afterwards.
Development
python -m pytest # 89 tests, all on synthetic tmp-dir fixtures
python -m devin_switch.cli doctor --profiles-dir profiles
The test suite runs entirely against fake Devin roots — no real
installation is touched, and credentials.toml fixtures are only ever
checked by digest.
Adapters (MCP server, Devin skill, plugin)
devin_switch.mcp_server exposes the read-only half of the CLI as MCP
tools (switch_list_profiles, switch_preview, switch_diff,
switch_status) via the devin-switch-mcp entry point
(pip install 'devin-switch[mcp]'). The write path stays CLI-only for
a human. Profile descriptions are passed through the same secret
masking the diff output uses before they reach the MCP client.
adapters/ is a self-contained Devin plugin root
(adapters/.devin-plugin/plugin.json + adapters/skills/devin-switch/SKILL.md),
installed with
devin plugins install Icaro0310/devin-control#packages/switch/adapters —
it ships in the repo, not inside the wheel. The manifest launches the
server through uvx --from 'devin-switch[mcp]' devin-switch-mcp, which
resolves from PyPI as of 0.1.1 (the 0.1.0 wheel predates both the
entry point and the mcp extra). On 0.1.0 or a source checkout, run a
source-installed devin-switch-mcp directly or point a local manifest
copy at the checkout (uvx --from './packages/switch[mcp]' devin-switch-mcp).
The same stdio server registers in other MCP hosts:
Zed — context_servers in settings.json:
{
"context_servers": {
"devin-switch": { "command": { "path": "devin-switch-mcp", "args": [] } }
}
}
JetBrains — Settings → Tools → AI Assistant → Model Context Protocol → Add:
{ "mcpServers": { "devin-switch": { "command": "devin-switch-mcp" } } }
Antigravity — ~/.gemini/config/mcp_config.json:
{ "mcpServers": { "devin-switch": { "command": "devin-switch-mcp" } } }
Related
devin-doctor— diagnoses a Devin installation (read-only, always);devin-switch doctorcovers the config-file corner of that space, inline, with no dependency.
Metadata
Release files for devin-switch 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| devin_switch-0.1.1.tar.gz | 53.8 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| devin_switch-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 98.8 kB
Release files / devin_switch-0.1.1.tar.gz
| Download URL | devin_switch-0.1.1.tar.gz |
|---|---|
| Size | 53.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
140973f3fd83652b480944a8ee9b34b06cbc1db9a0f2138c238da2dc8bc65a96
|
|
BLAKE2b-256 checksum How to use checksums |
2900277bdfc2dc154094b8f77a7b4298358fb9f8cfc5058f30963281cd946f54
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logRelease files / devin_switch-0.1.1-py3-none-any.whl
| Download URL | devin_switch-0.1.1-py3-none-any.whl |
|---|---|
| Size | 45.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9f85f94d139fafb215cc2e91948db4565f5e0e4c6c592cb176c03a1193f3be42
|
|
BLAKE2b-256 checksum How to use checksums |
e8d76958a12854075a850551e008102d04ac7df31a95b8d5841ea8c8deb1dc4d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logSecurity for devin-switch 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for devin_switch-0.1.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.