はじめに

こんにちは。グループIT推進本部の Roki です。

Claude CodeやOpenAI Codex CLIを日常的に使うようになると、コードだけでなくAIエージェントへ与える設定も開発環境の重要な一部になります。Claude CodeにはCLAUDE.mdやskillsやpoliciesがあり、Codex CLIにはAGENTS.mdやskillsやagentsがあります。利用するハーネスが増えるほど設定はホームディレクトリの各所に分散し、同じ指示を複数のファイルへコピーする場面も増えていきます。

一台のマシンだけで使う間は手作業でも大きな問題になりません。しかし会社と個人のPCで同じ設定を使いたい場合や、チームで有用なルールを共有したい場合には、どれが最新版なのか分からない、片方だけ修正される、セットアップ手順が人によって異なるといった問題が表面化します。

本記事ではdotfile管理ツールのchezmoiを土台にして、AIエージェントの共通設定を一つの原稿から複数のハーネスへ展開する方法を紹介します。あわせてAgent Package ManagerであるAPMとの役割分担、秘密情報を公開リポジトリへ混入させないための対策、運用中に発見したAPMの不具合が修正されるまでの経緯も扱います。

なお今回の構成は完成形ではなく、個人環境で運用しながら共有単位と責務の境界を探っている段階のものです。

AIエージェント設定の共有が難しくなった

AIエージェントの設定にはコード変更後に実行するコマンド、Gitの操作方針、レビュー時の観点、外部サービスへ変更を加える前の確認ルールなど、開発フローと安全性を左右する知識が含まれます。

Claude Code向けに書いたルールをCodex CLIでも使う最も単純な方法はCLAUDE.mdからAGENTS.mdへコピーすることです。しかしコピーした時点で同じ内容を持つ二つの独立したファイルが生まれ、片方だけを修正すれば設定はすぐにドリフトします。

必要だったのはすべてのツールを同じ形式へ統一することではなく、共通部分だけをSingle Source of Truthとして保持し、各ハーネス固有の入口から同じ原稿を呼び出す仕組みでした。さらにマシンごとの差分を吸収し、新しい環境でも再現可能な手順で展開する必要があります。そこでchezmoiを採用しました。

chezmoiを設定配布の土台にする

chezmoiはGoで実装されたdotfileマネージャです。管理対象のソースディレクトリと実際のホームディレクトリを分離し、Gitリポジトリに置いたsource stateからtarget stateを生成します。chezmoi initでリポジトリを初期化し、chezmoi applyで現在のマシンへ反映できるため、新しい環境でも同じ状態を再現しやすくなります[1]。

AIエージェント設定との相性が良いと感じたのはGo templateを利用できる点です。.tmplを持つファイルや.chezmoitemplates以下のファイルはテンプレートとして解釈され、OSやホスト名などの環境情報と独自のデータを使って出力を変えられます[2]。

今回の構成を単純化すると次のようになります。

.
├── dot_claude/
│   ├── CLAUDE.md.tmpl
│   ├── skills/
│   └── policies/
├── dot_codex/
│   ├── AGENTS.md.tmpl
│   ├── skills/
│   └── agents/
├── dot_apm/
├── .chezmoitemplates/
│   └── agent-instructions/base.md.tmpl
├── .chezmoiignore
├── .pre-commit-config.yaml
└── run_after_20_apm-install.sh.tmpl

source stateではdot_が先頭に付いた名前がtarget stateでは.に変換されます。したがってdot_claude~/.claudeへ、dot_codex~/.codexへ展開されます。

初回セットアップは次の二つのコマンドで完了します。

chezmoi init <dotfiles-repository>
chezmoi apply

chezmoi init --apply <dotfiles-repository>と一度に実行することもできます[3]。初めて使うリポジトリでは、先にchezmoi diffchezmoi apply --dry-run --verboseで変更内容を確認してから反映する方が安全です。

一つの原稿からCLAUDE.mdとAGENTS.mdを生成する

共通設定の中心は.chezmoitemplates/agent-instructions/base.md.tmplです。ここにコード設計、Git操作、レビュー、通知、ブラウザ操作など、Claude CodeとCodex CLIの双方で共有したい原則を集約します。

# 共通の開発ルール

エージェント固有の設定は `{{ .agent_home }}` を起点に参照してください

コードを変更した後は対象プロジェクトのformatterとlintとtestを実行してください
破壊的なGit操作や外部サービスへの書き込みは実行前に確認してください
レビューでは正しさだけでなく保守性と運用時の失敗方法も確認してください

Claude Code側のCLAUDE.md.tmplは共通テンプレートへClaude Codeのホームディレクトリを渡します。

{{ template "agent-instructions/base.md.tmpl"
  (dict "agent_home" (printf "%s/.claude" .chezmoi.homeDir))
}}

## Claude Code固有の設定

Claude Codeのhooksとpermissionsは `~/.claude/settings.json` を参照してください

Codex CLI側のAGENTS.md.tmplも同様です。

