🍎

dotfiles を「開発ハーネス」として育ててみた

に公開

Highlights

  • dotfiles を設定置き場ではなく、自分用の開発ハーネスとして扱ってみた話です
  • 好きな環境の再現性を高めつつ、流用しやすい形を目指しました
  • 流行りの AI(Claude Code)も試しに組み込んで、楽しく学びながら進めてみました
  • 最適解や完成形よりも、自分のスコープで納得できる進め方を優先しています
  • 最終的に「愛着をもてるかどうか」を一つの判断基準にしています

Context

PC を乗り換えたり、環境を作り直したりするたびに、「また同じ設定を入れ直しているな」と感じることが増えてきました。そのたびに「あれ、どうやったっけ?」はありがちな話…。

dotfiles は以前から管理していましたが、単に設定ファイルを置いておくだけではなく、もう少し"使い回しが効く形"にできないかと考えるようになりました。

殊、10年以上ぶりに MacBook を新調したことも大きなきっかけでした。M4チップに変わったことで、日常の体験が一段変わりました。macOS をもっと活用していけるように、慣れ親しんでいく過程と並行して進めました。

この記事は、
チュートリアルやステップバイステップのセットアップガイドではありません。自分なりに試行錯誤した結果を、そのまま記録したものです。

Overview

この記事で書いているのは、
dotfiles と AI を使って、自分が気持ちよく使い続けられる開発ハーネスを作ってみた話です。

  • 流行っているツールをすべて取り入れること
  • できるだけ汎用的・完璧な構成にすること

こうした方向は、今回はあえて目指していません。

まずは自分のスコープで、自分の勉強のために、「こうだったら使いやすい」と思う形を素直に作ってみることを優先しました。

chezmoi や rulesync などの既存ツールを使わなかったのも、今の自分には少しオーバースペックに感じたためです。

Design Decisions

dotfiles を「ハーネス」として扱う

dotfiles を単なる設定ファイルの集まりではなく、日々の作業を支える"開発ハーネス"として扱うことにしました。

  • 何度でも再利用できる
  • 部分的に流用できる
  • 壊れても直しやすい

こうした性質を重視しています。

たとえば、シェル設定を複数のソースファイルに分けておき、ビルド時に結合して一つの生成ファイルを作る形にしました。複数のファイルを source するよりも、結合された単一のファイルをsource する方が、生成物を見れば完全な状態がわかるため、壊れても原因を特定しやすいと考えました。

たとえば、以下のようなビルド関数を使っています。

concatenate_files_with_shebang() {
    local output_file="$1"
    local shebang="$2"
    shift 2
    local input_files=("$@")
    echo "$shebang" > "$output_file"
    cat "${input_files[@]}" >> "$output_file"
}

これで shell.common + shell.bashbashrc.generated のように結合しています。

Single Source of Truth を保つための工夫

設定や判断が分散すると、自分でも把握できなくなります。AI を使うようになると、なおさらです。

そのため、「どこを見れば正しい状態が分かるか」はなるべく一つに絞るようにしました。

dotfiles 中に、別プロジェクトを立ち上げるテンプレートも包含させたのですが、エージェント定義は dot.agent/ ディレクトリにまとめています。Claude Code、Cursor、Codex を併用していますが、どこを直せばいいか迷わなくなりました。

具体的には、2段階の構造になっています。

Step 1: dotfiles にテンプレートを用意

dotfiles/templates/project/
├── dot.agent/                  # 'dot.' prefix = デプロイ後に '.' になる
│   ├── subagents/
│   │   ├── analyst.md
│   │   ├── architect.md
│   │   ├── builder.md
│   │   └── reviewer.md
│   ├── skills/
│   └── commands/
├── dot.devcontainer/           # DevContainer 設定
└── Makefile                    # sync コマンド含む

Step 2: 下流プロジェクトにデプロイ → make sync

my-new-project/                 # テンプレートからコピー後
├── .agent/                     # dot.agent/ → .agent/ に変換済み
│   ├── subagents/
│   └── ...

│   ↓ make sync

├── .claude/agents/             # Claude Code (symlinks)
├── .cursor/rules/agents/       # Cursor IDE (symlinks)
└── .codex/AGENTS.override.md   # Codex (generated)

この構造により、dotfiles を「開発ハーネス」として位置づけることで、自分の環境設定だけでなく、プロジェクトの立ち上げ基盤としても活用できるようになりました。

エージェントの設定と Sync はまだまだ改善の余地がありますが、それぞれの仕様を踏まえた対応が必要なのもあって、自分の中で理解と整理が進みました。最初はちゃんと設定を認識させられないなど失敗がつきものでした。AI の指示通りに書いた構文がエラーになった時期もありました。

統一しすぎない、という判断

