複数プロジェクトのテンプレを束ねる kata
Claude Code と一緒に Rust 製の CLI を作るのが楽しくて、ここ最近で shun / rvpm / todoke / yui / renri といった CLI がどんどん増えていきました。これらにまったく同じボイラープレートを適用しつづけるためのメタテンプレート CLI、kata (型) を作りました。
型 —
the woodblock pattern. 各プロジェクトに同じ型を押し当てる、版木のイメージです。
https://github.com/yukimemi/kata
なぜ作ったのか
同じような CLI が増えてくると、共通ボイラープレートのメンテがどんどんしんどくなります。具体的にしんどかったのはこのあたりです。
-
Makefile.toml
のcheck
/clippy
/test
タスクの並び -
.github/workflows/ci.yml
の OS マトリクスと action のバージョン pin -
.github/workflows/release.yml
の cross-compile + cargo publish のテンプレ -
rustfmt.toml
/clippy.toml
/rust-toolchain.toml
の方針 -
apm.yml
でAPM経由の AI エージェント用 skill (renri
など) を入れる定型 -
renovate.json
の auto-merge ルール - そして極めつけに
AGENTS.md
/CLAUDE.md
/GEMINI.md
最後の
AGENTS.mdがいちばんやっかいでした。Claude / Gemini / Codex に渡している「PR レビューはこう回す」「worktree workflow はこう」「Rust の lint/format ポリシーはこう」みたいな会話で蓄積されたノウハウを、新しいプロジェクトを生やすたびに、あるいは規約を 1 行直すたびに、N 個のリポジトリにぜんぶ手でコピペする必要があったのです。
「Gemini Code Assist と CodeRabbit 両方のレビュー待つようにしよう」と気付いたら 7 個のリポジトリの
AGENTS.mdを全部開いて編集する、というのを何度かやりました。1 回ならいいんですけど、規約は一度決めて終わりじゃなくて、運用しながら何度も微調整したくなる。
copier / cookiecutter / cruft といった既存ツールは「最初のプロジェクト生成」と「機械的な再適用」までは面倒を見てくれるんですが、
AGENTS.mdのように
- リポジトリ共通のセクション (PR レビュー規約、worktree workflow)
- プロジェクト固有のセクション (このプロジェクトのアーキ概要、特殊な事情)
が1 枚のファイルに同居しているようなものを update し続ける機能はありませんでした。AI に判定を委譲するモードもありません。
そこで作ったのが kata です。「型 (テンプレート) を版木のように押し当てて、押し当てきれないところだけ AI に判断させる」 という発想で設計しました。
kata の特徴
-
layered template—
pj-base
+pj-rust
+pj-rust-cli
を順に重ねて、後勝ち。言語非依存な部分をpj-base
に集約できる -
—
how
×when
の二軸how
(overwrite
/merge-section
/merge-toml
/merge-yaml
/ai
/script
) とwhen
(once
/always
/manual
) が独立。how="ai", when="once"
とhow="script", when="always"
がどちらも自然に書ける -
marker-bracketed merge-section—
AGENTS.md
の<!-- kata:agents:base:begin -->
~<!-- kata:agents:base:end -->
の間だけを kata が管理。プロジェクト固有の節は外側にいくらでも書ける -
path-based merge-toml—
Makefile.toml
のtasks.check
/tasks.clippy
/tasks.test
だけを kata が所有して、tasks.install-local
のような独自タスクは触らない -
AI 委譲—
how = "ai"
なファイルは、インストール済みのclaude
/gemini
/codex
CLI に template の diff と現在の中身を投げて、chezmoi 風の[a]ccept / [e]dit / [s]kip / [d]efer
で確認 -
truth は PJ 側— どのテンプレートをどの rev で適用したかは各 PJ の
.kata/applied.toml
に記録される。グローバル設定は単なる PJ パスのレジストリ - 並列実行— tokio で PJ をファンアウト、AI 呼び出しは semaphore で抑制してエージェント CLI の同時起動が爆発しないようにする
-
CI 同期—
kata-apply.yml
を pj-base が配布。daily でkata update + kata apply
を回して、テンプレ上流の変更を PR として自動取り込み
インストール
cargo install kata
kata --versionで動作確認できれば OK です。
クイックスタート
# 新しい Rust CLI プロジェクトに rust-cli プリセットを適用 mkdir my-rust-cli && cd my-rust-cli kata init github.com/yukimemi/pj-presets:rust-cli --non-interactive # テンプレが進化したら再適用 (idempotent) kata apply --non-interactive # 適用前のドライラン kata status # 何が tracked か確認 kata list
これで
Makefile.toml/
apm.yml/
renri.toml/
.github/workflows/ci.yml/
release.yml/
AGENTS.md/
CLAUDE.md/
GEMINI.md/
rustfmt.toml/
clippy.toml/
rust-toolchain.toml/
renovate.jsonなどがまとめてプロジェクトに落ちてきます。
preset = テンプレートの束
pj-presets:rust-cliは単に「どのテンプレートを、どの順に重ねるか」を書いた小さなファイルです。
# pj-presets/rust-cli.toml name = "rust-cli" [[templates]] source = "github.com/yukimemi/pj-base" [[templates]] source = "github.com/yukimemi/pj-rust" [[templates]] source = "github.com/yukimemi/pj-rust-cli"
順に書かれた順番で適用され、同じファイルが衝突したら後勝ちです。これによって
-
pj-base
— 言語非依存 (LICENSE,.gitignore
,AGENTS.md
の共通節,apm.yml
,renri.toml
の base,kata-apply.yml
……) -
pj-rust
— Rust 共通 (Makefile.toml
, CI matrix,rust-toolchain.toml
,rustfmt.toml
,clippy.toml
) -
pj-rust-cli
— CLI 用追加 (release.yml
の cross-compile + cargo publish)
という役割分担ができて、たとえば「Rust ライブラリだから CLI 用 release は要らない」というケースには
pj-rust-libを組み合わせた別 preset を用意するだけで済みます。
ライブラリ向けに
rust-lib、Web フロント向けに
web-react、Firebase まで含む
web-react-firebaseも用意していて、preset 単位で気軽に「型」を切り替えられます。
how
と when
を独立に持つということ
kata の設計でいちばんこだわったのが
how(適用方法) と
when(タイミング) を別の軸として持つことです。
how |
何をするか |
|---|---|
overwrite |
テンプレ通りにファイルを上書き |
merge-section |
<!-- kata:*:begin -->~ endの間だけ差し替え |
merge-toml |
toml_editで指定パスだけマージ |
merge-yaml |
serde_yamlで YAML の指定パスだけマージ |
ai |
claude / gemini / codex に判断委譲 |
script |
任意のシェルコマンドを実行 |
when |
いつ適用するか |
|---|---|
once |
初回だけ。以降は .kata/applied.tomlの once_applied = trueで skip |
always |
毎回適用 (kata applyのたびに同期) |
manual |
明示的に --fileを指定したときだけ |
この 2 軸が独立なので、たとえば
-
release.yml
はoverwrite, when=once
— 初回だけ配って、以降はプロジェクト側で自由に編集 -
ci.yml
はoverwrite, when=always
— CI は常に上流追従 -
Makefile.toml
はmerge-toml, when=always
— kata 所有のタスクだけ追従 -
AGENTS.md
はmerge-section, when=always
— マーカーの中だけ追従 -
apm.yml
はoverwrite, when=once
— 初回テンプレ、以降はプロジェクト側 -
LICENSE
はoverwrite, when=once
— 初回だけ
という細かいポリシーを 1 つの manifest で表現できます。「mode」として 1 つの enum にまとめなかったのは、
how="ai", when="once"(ROADMAP.md の初期生成だけ AI に任せる) のような組み合わせを潰したくなかったからです。
AGENTS.md の merge-section が刺さるところ
kata を入れて一番恩恵を感じているのが
AGENTS.mdの扱いなので、ここは少し詳しく書きます。
pj-base側の
AGENTS.md.base(テンプレ) には共通規約だけが書かれています。
## Shared conventions This file is the agent-agnostic source of truth (per the [agents.md](https://agents.md) convention)... ### Git workflow - **No direct push to `main`.** Open a PR. - Branch names: `feat/...`, `fix/...`, `chore/...`. - **PR titles + bodies in English.** ... ### PR review cycle - Every PR runs reviews from **Gemini Code Assist** and **CodeRabbit**... - **After opening a PR, immediately enter the review-monitoring loop...** ... ### Worktree workflow Use [`renri`](https://github.com/yukimemi/renri) for any commit-bound change...
これを
pj-base/template.tomlでこう宣言しています。
[[file]]
src = "AGENTS.md.base"
dst = "AGENTS.md"
how = "merge-section"
when = "always"
marker = { begin = "<!-- kata:agents:base:begin -->", end = "<!-- kata:agents:base:end -->" }
kata applyを実行すると、各プロジェクトの
AGENTS.mdの対応マーカーの中だけがテンプレ内容で置き換わります。マーカーの外にはそのプロジェクトの固有の事情 (アーキテクチャ、設計判断、ドメイン用語、Phase n の状況……) をいくらでも書いておけて、それは
kata applyで一切触られません。
さらに layered なので、
pj-rustは
<!-- kata:agents:rust:* -->、
pj-rust-cliは
<!-- kata:agents:rust-cli:* -->とそれぞれ自分のマーカーブロックを所有します。これによって 1 枚の
AGENTS.mdの中に「言語非依存 / Rust 共通 / Rust CLI 専用 / プロジェクト固有」の 4 層が綺麗に同居する、という構造になります。
Makefile.toml の merge-toml で「kata 所有タスク」だけ追従させる
AGENTS.mdの marker と並んでよく使うのが、
Makefile.tomlの
merge-tomlです。
pj-rust/template.tomlではこう宣言しています。
[[file]]
src = "Makefile.toml"
how = "merge-toml"
when = "always"
# kata owns these specific tasks; everything else is left untouched
paths = [
"tasks.default",
"tasks.check",
"tasks.fmt",
"tasks.fmt-check",
"tasks.clippy",
"tasks.test",
"tasks.lock-check",
"tasks.publish-dry",
"tasks.hook-install",
"tasks.apm-install",
"tasks.setup",
"tasks.on-add",
]
pathsで「kata が所有する TOML のパス」を明示します。
tasks.check/
tasks.clippy/
tasks.testのような kata-managed task は毎回上流から上書きされますが、consumer 側で勝手に追加した
tasks.install-localとか
tasks.deployのような独自タスクは paths に含まれないので一切触られない という挙動になります。
merge-tomlは
toml_editで
pathsで指定した key だけを replace していくので、
- 同じ key の値は更新される (例:
tasks.check.script
の中身が変わる) - paths に出てこない key は consumer の手書きが完全に保持
- インデント / コメント / 順序も
toml_edit
レベルで保持
という、
overwriteだと潰してしまう手書き内容を尊重した同期ができます。
GHA の action バージョンは .kata/vars.toml
に切り出して Renovate に任せる
merge-toml×
when = "once"のもう 1 つの実用例が、GitHub Actions の version pin を .kata/vars.toml (Tera 変数ファイル) に切り出す パターンです。
考えたい問題はこうです。
-
ci.yml
/release.yml
/kata-apply.yml
の version pin (actions/checkout@v6.0.2
等) をRenovate に自動 bumpさせたい - しかし workflow 本体は
overwrite, when=always
で kata-managed なので、Renovate が直接ci.yml
を編集しても次のkata apply
で潰されてしまう
解決策は、pin だけを .kata/vars.toml に切り出して、workflow 本体は Tera テンプレでそれを参照 することです。
pj-base/vars.toml(universal pin):
[actions] checkout = "actions/checkout@v6.0.2" create_pull_request = "peter-evans/create-pull-request@v8.1.1"
pj-rust/vars.rust.tomlで Rust 専用の pin を追加マージ:
[actions] swatinem_rust_cache = "Swatinem/rust-cache@v2"
template.toml側で
.kata/vars.tomlの所有関係を宣言:
# pj-base — universal pin を初回だけ seed [[file]] src = "vars.toml" dst = ".kata/vars.toml" how = "overwrite" when = "once" # pj-rust — Rust 専用 pin を merge-toml で重ねる (初回だけ) [[file]] src = "vars.rust.toml" dst = ".kata/vars.toml" how = "merge-toml" when = "once" paths = ["actions.swatinem_rust_cache"]
両方とも
when = "once"なので、初回 apply で seed したあとは consumer の .kata/vars.toml を kata は一切触らない、という所有関係になります。
ci.yml.teraの中では:
- uses: {{ vars.actions.checkout }}
- uses: {{ vars.actions.swatinem_rust_cache }}
として参照されているので、
.kata/vars.tomlの pin が変われば次の
kata applyで
ci.yml全体が新しい version で再レンダリングされる、という流れになります。
その上で consumer 側の .kata/vars.toml を Renovate の customManager が scan していて、新しい action リリースが出たら
を作ってくれます。Renovate が触るのは
.kata/vars.tomlの pin 値を bump する PR
.kata/vars.tomlの 1 行だけ、workflow 本体は kata-apply の再レンダリングで反映、という分業になります。
まとめると、
-
workflow の構造変更(新ステップ追加、jobs の整理など) → 上流テンプレへの push → daily
kata-apply
で降りてくる -
action の version bump→ consumer の
.kata/vars.toml
を Renovate が自走で書き換える → 次のkata apply
でci.yml
が再レンダリング
という、役割分担の明快な並列同期が
merge-tomlと
when = "once"の組み合わせだけで組めます。
ところで、上の
ci.yml.teraで
{{ vars.actions.checkout }} のように書けているのは Rust 製のテンプレートエンジン Tera のおかげです。.teraサフィックスの付いたファイルが apply 時に Tera で render されて、suffix を落とした名前で consumer に書き出される、というシンプルな仕組み。
{% if is_windows() %} のような分岐も {{ env.HOME }} のような環境変数参照も全部 Tera の機能で、kata 側はほぼ何も再発明していません。実はこの「設定を Tera で書ける」感覚は、rvpm や todoke といった他の Rust 製 CLI でも同じスタックで提供していて、内部では teravars (Tera + vars + include + system context を統一した薄いラッパー) を共有しています。
rvpmで見慣れた
{% if is_windows() %} がそのまま kata の template でも通る、というのが地味に効いていて、Rust で「設定が宣言的に書ける小さな CLI」を作るときの定番スタックとして teravars はかなりおすすめです。ここが本題: pj-base を直せば全プロジェクトに反映される
これが kata を作って一番うれしかったところです。
「あ、
AGENTS.mdのこの一節、ちょっと書き方が悪かったな。Claude が誤解しがちだから直したい」
「PR review cycleの節、CodeRabbit の rate-limit notice の扱いを追記したい」
「Worktree workflowの節に新しいrenri pruneの説明を入れたい」
こういう気付きは規約を運用してるとめちゃくちゃ頻繁にあります。kata を入れる前は N 個の AGENTS.md を全部開いて同じ編集を N 回繰り返す ことになっていました。
kata を入れた今はこうなっています。
# pj-base 側でだけ直す cd ~/src/github.com/yukimemi/pj-base $EDITOR AGENTS.md.base git commit -am "docs(agents): clarify PR review cycle" git push # あとは何もしなくていい — 翌日になれば、 # kata-apply ワークフローが全 PJ に PR を作って auto-merge する
具体的には、各プロジェクトの
.github/workflows/kata-apply.yml(これも pj-base 配布) が毎日 03:17 UTC に走って、
- 上流テンプレ (
pj-base
/pj-rust
/pj-rust-cli
) の最新 rev を取得 -
kata update
で applied.toml の rev を更新 -
kata apply --non-interactive --no-ai
で再レンダリング - 差分が出たら
kata-apply/auto
ブランチに PR を作成 - CI が緑なら auto-merge
を全リポジトリで自動的に回します。規約や設定の更新が該当するテンプレレイヤへの 1 push だけで N プロジェクトに伝播していくわけです。
AGENTS.mdの共通節なら
pj-base、
Makefile.toml/
rustfmt.toml/
clippy.toml/
ci.ymlといった Rust 共通の規約なら
pj-rust、
release.ymlの cross-compile + cargo publish なら
pj-rust-cli、というレイヤ分担そのままに、それぞれの上流に 1 push すれば全 consumer PJ が翌日には追従します。
ワークフロー本体もテンプレ管理なので、ワークフロー自体の改善 (新しい action バージョン、ステップの追加) も同じ仕組みで自動的に各プロジェクトに降りていきます。テンプレが自分自身のメンテも見るかたち。
CI で kata-apply を回す
このフローの肝は CI で kata apply を回せることです。設計時点でここを最優先に考えていて、
-
--non-interactive
でプロンプトを完全にスキップできる -
--no-ai
で AI ファイルを丸ごとスキップ (= 機械的なテンプレ同期だけ自動で回せる) -
--non-interactive --yes
で「全部 accept」モードもあり (= 信頼できるテンプレなら AI も自動 accept)
という 3 つのフラグで CI 適性が担保されています。
pj-baseが配布する
kata-apply.yml.teraの中身は essentially こんな感じです (一部抜粋)。
name: kata-apply
on:
schedule:
- cron: "17 3 * * *" # 03:17 UTC daily
workflow_dispatch:
permissions:
contents: write
pull-requests: write
concurrency:
group: kata-apply
cancel-in-progress: false
jobs:
apply:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
with:
token: ${{ secrets.KATA_APPLY_TOKEN }} # ← PAT 必須
- name: Install kata
run: |
KATA_VERSION="$(curl -fsSL https://api.github.com/repos/yukimemi/kata/releases/latest | jq -r .tag_name)"
curl -fsSL "https://github.com/yukimemi/kata/releases/download/${KATA_VERSION}/kata-x86_64-unknown-linux-gnu.tar.gz" \
| tar xz -C /tmp
sudo mv /tmp/kata /usr/local/bin/kata
- name: kata update + apply
run: |
kata update
kata apply --non-interactive --no-ai
- name: Open / update PR if there are changes
id: cpr
uses: peter-evans/create-pull-request@v8.1.1
with:
token: ${{ secrets.KATA_APPLY_TOKEN }}
branch: kata-apply/auto
title: "chore(kata): auto-apply"
- name: Enable auto-merge
if: steps.cpr.outputs.pull-request-number != ''
env:
GH_TOKEN: ${{ secrets.KATA_APPLY_TOKEN }}
run: |
gh pr merge --auto --squash ${{ steps.cpr.outputs.pull-request-number }}
要点だけハイライトしておきます。
-
。
KATA_APPLY_TOKEN
はGITHUB_TOKEN
じゃダメGITHUB_TOKEN
経由で開いた PR は GitHub のループ防止仕様で下流ワークフロー (CI) を triggers しません。auto-merge の前提が CI 緑判定なので、CI が走らないと永遠にマージされません。適切な権限 (contents: write
+pull-requests: write
) を付けた PAT (fine-grained でも classic でも可) をKATA_APPLY_TOKEN
リポジトリシークレットとして設定する、というのが consumer 側の唯一のセットアップ作業です -
ブランチは。
kata-apply/auto
固定create-pull-request
のdelete-branch: true
と組み合わせると、毎日新しい差分を同じブランチに rolling で上書きしていく動きになるので、PR が大量にスタックしません - CI が落ちたら PR は open のまま残る。auto-merge は CI 緑になったときだけ発火するので、何か壊れたら人間が見るタイミングが自然に生まれます
-
。
cron: "17 3 * * *"
は意図的な off-peak かつ off-the-hour pin0 0 * * *
や0 9 * * *
みたいに :00 ちょうどでスケジュールする人が地球上に多すぎて、GitHub Actions の runner プールが :00 / :30 で枯渇する (cron-storm) という現象があります。:17
のような半端な分にずらすと runner 確保がスムーズ。さらに3 UTC
は日本時間 12:00 / 米西海岸の夜 / 欧州早朝で、runner 自体も比較的空いている時間帯なので、daily な機械的同期にはちょうどいい枠です
「全プロジェクトに同じワークフローが入っている」という事実が、全プロジェクトに同じ自動同期がかかっている という安心感に繋がっていて、これは入れた価値が大きかったです。
AI 委譲モード
how = "ai"を指定したファイルは、インストール済みの AI CLI に判断を投げます。
template.toml にこう書くと:
[[file]] src = "ROADMAP.md.tera" dst = "ROADMAP.md" how = "ai" when = "always" agent = "auto" # claude > codex > gemini で最初に見つかったやつ prompt = """ Merge the template's structural changes into the project's ROADMAP.md. Preserve project-specific phases and dated entries. """
kata は template の diff、現在の dst の中身、prompt をまとめて指定エージェントの CLI (
claude -p,
gemini -p,
codex execのいずれか) に投げます。返ってきた full body または patch に対して、chezmoi 風の対話プロンプトが出ます。
proposed change for ROADMAP.md: + Phase 5 — opencode adapter + ## Crate structure (regenerated section) ... [a]ccept / [e]dit / [s]kip / [d]efer ?
-
a
= そのまま採用 -
e
=$EDITOR
で開いて手で直してから採用 -
s
= この回はスキップ (次回kata apply
でもう一度提案される) -
d
=defer
(今回は見送り、ただし「次回必ず再提案」をapplied.toml
に記録)
--non-interactiveだけだと安全側に倒れて AI ファイルはスキップ、
--non-interactive --yesだと全部 accept という CI 完全自動モードになります。
backend は trait で抽象化されていて、
claude/
gemini/
codexの 3 つを実装済みです。
agent = "auto"は PATH を見て上から順にフォールバックしていく挙動。
MockAiAgentも組み込まれていて、テストでは決定的な応答が返せます。
並列度の制御もあって、AI 呼び出しはグローバル semaphore (default 4) で絞られます。
kata apply --allで 10 個のプロジェクトを並列で回したときに、エージェント CLI の同時起動が爆発しないようにするためです。
.kata/applied.toml
が source of truth
kata の状態は全部 PJ 側の
.kata/applied.tomlに書かれます。グローバル設定 (
~/.config/kata/config.toml) は単なる PJ パスのレジストリで、何が適用されているかは知りません。
applied.tomlはだいたいこんな感じです。
preset = "github.com/yukimemi/pj-presets:rust-cli" applied_at = "2026-05-17T04:40:43Z" [[templates]] source = "github.com/yukimemi/pj-base" rev = "f04151faf4f0678be9621bb724c8f3120a5e4d8b" version = "0.10.0" [[templates]] source = "github.com/yukimemi/pj-rust" rev = "9f103ca5aaf39e1dcf1a4d84b11685821aabc62f" version = "0.5.0" [[templates]] source = "github.com/yukimemi/pj-rust-cli" rev = "9263751c3f94f3415147e546db33170157fb1503" version = "0.2.0" [files."AGENTS.md"] content_hash = "e4f146a1c66d11e6b6c707f52507b3e37da2fe52d8d7cf13f75edcf9ad5d3a7f" [files."Makefile.toml"] content_hash = "27e3f57b9efd6c177843d9b0d248f40af5843739bcde6ceaf985f2ce518ecfcf" [files."LICENSE"] once_applied = true
これを git に commit しておくと、
-
teammateが clone →
kata apply
で同じ状態が再現できる -
CIが
applied.toml
を見るので、ローカル設定なしで CI が完結する -
rev が pin されているので、上流の HEAD が動いても勝手に当たらない (
kata update
で明示的に上げる)
という運用になります。「状態は対象 (PJ) 側に置く、グローバル設定は単なるレジストリに留める」という設計を kata でも採用しました。
kata apply
が冪等であること
設計でずっと気をつけたのが「何度走らせても結果が変わらない」ことです。
applyが走るたびに差分が出るようなツールは CI で回せないので、
-
content_hash
をapplied.toml
に記録して、変化がないファイルはそもそも書き換えない -
once_applied = true
のファイルは 2 回目以降は完全 skip -
merge-section
/merge-toml
のマージはべき等なように実装 (同じ入力で何度マージしても同じ出力) - AI モードも
--non-interactive --no-ai
で完全に固定動作 (= AI モードを除いて再現可能)
という不変条件を守るようにしてあります。
おかげで
kata apply --non-interactive --no-aiを毎日 CI で回すと、変更が必要なときだけ PR が立ち、なければ何も起きないという静かな動きになります。
関連プロジェクト
kata の周りに必要な template repo は以下です。すべて単体で意味があるので、好きな組み合わせで preset を組めます。
| repo | 役割 |
|---|---|
pj-base |
.gitignore, AGENTS.md 共通節, kata-apply ワークフロー, …)
pj-rust
pj-rust-cli
pj-rust-lib
pj-pnpm
pj-react-web
pj-firebase
pj-presets
rust-cli/
rust-lib/
web-react/
web-react-firebaseのバンドル
kata 自身も dogfood で
pj-presets:rust-cliを適用しています — README の表が示す通り、
Makefile.tomlも CI も
AGENTS.mdも全部 kata-apply 経由で同期されています。
おわりに
kata を入れる前は、
AGENTS.mdを 1 行書き換えるのに 7 リポジトリの編集が必要でした。今は pj-base に 1 push すれば、翌日には全プロジェクトに PR が立って auto-merge されています。Claude / Gemini / Codex に渡しているノウハウが全プロジェクトで瞬時に揃う、というのが体験として相当よかったです。
「規約を更新する心理的コストが下がると、規約をもっと細かく洗練させたくなる」というポジティブフィードバックが回り始めていて、Claude Code との PR レビューサイクル運用 (
/loopでの 60s ポーリング、CodeRabbit の rate-limit notice の扱い、version-bump-only PR の特例……) みたいな細かい知見が、書いた次の日には全 PJ の Claude に届くようになりました。
Rust + Tera + tokio + AI CLI 委譲という shun / rvpm / todoke のときから使い倒している組み合わせに、
teravars(Tera engine + vars + include の共通エンジン) を載せた構成で、いつもの定番スタックの延長で書けたのも開発体験として良かったところです。
複数のリポジトリのボイラープレートに疲れている方、特に AGENTS.md などの AI への指示書 を複数プロジェクトに散らかしてしまっている方は、ぜひ kata を試してみてください — 型を押して、版木を当てて、揃えていきましょう。