{{ template "agent-instructions/base.md.tmpl"
  (dict "agent_home" (printf "%s/.codex" .chezmoi.homeDir))
}}

## Codex CLI固有の設定

Codex用のagentsは `~/.codex/agents` を参照してください

この構成では共通ルールを修正する場所が一つになります。ハーネス固有の差分は薄いラッパー側へ残るため、無理に全設定を共通化する必要もありません。意味が同じルールだけを共有し、具体的なパスや機能名は引数として渡す形が扱いやすいと感じています。

テンプレートはchezmoi execute-templatechezmoi catで適用前に確認できます[2]。長い指示は小さな構文ミスでも全体が生成できなくなるため、変更時に出力を確認する習慣を持つと安全です。

symlinkテンプレートでskillsを共有する

skillsの中にはClaude CodeとCodex CLIの双方でほぼ同じ内容を使えるものがあります。この場合もファイルを二重管理せず、片方を正本としてもう片方からシンボリックリンクで参照します。

たとえばClaude Code側の~/.claude/skills/review/SKILL.mdを正本とする場合、Codex CLI側には次のsymlinkテンプレートを置けます。

# dot_codex/skills/review/symlink_SKILL.md.tmpl
{{ .chezmoi.homeDir }}/.claude/skills/review/SKILL.md

chezmoiではsymlink_で始まるsource fileの内容がリンク先として解釈され、.tmplを付ければマシンごとのパスも解決できます[4]。絶対パスを直接書かず.chezmoi.homeDirを使うことで、ユーザー名が異なる環境にも展開できます。

ただしすべてのskillが完全に互換とは限りません。利用可能なツールやメタデータの仕様が異なる場合は、共通の本文だけをテンプレート化し、ハーネス固有の情報を各ファイルへ残す方が安全です。

APMとchezmoiの責務を分ける

AIエージェント向けの設定を扱うツールとしてAPMも利用しています。APMはinstructions、skills、agents、hooks、commandsなどのagent primitivesをパッケージとして解決し、対象のハーネスへ展開します。現在は依存関係の解決、lockfile、セキュリティスキャン、複数ターゲットへの配置までを担います[5]。

今回の構成ではchezmoiをホームディレクトリ全体の土台とし、自作の指示や非公開のポリシー、各ツールの設定、マシン差分、暗号化、セットアップスクリプトを管理します。一方で公開パッケージとして再利用できるskillsやinstructionsはAPMで管理します。chezmoiが環境のsource of truthであり、APMがagent primitivesのpackage layerです。

chezmoiのrun_after_スクリプトを使うと、chezmoi applyの後にAPMを自動実行できます。スクリプトはbeforeafteronceonchangeといった属性で実行条件を制御できます[4]。

#!/bin/sh
set -eu

if command -v apm >/dev/null 2>&1; then
  apm install --global

  # グローバルなroot context fileを生成する場合だけ実行する
  apm compile --global
fi

現行のAPMではapm install --globalapm compile --globalは別の処理です。グローバルに導入したinstructionsから~/.claude/CLAUDE.md~/.codex/AGENTS.mdを生成する場合は後者を明示的に実行します[6]。これらのファイルをchezmoi自身が生成する構成なら、同じファイルを両方のツールへ所有させないよう整理する必要があります。

秘密情報を公開リポジトリへ持ち込まない

dotfilesにはAPIキーやアクセストークンや社内固有のURLが混入しやすく、AIエージェント設定では外部ツールの接続情報や実行権限も扱います。

.chezmoiignoreは管理対象や展開対象からファイルを除外するために有効ですが、秘密情報を暗号化する仕組みではありません。生成物やnode_modules.DS_Store、特定マシンにだけ必要なファイルの除外に使い、秘密情報そのものは平文でリポジトリへ置かない運用が必要です[7]。

ファイルとして管理する必要がある場合、chezmoiはageやGPGなどによる暗号化に対応しています[8]。1Passwordなどのパスワードマネージャからテンプレート適用時に値を取得することもできます[9]。公開リポジトリへ置くのは秘密情報ではなく、取得方法を記述したテンプレートだけにします。

さらにcommit前の検査としてsecretlintを利用しています。.pre-commit-config.yamlにsecretlintのhookを設定し、pre-commit互換のhook managerであるprekから実行します。prekは単一バイナリで動作し、既存のpre-commit設定を利用できます[10]。

prek install
prek run --all-files

不要なファイルを管理対象から外し、秘密値は暗号化またはパスワードマネージャへ分離し、commit直前にsecretlintで検査します。公開してはいけない高権限のskillや社内専用のpolicyは、公開dotfilesとは別のprivate repositoryで管理する方が境界を明確にできます。

実際に使って便利だった点

共通テンプレートを一度変更してchezmoi applyすれば、Claude CodeとCodex CLIの双方へ同じ内容が展開されます。片方を更新してもう片方を忘れる事故を減らせました。

新しいマシンではリポジトリを初期化してapplyするだけで、各ハーネスのディレクトリ、共通指示、skillsのsymlink、APMのパッケージまで順番に構成されます。ホームディレクトリは.chezmoi.homeDirで解決でき、OSやホスト名による差分もGo templateへ閉じ込められます。

