-
差分確認
diff -rq ~/.claude/rules/ ~/git/dotfiles/claude/rules/
diff -rq ~/.claude/skills/ ~/git/dotfiles/claude/skills/
diff -rq ~/.claude/agents/ ~/git/dotfiles/claude/agents/
diff ~/.claude/CLAUDE.md ~/git/dotfiles/claude/CLAUDE.md
diff ~/.claude/AGENTS.md ~/git/dotfiles/shared/common-agents.md
diff ~/.claude/statusline.py ~/git/dotfiles/claude/statusline.py
diff -rq ~/.claude/hooks/ ~/git/dotfiles/claude/hooks/
diff <(jq '{permissions: {allow, deny, additionalDirectories}, model, language, outputStyle, voiceEnabled, spinnerVerbs, autoUpdatesChannel, attribution, effortLevel, hooks}' ~/.claude/settings.json) <(jq '{permissions: {allow, deny, additionalDirectories}, model, language, outputStyle, voiceEnabled, spinnerVerbs, autoUpdatesChannel, attribution, effortLevel, hooks}' ~/git/dotfiles/claude/settings.json 2>/dev/null || echo '{}')
diff <(jq -r '.mcpServers | keys[]' ~/.claude.json) <(jq -r '.mcpServers | keys[]' ~/git/dotfiles/.claude.json)
💡 上記 diff で > 行(リポジトリにのみ存在)が出たら、それは Push で削除されるサーバー。意図した削除なら進める、意図しない削除(他Macで追加されたばかり)なら一度 Pull してから Push し直す。
-
同期実行
rsync -av --delete ~/.claude/rules/ ~/git/dotfiles/claude/rules/
rsync -av --delete ~/.claude/skills/ ~/git/dotfiles/claude/skills/
rsync -av --delete ~/.claude/agents/ ~/git/dotfiles/claude/agents/
cp ~/.claude/CLAUDE.md ~/git/dotfiles/claude/
cp ~/.claude/AGENTS.md ~/git/dotfiles/shared/common-agents.md
cp ~/.claude/statusline.py ~/git/dotfiles/claude/
rsync -av --delete ~/.claude/hooks/ ~/git/dotfiles/claude/hooks/
-
settings.json同期(permissions の双方向マージ + その他フィールド上書き)
LOCAL_USER=$(whoami)
jq -s --arg u "$LOCAL_USER" '
.[0] as $repo |
.[1] as $local |
($repo * {
permissions: {
allow: (
($local.permissions.allow +
($repo.permissions.allow | map(gsub("/Users/[^/]+/"; "/Users/\($u)/"))))
| unique
),
deny: ($local.permissions.deny // []),
additionalDirectories: (
($local.permissions.additionalDirectories // []) +
(($repo.permissions.additionalDirectories // []) | map(gsub("/Users/[^/]+/"; "/Users/\($u)/")))
) | unique
},
model: $local.model,
language: $local.language,
outputStyle: $local.outputStyle,
voiceEnabled: $local.voiceEnabled,
spinnerVerbs: $local.spinnerVerbs,
autoUpdatesChannel: $local.autoUpdatesChannel,
attribution: $local.attribution,
effortLevel: $local.effortLevel,
hooks: $local.hooks
}) | with_entries(select(.value != null))
' ~/git/dotfiles/claude/settings.json \
~/.claude/settings.json > /tmp/settings.json && \
mv /tmp/settings.json ~/git/dotfiles/claude/settings.json
-
mcpServers同期(明示削除モード + 機密情報マスク)
jq -s '
.[0] as $repo |
(.[1].mcpServers // {}) as $local |
($repo | .mcpServers = $local) |
.mcpServers |= walk(
if type == "object" then
with_entries(
if (.key | test("TOKEN|KEY|SECRET"; "i")) and (.value | type == "string")
then .value = "<MASKED>"
else .
end
)
else .
end
)
' ~/git/dotfiles/.claude.json ~/.claude.json > /tmp/.claude.json && \
mv /tmp/.claude.json ~/git/dotfiles/.claude.json
-
コミット・プッシュ(自動実行)
差分確認なしで自動コミット&プッシュする(同期による変更は想定内のため)。
⚠️ git add -A は絶対に使わない。~/git/dotfiles/ を含むリポジトリで、dotfiles/ はその中のサブディレクトリ(兄弟に docs/ notes/ marp/ qiita/ 等がある)。git add -A はカレントディレクトリではなくリポジトリ全体を対象にするため、dotfiles/ 外の未コミット変更まで巻き込んでこの「設定同期」コミットに混入する(過去にこの事故が発生)。cd 済みなので git add -- .(=カレント dotfiles/ 以下のみ)を使うこと。
cd ~/git/dotfiles
git add -- .
git commit -m "設定同期"
git push
-
リポジトリを最新化
cd ~/git/dotfiles
git pull
-
差分確認
diff -rq ~/git/dotfiles/claude/rules/ ~/.claude/rules/
diff -rq ~/git/dotfiles/claude/skills/ ~/.claude/skills/
diff -rq ~/git/dotfiles/claude/agents/ ~/.claude/agents/
diff ~/git/dotfiles/claude/CLAUDE.md ~/.claude/CLAUDE.md
diff ~/git/dotfiles/shared/common-agents.md ~/.claude/AGENTS.md
diff ~/git/dotfiles/claude/statusline.py ~/.claude/statusline.py
diff -rq ~/git/dotfiles/claude/hooks/ ~/.claude/hooks/
diff <(jq '{permissions: {allow, deny, additionalDirectories}, model, language, outputStyle, voiceEnabled, spinnerVerbs, autoUpdatesChannel, attribution, effortLevel, hooks}' ~/git/dotfiles/claude/settings.json 2>/dev/null || echo '{}') <(jq '{permissions: {allow, deny, additionalDirectories}, model, language, outputStyle, voiceEnabled, spinnerVerbs, autoUpdatesChannel, attribution, effortLevel, hooks}' ~/.claude/settings.json)
-
同期実行(ユーザー確認後)
rsync -av --delete ~/git/dotfiles/claude/rules/ ~/.claude/rules/
rsync -av --delete ~/git/dotfiles/claude/skills/ ~/.claude/skills/
rsync -av --delete ~/git/dotfiles/claude/agents/ ~/.claude/agents/
cp ~/git/dotfiles/claude/CLAUDE.md ~/.claude/
cp ~/git/dotfiles/shared/common-agents.md ~/.claude/AGENTS.md
cp ~/git/dotfiles/claude/statusline.py ~/.claude/
rsync -av --delete ~/git/dotfiles/claude/hooks/ ~/.claude/hooks/
-
settings.json適用(permissions の双方向マージ + その他フィールド上書き)
LOCAL_USER=$(whoami)
jq -s --arg u "$LOCAL_USER" '
.[0] as $local |
.[1] as $repo |
($local * {
permissions: {
allow: (
($local.permissions.allow +
($repo.permissions.allow | map(gsub("/Users/[^/]+/"; "/Users/\($u)/"))))
| unique
),
deny: ($repo.permissions.deny // []),
additionalDirectories: (
($local.permissions.additionalDirectories // []) +
(($repo.permissions.additionalDirectories // []) | map(gsub("/Users/[^/]+/"; "/Users/\($u)/")))
) | unique
},
model: $repo.model,
language: $repo.language,
outputStyle: $repo.outputStyle,
voiceEnabled: $repo.voiceEnabled,
spinnerVerbs: $repo.spinnerVerbs,
autoUpdatesChannel: $repo.autoUpdatesChannel,
attribution: $repo.attribution,
effortLevel: $repo.effortLevel,
hooks: $repo.hooks
}) | with_entries(select(.value != null))
' ~/.claude/settings.json \
~/git/dotfiles/claude/settings.json > /tmp/settings.json && \
mv /tmp/settings.json ~/.claude/settings.json
-
mcpServers適用(半自動 + 手動)
5-1. 新規サーバーの構造をマージ(自動)
jq -s '
.[0] as $local |
.[1] as $repo |
$local * {
mcpServers: (
$local.mcpServers +
($repo.mcpServers | with_entries(
select(
(.key as $k | $local.mcpServers | has($k) | not)
)
))
)
}
' ~/.claude.json ~/git/dotfiles/.claude.json > /tmp/.claude.json && \
mv /tmp/.claude.json ~/.claude.json
5-2. パスの修正(Mac 2台対応)
jq '.mcpServers["google-workspace"].command = "/Users/minorun365/.local/bin/workspace-mcp"' \
~/.claude.json > /tmp/.claude.json && \
mv /tmp/.claude.json ~/.claude.json
5-3. 機密情報を1Passwordから注入
1Password(my.1password.com)の Private vault に保管している secret を op 経由で取得して、必要な MCP にだけ注入する。事前にデスクトップアプリの設定 → Developer で「Integrate with 1Password CLI」を有効化しておくこと(Touch ID で op が動作するようになる)。
現行方針では、GitHub / Notion を raw MCP として再追加しない。コネクタで賄える SaaS は Claude Code Connect / コネクタを使う。
mkdir -p ~/.config/google-workspace-mcp
op read "op://Private/<アイテムID>/credentials" \
--account my.1password.com \
> ~/.config/google-workspace-mcp/gcp-oauth.keys.json
chmod 600 ~/.config/google-workspace-mcp/gcp-oauth.keys.json
CLIENT_SECRET=$(jq -r '.installed.client_secret' \
~/.config/google-workspace-mcp/gcp-oauth.keys.json)
jq --arg s "$CLIENT_SECRET" \
'.mcpServers["google-workspace"].env.GOOGLE_OAUTH_CLIENT_SECRET = $s' \
~/.claude.json > /tmp/.claude.json && mv /tmp/.claude.json ~/.claude.json
1Password に保管している secret アイテム一覧
| アイテム名 | アイテムID | フィールド | 用途 |
|---|
Google Workspace MCP OAuth (Claude Code) | <アイテムID> | credentials | google-workspace MCP の OAuth credentials JSON 全文 |
- 新しい secret を追加するときは、vault に保管 → ID を記録 → このスキルに注入コマンドを追記 の順で運用する
- GitHub / Notion 等の raw MCP secret 注入コマンドは追加しない。必要なら Connect / コネクタ側で認証する
- アイテムタイトルに
( ) が含まれる場合や、フィールド名に日本語/記号が含まれる場合は Secret reference URL(op://...)が使えないので、op item get <ID> --fields label="..." --reveal で取得する
- 詳細手順は
/kb-google-workspace-mcp 等の関連スキル参照