macOS、Linux、シェル、ツールごとに違いはあります。
無理に完全統一するよりも、違いを前提にしてフォールバックできる形を選びました。

GNU grep と BSD grep の違いを吸収するために、起動時に一度だけ利用可能なコマンドを検出し、変数に格納しています。すべての環境で同じ体験を目指すより、最低限動く状態を保証するほうが現実的でした(ちなみにまだ macOS 以外でのテストは十分ではありません)。

実際のコードはこのようになっています。

grep_command="grep ${grep_options:-}"

if command -v ggrep >/dev/null 2>&1; then
  alias grep='ggrep'
  grep_command="ggrep ${grep_options:-}"
elif command -v /opt/homebrew/bin/grep >/dev/null 2>&1; then
  alias grep='/opt/homebrew/bin/grep'
  grep_command="/opt/homebrew/bin/grep ${grep_options:-}"
fi
export grep_command

Implementation Notes

リポジトリの中では、スクリプトや設定を役割ごとに分けています。

  • 環境依存の処理はできるだけ外に出す
  • 共通化できる部分だけを中心に据える
  • AI が触っても壊れにくい形を意識する

コードそのものよりも、「なぜこの場所に置いたか」を自分で説明できることを重視しました。

セットアップスクリプト間で重複していた処理(プラットフォーム検出、シンボリックリンク作成、バックアップ)は共通ライブラリに抽出しました。同じロジックが複数箇所にあると、AI に修正を頼んだとき片方だけ直されて不整合が起きるためです。

What Worked / What Didn't

What Worked

  • 自分の環境を短時間で再現できるようになりました
  • 設定を触る心理的ハードルが下がりました
  • AI にリファクタリングや整理を任せやすくなりました

エージェントについては「analyst / architect / builder / reviewer」の4つに分けたことで、どの役割に何を頼むか明確になりました。特に builder と reviewer を分けたことで、実装と検証が混在しなくなったのは良かったです(reviewer が完全に read-only になりきれていないのは改善の余地ありです)。色々と記事やウェビナーを参考にしつつ、消費トークンを抑えたかったので、まずはシンプルに4つのエージェントに絞りました。Explore や Plan も Built-in でありますが、ここは自分でも定義して動かしてみる、というハンズオンを重視しました。

What Didn't

  • 最初から綺麗に設計するのは無理でした
  • 共通化しすぎて、逆に分かりにくくなった部分もあります
  • AI に任せすぎて、意図が薄れたこともありました

Demonstration

1つのマイルストーンとして、ひとまずスムーズに AI 駆動開発への動線を作った風になりました。dotfiles 中の templates からプロジェクトを作成して始めるハーネスです。

https://www.youtube.com/watch?v=i9iU3asN2IE

Repository:
https://github.com/ashmuk/dotfiles

Lessons Learned

  • dotfiles は「完成させるもの」ではなく「育てるもの」だと改めて感じました
  • AI は設計や整理の相棒にはなりますが、判断の代わりにはなりません。ただ、終始良き壁打ちの相手でした
  • 自分が理解できない構成は、いずれ放置されます
  • 愛着をもてるかどうかは、意外と重要な設計要件でした

Claude Code との協働では、私が意図・設計方針・レビューを示し、判断を下していました。Claude Code はこれらのプロセスを加速しつつ、主に実装と実行を担当してくれています。シェルスクリプトの解析やリファクタリングは特に得意で、私の方針に沿った形で素早く形にしてくれました。また、新しいものや見過ごしがちなポイントを AI はよく支援してくれました。

Who This Is (and Isn't) For

This is for:

  • dotfiles をすでに管理している人
  • 自分の環境に手を入れるのが嫌いではない人
  • AI を試しつつ、振り回されすぎたくない人

This is not for:

  • 最短手順だけを知りたい人
  • 完成されたテンプレートを探している人
  • ツール比較やベストプラクティスを期待している人

Closing Thoughts

「時代」を学ぶ姿勢は大切と思い、今回、はじめてこういう場に記事を書いてみました。一度現場を離れた経験があるからこそ、いまの技術や開発スタイルを、自分なりに咀嚼しなおしたいと思いました。

このハーネスは、誰かに勧めるためのものではありません。

自分が触り続けられるか、
手を入れたいと思えるか、
壊れても直したくなるか。

そういう感覚を大事にしながら、そしてこのような場所で「時代」を学びながら、しばらくはこの形で使ってみようと思っています。

また気が向いたら、少しずつ変えていくはずです。

NOTE:
この記事は、Claude と ChatGPT によって支援されています。ベースとなるプロンプトを自作し、両方の AI と壁打ちしブラッシュアップ、その上で記事を生成させています。このループを何度か繰り返したのち、自分自身の最終校閲で作成しています。

Discussion