またsource stateとtarget stateが分離されているため、chezmoi diffでホームディレクトリに何が変更されるかを確認できます。AIエージェント設定は権限や実行可能なコマンドに影響するため、反映前に差分を確認できることには大きな意味があります。

一方で自動化を増やすほど、chezmoiのテンプレート展開、symlink、run script、APMのパッケージ解決、各ハーネスの読み込みという複数の層を追う必要があります。次の不具合も各段階を分けて調べたことで原因へ到達できました。

APMの不具合を切り分けてアップストリームへ返した話

この構成を作っていた当時、APM 0.8.12でapm install --global --target claude -vを実行するとIntegrating local .apm/ content...の表示から長時間進まない問題に遭遇しました。

最初は自分のdotfiles構成か~/.claude以下のファイル数が原因だと考えました。そこで一時的なHOMEを作り、~/.apmだけを置いた環境、~/.apm~/.claudeを置いた環境、さらに~/.codexまで追加した環境で同じコマンドを実行しました。いずれも数秒で完了し、実HOMEでだけ処理が長時間化しました。計測値は約1.7秒、約4.1秒、約3.3秒でした[11]。

macOSでプロセスをsampleすると、ネットワーク待ちではなくos.scandirを繰り返していました。ソースコードを追うとuser scopeではdeploy rootがPath.home()になり、~/.apmが存在するとlocal integrationへ入り、primitive discoveryがHOME全体を再帰的に探索し得る流れになっていました。~/.claudeが直接の原因ではなく、実HOMEに存在する大量のディレクトリを広く走査していたことが本質でした。

再現手順、計測結果、再現しなかった条件、ソースコード上の仮説をmicrosoft/apmのissue #830として報告しました[11]。その後PR #850で修正され、2026年4月26日リリースのAPM 0.9.3へ取り込まれています[12][13]。

修正ではuser scopeのPackageInfo.install_pathを変更せず、link resolverがprimitiveを探索するscan rootだけを$HOMEから~/.apmへ絞っています。install_path自体を~/.apmへ変えると、既存のintegratorが<install_path>/.apm/...を参照して~/.apm/.apm/...を探す可能性があるためです。あわせてHOMEの場合にdiscover_primitives~/.apmが渡ることを確認する回帰テストも追加されました[12]。

現在この問題は修正済みです。APM 0.9.3以降を利用していれば、issue #830を理由に--globalを避ける必要はありません。スライド発表時点では未解決の課題として紹介しましたが、本記事ではアップストリームへの報告から修正とリリースまで進んだ事例として位置づけています。

この経験から、外部ツールとの連携で処理が止まった時は設定を闇雲に減らすより、縮小したHOMEで再現性を比較し、CPU sampleとファイルシステムアクセスを確認し、想定されるscopeと実際の探索範囲を照合する方が原因へ近づきやすいと分かりました。

まとめ

AIエージェントの設定は、使うツールが増えるほどコピーでは維持できなくなります。chezmoiのsource stateとGo templateを使えば、共通ルールを一つの原稿に集約しながら、Claude CodeのCLAUDE.mdとCodex CLIのAGENTS.mdへ必要な形で展開できます。互換性のあるskillsはsymlinkテンプレートで共有でき、run scriptを使えばAPMのパッケージ導入までセットアップへ組み込めます。

一方で設定共有にはセキュリティと所有権の設計が必要です。chezmoiとAPMのどちらが各ファイルを生成するのかを決め、秘密情報は平文でリポジトリへ置かず、暗号化やパスワードマネージャとcommit前検査を組み合わせます。

現時点では個人環境を中心とした構成ですが、今後は共有可能なskillsやpoliciesを増やし、チーム内で設定変更をreviewして配布できるワークフローへ発展させたいと考えています。AIエージェントの能力だけでなく、その能力をどの環境でも安全かつ再現可能に引き出す設定基盤も継続的に整備していきます。

参考文献

– [1] chezmoi公式ドキュメント「What does chezmoi do?
– [2] chezmoi公式ドキュメント「Templating
– [3] chezmoi公式ドキュメント「init」および「apply
– [4] chezmoi公式ドキュメント「Target types
– [5] Agent Package Manager公式ドキュメント「apm install
– [6] Agent Package Manager公式ドキュメント「apm compile
– [7] chezmoi公式ドキュメント「Special files
– [8] chezmoi公式ドキュメント「Encryption
– [9] chezmoi公式ドキュメント「Password Manager Integration
– [10] prek GitHub Repository  および  secretlint GitHub Repository
– [11] microsoft/apm Issue #830「User-scope install (--global) unexpectedly enters local .apm integration and can spend a long time scanning $HOMEhttps://github.com/microsoft/apm/issues/830
– [12] microsoft/apm Pull Request #850「fix(install): scope local content scan to ~/.apm/ at user scope (#830)
– [13] Agent Package Manager CHANGELOG v0.9.3