diff --git a/.github/workflows/setup-check.yml b/.github/workflows/setup-check.yml index ab788b7..6d88b97 100644 --- a/.github/workflows/setup-check.yml +++ b/.github/workflows/setup-check.yml @@ -1,4 +1,5 @@ -# setup/ (Tier 1 リアルタイム symlink・Tier 2 明示的スクリプト実行) の bats テストと +# setup/ (Tier 1 リアルタイム symlink・Tier 2 明示的スクリプト実行) と +# herdr/plugins/ (Herdr ローカルプラグイン) の bats テスト、および # Claude Code hook スクリプトの unit test 検証 # # 目的: @@ -10,7 +11,8 @@ # - runs-on: macos-latest — テスト対象がすべて zsh script かつ macOS 前提 (defaults/pam 等) # のため。bats-core は runner に無いため Homebrew で導入する。 # - 全テストは stub 実行ファイル・サンドボックス HOME を使い、実際の defaults/pam/mise/ -# claude/git/brew を一切呼ばない (setup/README.md 参照)。 +# claude/herdr/brew を一切呼ばない (setup/README.md 参照)。git はサンドボックス内に +# 作った使い捨てリポジトリに対してのみ実行する。 # # セキュリティ: # - run: で github.event.* の自由文を一切評価しない (injection 対策) @@ -20,6 +22,7 @@ on: pull_request: paths: - 'setup/**' + - 'herdr/**' - 'claude/hooks/**' - 'claude/settings.json' - '.github/workflows/setup-check.yml' @@ -30,6 +33,7 @@ on: - main paths: - 'setup/**' + - 'herdr/**' - 'claude/hooks/**' - 'claude/settings.json' - '.github/workflows/setup-check.yml' @@ -40,7 +44,7 @@ concurrency: jobs: bats: - name: setup/tests/*.bats + hook unit test + name: setup/tests + herdr plugin tests + hook unit test runs-on: macos-latest timeout-minutes: 10 steps: @@ -51,7 +55,7 @@ jobs: run: brew install bats-core - name: Run bats tests - run: bats setup/tests/*.bats + run: bats setup/tests/*.bats herdr/plugins/*/tests/*.bats # settings.json の PreToolUse hook が呼ぶ破壊的コマンド判定パーサ。 # 標準ライブラリのみで動くので runner の python3 をそのまま使う。 diff --git a/.gitignore b/.gitignore index f20d29b..9222b0b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,8 @@ nix/modules/darwin/homebrew.local.nix # 静的な seed は codex/config.base.toml に置き、~/.codex/config.toml は # アプリ所有の実体ファイルとして追跡しない (nix/modules/home/codex.nix 参照)。 .codex/config.toml + +# Herdr プラグインのマシンローカル allowlist。本来は Herdr のプラグイン設定ディレクトリ +# (~/.config/herdr/plugins/config//repos.local.json) に置くものだが、リポジトリ内に +# 誤って作られた場合の保険。非公開リポジトリの origin URL を含みうるため追跡しない。 +herdr/plugins/*/config/repos.local.json diff --git a/AGENTS.md b/AGENTS.md index 785947c..cae3a78 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,7 +5,7 @@ - `CONTEXT.md` — AI エージェント指示の層(規範フラグメント・グローバル / プロジェクト AGENTS.md・SOUL.md・CLAUDE.local.md・channel prompt)を区別する用語集 - `aliases` — alias 定義の SSOT(root 直下、`~/.aliases` にシンボリックリンク)。旧 `nix/modules/home/zsh.nix` の `shellAliases` から移行(`setup/link.zsh` が配置、Tier 1) - `scripts/` — 外部シェルスクリプト(`aliases` の alias から呼び出される。旧 `aliase/` から改名) -- `setup/` — Tier 1(リアルタイム symlink)・Tier 2(明示的スクリプト実行)・Tier 3(カットオーバー・ロールバック)の実装。`darwin-rebuild switch` を使わず、`zsh setup/link.zsh` で dotfiles を配置し(Tier 1)、`setup/languages.zsh`(mise による言語ランタイム + corepack + バージョン固定 CLI)/`setup/defaults.zsh`(macOS defaults・IME)/`setup/pam.zsh`(Touch ID for sudo)/`setup/claude-sync.zsh`(skills clone・plugin sync・MCP merge)/`setup/codex-sync.zsh`(config.toml seed-if-absent)を個別実行する(Tier 2)。既存 PC を home-manager 込みの旧構成から移行する場合は `setup/cutover.zsh`(pre-flight build 確認 + `darwin-rebuild switch`)/`setup/rollback.zsh`(`.before-nix` 衝突検出付きロールバック)を使う(Tier 3)。**実機での実行は `setup/migrate.zsh` が唯一のエントリポイント**(`--dry-run`/`--apply`。Tier 1/2/3 を依存順(Phase 1: link → Phase 2: cutover/pam [root] → Phase 3: languages/defaults/claude-sync/codex-sync)で実行し、`~/.dotfiles-migrate/manifest.log` で部分適用を検出・再開する。fail-closed、rollback.zsh は自動では呼ばない)。個別スクリプトの直接実行はメンテナンス目的のみ。詳細は `setup/README.md` と `docs/superpowers/specs/2026-08-21-restore-script-management-inventory.md`・`docs/superpowers/specs/2026-08-22-restore-script-management-tier3-cutover-design.md`・`docs/superpowers/specs/2026-08-22-migrate-orchestrator-recovery-plan.md` を参照 +- `setup/` — Tier 1(リアルタイム symlink)・Tier 2(明示的スクリプト実行)・Tier 3(カットオーバー・ロールバック)の実装。`darwin-rebuild switch` を使わず、`zsh setup/link.zsh` で dotfiles を配置し(Tier 1)、`setup/languages.zsh`(mise による言語ランタイム + corepack + バージョン固定 CLI)/`setup/defaults.zsh`(macOS defaults・IME)/`setup/pam.zsh`(Touch ID for sudo)/`setup/claude-sync.zsh`(skills clone・plugin sync・MCP merge)/`setup/codex-sync.zsh`(config.toml seed-if-absent)/`setup/herdr-sync.zsh`(Herdr ローカルプラグインの link・allowlist 配置)を個別実行する(Tier 2)。既存 PC を home-manager 込みの旧構成から移行する場合は `setup/cutover.zsh`(pre-flight build 確認 + `darwin-rebuild switch`)/`setup/rollback.zsh`(`.before-nix` 衝突検出付きロールバック)を使う(Tier 3)。**実機での実行は `setup/migrate.zsh` が唯一のエントリポイント**(`--dry-run`/`--apply`。Tier 1/2/3 を依存順(Phase 1: link → Phase 2: cutover/pam [root] → Phase 3: languages/defaults/claude-sync/codex-sync/herdr-sync)で実行し、`~/.dotfiles-migrate/manifest.log` で部分適用を検出・再開する。fail-closed、rollback.zsh は自動では呼ばない)。個別スクリプトの直接実行はメンテナンス目的のみ。詳細は `setup/README.md` と `docs/superpowers/specs/2026-08-21-restore-script-management-inventory.md`・`docs/superpowers/specs/2026-08-22-restore-script-management-tier3-cutover-design.md`・`docs/superpowers/specs/2026-08-22-migrate-orchestrator-recovery-plan.md` を参照 - `claude/` — Claude Code 設定(`~/.claude/` にシンボリックリンク) - `claude/rules/` — 全 AI エージェント向けグローバル指示のフラグメント(SSOT)。`core` / `worker` / `orchestrator` / `hermes-identity` の 4 ファイルを `scripts/build-agent-rules.zsh`(旧 `aliase/build-agent-rules.zsh`)が結合して生成物を作る - `claude/hermes/SOUL.md` — Hermes Agent 用グローバル指示の生成物(`~/.hermes/SOUL.md` にシンボリックリンク)。直接編集しない @@ -21,6 +21,7 @@ - `.gitignore` — リポジトリ内に偶発的に作られたローカル overlay ファイル(例: `nix/modules/darwin/homebrew.local.nix`)を保険的に除外する - `gitignore_global` — グローバル gitignore(`~/.gitignore_global` にシンボリックリンク) - `grip/` — grip 設定(`~/.grip/` にシンボリックリンク) +- `herdr/` — Herdr のローカルプラグイン置き場。`herdr/plugins/safe-worktree/` は allowlist 済みリポジトリでリモート既定ブランチの最新 SHA を base に linked worktree を作り、`worktree.created` イベントで直接作成を監査する(削除・自動修復はしない)。`setup/herdr-sync.zsh`(Tier 2)が `herdr plugin link` で作業ツリーを直接登録し、allowlist の SSOT `config/repos.json` をプラグイン設定ディレクトリへシンボリックリンクする(非公開リポジトリは追跡しない `repos.local.json` 側に書く)。登録先パスがそのまま保存されるため、link と設定配置は primary チェックアウト(`~/.dotfiles`)から実行したときだけ行う(使い捨て worktree を登録すると削除時に壊れるため)。パス解決は `setup/lib/herdr.zsh` に集約し、`setup/migrate.zsh` の health check も同じ関数から引く。詳細は `herdr/plugins/safe-worktree/README.md` - `nix/` — nix-darwin + flakes による Homebrew パッケージ管理定義(`darwin-rebuild` から参照される。home-manager は Tier 3 で廃止済み、詳細は `nix/README.md`。`nix/lib/` は Brewfile 生成用の小さな Nix ヘルパー、`nix/tests/` は生成 Brewfile 側ロジックの bats テストで、`nix-check` workflow が実行する) - `ssh/` — SSH 設定(`~/.ssh/` にシンボリックリンク) - `zsh/` — zsh 補完ファイル(`~/.zsh/` にシンボリックリンク) diff --git a/herdr/plugins/safe-worktree/README.md b/herdr/plugins/safe-worktree/README.md new file mode 100644 index 0000000..de74f1e --- /dev/null +++ b/herdr/plugins/safe-worktree/README.md @@ -0,0 +1,213 @@ +# safe-worktree + +Herdr のローカルプラグイン。allowlist に登録したリポジトリだけを対象に、 +**リモートから取り直した実 SHA** を base にして linked worktree を作る。 + +## 何を防ぐか + +Herdr 標準の `herdr worktree create --base ` は `` をローカルで解決する。 + +```sh +herdr worktree create --branch feat/x --base main # ローカルの main(数日前かもしれない) +herdr worktree create --branch feat/x --base HEAD # 今チェックアウト中のコミット +``` + +どちらも成功して worktree ができるので、古い base で作ったことに気づくのは +だいたい PR を出したあとになる。このプラグインは + +1. base をローカル ref のまま受け取らない(`HEAD` とローカルブランチ名は拒否) +2. 既定ブランチは毎回 `git ls-remote --symref origin HEAD` で問い合わせる +3. fetch してから SHA に解決し、その SHA を base として渡す +4. 作成後に実際の HEAD と突き合わせてから成功を報告する + +の 4 点で、この経路を塞ぐ。2 のおかげで、上流が既定ブランチを付け替えても +(`v2` → `main` など)ローカルの設定を触らずに追従する。 + +## 使い方 + +### popup から(人間) + +`herdr plugin action invoke dotfiles.safe-worktree.create` か、 +`config.toml` にキーバインドを足して呼ぶ。 + +```toml +[[keys.command]] +key = "prefix+W" +type = "plugin_action" +command = "dotfiles.safe-worktree.create" +description = "safe worktree create" +``` + +popup が開いてブランチ名を尋ね、リポジトリ・base・SHA を表示してから確認を取る。 + +### コマンドラインから(エージェント含む) + +```sh +~/.dotfiles/herdr/plugins/safe-worktree/bin/create.zsh --repo dotfiles --branch feat/x +``` + +| 引数 | 意味 | +| --- | --- | +| `--repo ` | 対象リポジトリ。allowlist の `label` か作業ツリーのパス。省略時は Herdr の呼び出しコンテキスト → カレントディレクトリの順で解決する | +| `--branch ` | 作成するブランチ名。TTY があれば省略時に尋ねる | +| `--base ` | 省略時はリモートの既定ブランチ。明示するなら `origin/` か存在するコミット SHA のみ | +| `--label ` | Herdr ワークスペースのラベル | +| `--reuse` | ローカルにブランチが既にある場合、新規作成の代わりに `herdr worktree open` で開く | +| `--yes` | 対話確認を省略する | +| `--focus` / `--no-focus` | 作成したワークスペースにフォーカスを移すか(既定は `--no-focus`) | + +終了コード: + +| コード | 意味 | +| --- | --- | +| 0 | 成功 | +| 1 | git / herdr の実行失敗、リモートへの問い合わせ不能、状態ディレクトリ・監査マーカーの書き込み失敗 | +| 2 | 引数・設定ファイルの不備 | +| 3 | origin URL が allowlist に無い | +| 4 | base として受け付けない ref | +| 5 | 既存ブランチでの新規作成要求(`--reuse` を促す) | +| 6 | 作成結果の照合失敗 | + +## allowlist + +設定は Herdr のプラグイン設定ディレクトリ +(`herdr plugin config-dir dotfiles.safe-worktree`)に置く。2 ファイルを結合して使う。 + +| ファイル | 実体 | 用途 | +| --- | --- | --- | +| `repos.json` | dotfiles の `config/repos.json` への symlink | 公開リポジトリ。dotfiles で追跡する | +| `repos.local.json` | マシンローカルの実ファイル | 非公開リポジトリ。追跡しない | + +配置は `setup/herdr-sync.zsh`(Tier 2)が行う。ただし `herdr plugin link` は渡された +パスをそのまま登録先として保存するため、**primary チェックアウト(`~/.dotfiles`)から +実行したときだけ** link と設定配置を行う。使い捨ての worktree を登録すると、その worktree を +消した時点でプラグイン本体と allowlist の symlink が同時に壊れる。primary 以外から +実行された場合は link も設定配置も両方まとめてスキップする(片方だけ実行すると、 +登録されているプラグインとは別の場所の allowlist を指す symlink が残る)。 + +`repos.json` が無い場合、 +プラグインは「allowlist が空」ではなく「設定が壊れている」として fail-closed で停止する。 + +```json +{ + "version": 1, + "defaults": { "remote": "origin" }, + "repos": [ + { "label": "dotfiles", "origin": "https://github.com/gotomts/dotfiles.git", "root": "~/.dotfiles" } + ] +} +``` + +`origin` の突き合わせは scheme・userinfo・port・末尾の `.git` を無視した +`ホスト/パス` で行う。ssh 経由と https 経由で同じリポジトリを別物として扱わないため。 +どちらかの URL が正規化できない場合は「不一致」として扱う(正規化に失敗した空文字列同士が +一致して allowlist をすり抜けるのを防ぐ)。 +`root` は `--repo