·tips

Devin CLIで複数アカウント・orgを切り替える

XDG_DATA_HOME/XDG_CONFIG_HOMEを切り替えてDevin CLIの認証と設定をprofileごとに分離し、sessionsはsymlinkで共有する

4 min read
Writing style

まとめ

  • Devin CLIはXDGをそのまま読む。XDG_DATA_HOMEXDG_CONFIG_HOMEを切り替えれば認証・設定・sessionsをまるごと分離できる
  • DEVIN_PROFILEを読んでXDGを差し替えるwrapperをdevinという名前でPATH前方に置くと透過的に切り替わる
  • devin.org_idはuser config専用フィールドでenvやproject configから上書きできない。セッション開始時に書き戻されるので、複数orgに所属する同一アカウントでもprofileごとにorgをpinできる
  • sessionsを共有したい場合はcli/だけをsymlinkする。credentials.tomlmcp/oauth/は実ファイルのまま残す

ファイル配置

devin auth statusがcredentialsのパスを表示してくれる。

種別パスprofile間で
認証$XDG_DATA_HOME/devin/credentials.toml分離する
MCP OAuth$XDG_DATA_HOME/devin/mcp/oauth/分離される
sessions/transcripts/logs等$XDG_DATA_HOME/devin/cli/symlinkで共有可能
設定・hooks$XDG_CONFIG_HOME/devin/config.json分離する(devin.org_idを含むため)
グローバルskills等$XDG_CONFIG_HOME/devin/配下の他ファイルsymlinkで共有可能

profile切り替えwrapper

~/bin/opt/homebrew/binよりPATH前方にある前提で、~/bin/devinを置くとdevinを透過的にshadowできる。

#!/bin/sh
# DEVIN_PROFILE=<name> devin [...]
set -eu

self="$(cd "$(dirname "$0")" && pwd -P)/$(basename "$0")"
real="$(type -aP devin | grep -Fvx "$self" | head -n 1)"

case "${DEVIN_PROFILE:-default}" in
  default)
    exec "$real" "$@" ;;
  *)
    export XDG_DATA_HOME="$HOME/.local/share/devin-$DEVIN_PROFILE"
    export XDG_CONFIG_HOME="$HOME/.config/devin-$DEVIN_PROFILE"
    exec "$real" "$@" ;;
esac
devin                              # 今まで通り
DEVIN_PROFILE=work devin auth login  # 別アカウントでログイン
DEVIN_PROFILE=work devin auth status # Credentials pathが~/.local/share/devin-work/...ならOK

設定とsessionsの共有

config側はconfig.jsonだけがorg依存なので、それ以外をsymlinkする。

cd ~/.config/devin-work/devin
for f in AGENTS.md skills; do
  ln -sfn ~/.config/devin/$f .
done

data側はcredentials.tomlmcp/oauth/を残してcli/を丸ごとsymlinkすれば、sessions.db・transcripts・logsが共有され、どちらのprofileからも同じsession一覧が見える。

mv ~/.local/share/devin-work/devin/cli /tmp/devin-work-cli-bak 2>/dev/null
ln -sfn ~/.local/share/devin/cli ~/.local/share/devin-work/devin/cli

hooksだけはsymlinkできない

user-levelのhooksはconfig.jsonhooksキー内にあり、ファイル単位のsymlinkが効かない。hooksだけを流し込むスクリプトで吸収する(~/.local/bin/devin-sync-hooks):

#!/bin/sh
# devin-sync-hooks <profile config dir>
# 例: devin-sync-hooks ~/.config/devin-work/devin
set -eu

dst="${1:?usage: devin-sync-hooks <profile config dir>}"
src="$HOME/.config/devin/config.json"
target="$dst/config.json"
tmp="$target.tmp.$$"

mkdir -p "$dst"
[ -f "$target" ] || echo '{}' > "$target"

jq --argjson h "$(jq '.hooks' "$src")" '.hooks = $h' "$target" > "$tmp"
mv "$tmp" "$target"

注意点

  • devin.org_idauth loginではなくセッション開始時に書き戻される。未設定ならデフォルトorg(個人org)に解決される。config.jsonをsymlinkするとorg_idが上書き合戦になる
  • 共有sessions.dbは書き込み競合でdatabase is lockedになり得る。WALなのでretryすれば通る
  • read_config_fromはprofile側に引き継がれない。main側でproviderを絞っている場合はprofile側configにも書く
  • devinセッション内ではCHISEL_SESSION_DBがexport済みで、nestedでdevinを呼ぶと親のsessions.dbを継承する
  • WINDSURF_API_KEYはACP server向けでdevin authの認証面には効かない

ref