sync-skills-lock

ルート直下の `skills-lock.json` の `computedHash` を upstream リポジトリの最新状態と照合して更新する。`source` が `Fandhe-AI/<repo>` に完全一致しないエントリは clone せず skip (安全弁)。submodule 配下の `skills-lock.json` は触らない。contribute-skill のマージ後や upstream 同期後、「ハッシュ更新」「skills-lock 同期」などで使用。

By fandhe-ai · 650 installs

npx skills add fandhe-ai/agent-cli-skills --skill sync-skills-lock

Source repository · Upstream listing

sync skills lock ルート直下の skills lock.json の computedHash を、upstream リポジトリの現状と照合して更新する。 対象ファイル ルート : 呼び出し元リポジトリ直下の skills lock.json — このスキルが唯一編集するファイル 除外 : submodule 配下の skills lock.json — submodule 境界を跨がないため 絶対に触らない 前提条件 gh CLI がインストールされ、認証済みであること node / npx が利用可能であること( npx skills add を使用するため)。 skills CLI は固定版( SKILLS CLI VERSION )で実行する。値と更新手順は「skills CLI のバージョン固定と更新手順」節を参照 python3 が利用可能であること(状態署名〔 path state / index state signature 〕と復元処理〔既存 ignored ファイルの比較・復元等〕で使用。主要 Linux / macOS には標準搭載されている。無い環境では Step 4 フェンスが npx 実行前に fail closed で停止するため、導入してから再実行すること) file CLI が利用可能であること(未追跡バイナリファイルの種別表示に使用。未導入の環境では 種別が file コマンド未検出 として表示され、承認前にサイズ・git blob ハッシュのみで 判断することになる。macOS / 主要 Linux ディストリビューションには標準搭載されている) ルート直下の skills lock.json が存在すること 実行前に skills lock.json に未コミットの変更がないこと (ステージ済み・未ステージ問わず)。本スキルの実行中に発生する変更は sync 由来のみとなり、 git add skills lock.json で全体をステージしても無関係な変更が混入しない 対象スキルの .agents/skills/<name / に未コミット変更がないこと 。 npx skills add は .agents/skills/<name / を upstream の最新版で上書きするため、そのディレクトリに WIP が存在すると即座に失われる。 git checkout で戻せるのは「最後にコミットされた状態」のみであり、npx 実行前の未コミット編集は復元できない。 未追跡ファイルとして存在する WIP も対象 であり、 git status porcelain で検出する 消費側リポジトリが commit 済み local patch を持つ場合 : vendored skill( .agents/skills/ 配下)へ commit 済みの local patch を適用しているリポジトリは、その検証・再適用の入口として repository owned checker scripts/check skill local patches.sh (無引数 = check / apply の 2 モード)と台帳 .agents/skills/LOCAL PATCHES.md を持つ。commit 済み patch は上記 clean ガードでは保護できないため、checker が存在する場合は同期の前後(Step 4 の pre check・Step 5.5 の apply + 最終検証)での成功が必須(非 0 は fail closed で同期・stage しない)。台帳があるのに checker が無い状態も検証不能として fail closed で停止する。checker(apply)の書き込み先は当該スキルディレクトリ・ skills lock.json ・durable patch 置き場 scripts/local patches/ に限る契約とし、範囲外の変更は各実行直後の digest 比較で fail closed に検出する(検出範囲は Git が追跡・列挙する対象に限る best effort であり、書き込み制限の保証ではない。保証はユーザーによる checker 内容レビュー + blob hash 承認が担う。詳細は Step 4 の「検出範囲の限界」コメント)。checker は消費側が配置する実行可能コードのため「存在するだけ」では実行せず、symlink ではない regular file(HEAD 側 mode も 100644/100755)であり、HEAD に commit 済みで worktree と一致し、かつユーザーへ由来・内容を提示して blob hash 単位の明示承認を得た場合のみ実行する(Step 4 で機械検証)。実行対象は worktree のファイルではなく承認済み HEAD blob を取り出した一時ファイル( CHECKER EXEC )とし、hash 確認後に worktree の checker を差し替える TOCTOU 経路を断つ 通常構成のメイン worktree で実行すること 。linked worktree( git worktree add で作られた作業ツリー)では .git が gitdir を指す通常ファイルになり、実 Git ディレクトリ( .git/worktrees/<name / と共有側の refs ・ logs ・ config ・objects)が状態署名の対象外になるため、Step 4 フェンスが npx 実行前に git rev parse absolute git dir / git common dir の不一致で検出して fail closed で拒否する。同様に、実 Git ディレクトリが作業ツリー外にある構成( git clone separate git dir ・submodule checkout・ .git が symlink)も、「実 Git ディレクトリ = 作業ツリー直下の .git 実体ディレクトリ」の検証( show toplevel との厳密一致 + lstat)で npx 実行前に fail closed で拒否する フロー Step 1: 引数を確認し、事前条件を検証する 引数ありの場合は該当スキルのみ処理、なしの場合は skills lock.json の全エントリを対象にする。 次に skills lock.json の clean 状態を確認する。未コミット変更(ステージ済み・未ステージ問わず)があれば中止する。 Step 2: upstream 一覧を集計する skills lock.json を読み、 source フィールドごとにスキルをグルーピングする(同一リポへの処理を 1 回にまとめるため)。 Step 3: source を検証する 安全弁 : 処理前に必ず source フィールドが Fandhe AI/<repo に完全一致することを確認する。前方一致では ../ を含む値が通過し、clone 時の URL パス正規化で組織外リポジトリを対象にできてしまうため、 OWNER/REPO へ正規化後に厳密な正規表現で検証する。想定外の source は skip してユーザーに警告する。 skills lock.json の改ざん・誤設定によって untrusted リポジトリから clone することを防ぐためである。 Step 4–7: 対象スキルを1つずつ処理する(ループ) 対象スキルそれぞれについて、次の 4→5→6→7 を順に実行し、 1スキル完了後に次スキルへ進む 。全スキル sync であっても同時に複数スキルを処理せず、1スキルずつ完結させること。 Step 4: npx skills add で computedHash を更新する sha256sum などで手動計算するのではなく、 npx skills add に計算を任せる。これにより CLI の内部アルゴリズムと完全に一致する。 npx skills add は以下を行う: upstream の最新スキルをダウンロード インストール先( .agents/skills/<name / )を最新化 skills lock.json の computedHash を CLI 算出値で更新 重要な副作用 : npx skills add はインストール済みファイルを最新の upstream 版で上書きする。upstream との同期が目的のため、これは意図した動作である。上記の per skill clean ガードは git status porcelain を使い、ステージ済み・未ステージ・ 未追跡ファイルも含めて 検出する。WIP がある場合は npx 実行前に skip するため、未コミット編集の消失は防止される。 注意 : clean ガードを通過したスキルについては、npx が即座に skills lock.json と .agents/skills/<name / を書き換える。ユーザー承認(Step 6)の前に変更が確定するため、承認しない場合は Step 6 の案内に従いリバートが必要。 書き込みスコープの制限( agent universal ) : npx skills add はエージェント/パス制限なしで実行すると、検出した各エージェント向けツリー( .claude/skills/ 等)へも書き込み得る(Issue 410)。しかし clean ガード・Step 5 のプレビュー・Step 6 のリバート・Step 7 の git add はいずれも skills lock.json と .agents/skills/<name / のみを対象としており、スコープ外への書き込みが発生すると WIP 上書き・レビュー迂回・「clean 報告後の dirty 残留」が起き得る。 agent universal により書き込みは .agents/skills/<name / と skills lock.json に限定され、他エージェントツリー( .claude/skills/ 等)へは書かない(実測: スクラッチリポジトリで確認済み)。万一 CLI のバージョン更新等でこの前提が崩れて書き込まれた場合も、npx 実行前後のスナップショット比較で fail closed に停止する(多層防御)。 Step 5: 当該スキルの差分を表示する git diff は未追跡ファイルを表示しない。Step 4 の clean ガード( git status porcelain )により npx skills add 実行前の当該ディレクトリは必ず clean であるため、 upstream 側でファイルが増えた ケースでは、その新規ファイルは例外なく未追跡になる 。tracked diff だけを見せて Step 6 の承認判断へ 進むと、その内容を一切確認しないまま承認できてしまうため、tracked 差分と未追跡ファイルの内容を分けて 両方提示する。 変更点を確認し、更新された computedHash の内容と未追跡ファイルの中身を合わせてユーザーに提示する。 checker( scripts/check skill local patches.sh )を持つリポジトリの場合 、この時点の diff は raw な upstream 差分 であり、local patch はまだ再適用されていない(Step 5.5 の再適用後の最終 diff と混同しないこと)。 Step 5.5: local patch を再適用して最終検証する(checker を持つリポジトリのみ) npx skills add の上書きで消費側リポジトリの local patch が worktree から消えているため、 stage より前に 再適用と最終検証を行う。checker(apply)は当該スキルディレクトリのほか durable patch( scripts/local patches/ )も変更・stage し得るため、(1) 却下・失敗時の厳密復元用に apply 前の index を snapshot し、(2) apply 後は変更集合が契約範囲( skills lock.json ・当該スキル・ scripts/local patches/ )に収まることを機械検証する。すべて成功した場合のみ Step 6 以降へ進める。 いずれかが非 0 の場合は fail closed で停止 し、Step 6・7(承認・stage)へ進まない。 local patch が欠けた状態を承認済みとして stage してはならない 。すべての失敗分岐は restore contract scope が契約範囲(当該スキル・ skills lock.json ・ scripts/local patches/ )の index + worktree を同期開始前へ自動復元してから終了する(契約範囲内の未追跡ファイルは削除せず一時ディレクトリへ退避して案内する。退避自体に失敗した場合は worktree を復元せず index のみで停止する)。範囲外 path の破壊が報告された場合のみ、 verify outside and checker の案内に従って範囲外を手動復旧してから原因を調査する。 Step 6: ユーザーに当該スキルの承認を求める 差分がある場合のみ、ユーザーに「この更新を適用してよいか」を確認する。Step 5 のプレビュー ( git ls files others exclude standard )・本 Step の拒否( git clean fd )・Step 7 の承認 ( git add )は同じ集合(追跡ファイルの変更 + 非 ignore の未追跡ファイル)を対象とする。 .gitignore 対象はいずれの経路でも扱わない。 checker を持つリポジトリの場合、承認の対象は Step 5.5 完了後の最終 diff (upstream 更新 + local patch 再適用を含む commit 候補。durable patch scripts/local patches/ を含む)であり、次で確認する。 git diff HEAD は未追跡ファイルを表示しないため、未追跡分は Step 5 と同じ手順( git ls files z others exclude standard で列挙し、 git diff no index / バイナリ判定で内容表示)を契約範囲の path に対して再実行し、tracked 差分と合わせて提示する: 却下された場合 は当該スキルのみ即座にリバートして 次スキルへ continue する(全体を中止しない)。 ただし直後の検証でリバート未完了を検出した場合は、この skip 継続の対象外としてループ全体を exit 1 で停止する(fail closed。未完了のまま次スキルへ進むと、残留した却下対象の変更が 次スキルの承認時に一緒に stage され得るため。Issue 417): Step 4 の clean ガードにより npx 実行前の当該ディレクトリは clean(未追跡含む)であることが保証されているため、 git clean で削除される未追跡ファイルは npx が作成したものに限られる。 git clean の対象は kebab case 検証済みの ${SKILL NAME} 配下のみに限定されており、リポジトリ全体には影響しない。 このリバートは「次スキルの npx skills add 実行前」に行うため、 skills lock.json から戻るのは当該スキル分のみである。 git checkout は HEAD ではなく index から復元するため、承認済みの他スキルの hash は index にも作業ツリーにも保持されており、影響を受けない。 checker を持つリポジトリの却下は次を使う (同期前 check・Step 5.5 の apply が当該スキルや durable patch の file を index へ stage している可能性があるため、 git checkout (index → worktree)だけでは戻らない。Step 4 で checker 初回実行より前に保存した PRE SYNC TREE (同期開始前の index snapshot)を source に、 契約パス限定 で index + worktree を復元する。npx 後に取得した snapshot を使ってはならない — 復元先が raw upstream 状態になり「同期前へ戻す」契約に反する。index 全体の git read tree も使わない — 範囲外 path の index まで書き換わり、範囲外の手動復旧案内と矛盾する): 対象は kebab case 検証済みの当該スキルディレクトリ配下・ skills lock.json ・durable patch( scripts/local patches/ )のみで、承認済みの他スキルの stage にも範囲外 path の index にも影響しない。Step 4・5.5 の 失敗経路 では同じ復元を restore contract scope が自動実行するため、この手動フェンスはユーザー却下時にのみ使う。却下の復元後検証(上記フェンス)が失敗した場合は、この却下自体を完了扱いにせずループ全体を exit 1 で停止する(「却下された場合は次スキルへ continue する」という上記の既定動作は、復元後検証が成功した場合にのみ成立する)。未追跡ファイルの退避( mktemp d / mktemp / git ls files / mkdir / mv )に失敗した場合は worktree への git restore を一切行わず fail closed で停止する(退避されていない唯一のコピーを無音で上書きしないため。Issue 418)。 Step 7: 承認されたスキルを stage する(ループ内で積み上げる) skills lock.json は単一 JSON ファイルのため行単位での部分ステージは現実的でない。しかし Step 1 の事前ガードで実行開始時の clean 状態を保証しているため、ファイル全体をステージしても sync 由来の変更のみが含まれ、無関係な編集が混入することはない。このコマンドをループ内で実行することで、複数スキルの全スキル sync でも処理した全スキルが過不足なく stage に積み上がる。 checker を持つリポジトリでは Step 5.5(apply + final check)の成功が stage の前提 であり、 computedHash は npx が書いた upstream 版の値のまま変更しない(local patch で hash を更新しない)。 Step 8: コミット提案(ループ後に1回だけ実行) ループ完了後、stage 済みの全承認スキルをまとめて1コミットにする。 ユーザーにコミットしてよいか確認する。承認済みスキルが1つもなかった場合(全却下・差分なし)はコミットせずその旨を伝える。 skills CLI のバージョン固定と更新手順 Why : npx skills add をバージョン未固定で実行すると、npx はローカルキャッシュに無い場合レジストリのその時点の最新版を確認なしで即時取得・実行する。 skills (vercel labs/skills)パッケージが乗っ取られた場合、これは任意コード実行の経路になる。しかもこの実行は Step 5 の差分確認・Step 6 のユーザー承認より 前 に走るため、source の Fandhe AI/<repo 完全一致検証では防げない。exact 版( X.Y.Z 。dist tag・ ^ / ~ レンジは禁止)への固定が信頼アンカーになる。 固定版の決め方 : 1. npm view skills version で現在の latest を確認する 2. npm view skills repository.url が vercel labs/skills であることを確認する 3. npm view skills time json 等で公開日時が不自然でないことを確認する 4. upstream リポジトリの該当タグ間の差分・リリースノートを確認し、問題なければ採用する 更新手順 : 1. scripts/skills lock update.sh の SKILLS CLI VERSION と、本ファイルの Step 4 フェンス内の SKILLS CLI VERSION を 同一コミット で更新する(値は完全一致させる) 2. node test skills/sync skills lock/tests/ で両ファイルの一致を検証する 3. universal が新版でも有効な agent id であることを確認する 。無効値へ変わっていた場合、CLI はエラー表示のうえ exit 0 の no op になる(実測: skills@1.5.22 で確認済み)ため、気付かずに運用すると「同期したつもりで何も更新されていない」状態になる。確認方法: スクラッチリポジトリで 1 スキルを実際に agent universal で実行し、出力に Invalid agents が出ないこと、および .agents/skills/<name / が実際に更新されることを確認する 4. 1 スキルで実際に実行し、差分が正常であること・書き込みが .agents/skills/<name / と skills lock.json のみに限定されていること( git status porcelain に .claude/ 等の他ツリーが現れないこと)を確認する 5. chore(sync skills lock): skills CLI を X.Y.Z へ更新 でコミットする fail closed : 固定版が解決できない場合(該当版の不存在・レジストリ障害)は npx が非ゼロ終了する。黙って最新版へフォールバックする経路は存在せず、dist tag・レンジ指定への書き換えも禁止する。 npx の失敗経路でも成功経路と同じスコープ外書き込み検査(後述の「多層防御」)を必ず実行する。両実行経路とも、 npx 行だけ errexit を無効化( set +e / set e )して終了コードを PIPESTATUS から保存し、成功・失敗いずれの経路でも事後のスコープ外検査を必ず実行する( set e / pipefail の下でこの無効化を欠くと、 npx の非ゼロ終了でパイプラインごとその場で異常終了し、事後検査・リバートに到達できないまま部分書き込みが残置される。Bugbot High 指摘)。ただし復元方法は経路ごとに異なる: 本ファイルの Step 4 フェンス側は呼び出し元シェルの errexit 状態( $ )を保存し、元々有効だった場合のみ set e で再有効化する条件付き復元であり、無条件 set e は行わない(このフェンスは errexit が無効なエージェント対話シェルへコピペ実行され得るため、無条件復元だと呼び出し元シェルへ errexit を新規有効化してしまい、同一シェルで後続する Step 6 却下コマンド等がガードの無い非ゼロ終了で途中 abort し得る。Issue 417)。一方 scripts/skills lock update.sh はファイル先頭で set euo pipefail を自ら宣言しており errexit 常時有効の前提が成立するため、同スクリプト側は無条件 set e 復元のままで正しい(詳細は同スクリプト内コメント参照)。 scripts/skills lock update.sh を単体実行した場合はスクリプト全体を exit 1 で停止する(この行の前後だけ set +e / set e を挟む理由は同スクリプト内のコメント参照)。一方、本ファイルの Step 4 フェンス(複数スキルをループで処理する経路)では、 npx の失敗を検出したら事後検査のうえ Step 6 の却下時と同じ手順( git checkout / git clean fd )で当該スキル分の部分書き込みをリバートしてから skip( continue )して次スキルへ進む — Step 1/3 の他の skip 分岐と同じ制御フローであり、ループ全体を停止させるものではない。事後検査・リバートを挟まずに skip すると、失敗が部分書き込み後に発生した場合の残置変更(スコープ内は次スキルの git add (Step 7)が承認済み変更と一緒に stage してしまう、スコープ外は後続処理から「元から存在した dirty 状態」と誤認され得る)を防げないため、両方とも必須の手順である。 スコープ外書き込みの検出( exit 1 )はこれとは別の停止経路であり、 continue ではなくループ全体を止める (詳細は次項「書き込みスコープの制限」を参照)。この停止経路は成功経路( NPX STATUS eq 0 )だけでなく npx 失敗 経路にも及ぶ: 失敗後の事後検査でスコープ外差分・状態シグネチャ不一致を検出した場合も、成功経路と同じ判定基準で NPX STATUS OUT OF SCOPE DIRT=1 を立ててループ全体を exit 1 で停止し、 continue で次スキルへ進まない(そのまま skip すると未リバートのスコープ外差分が「元から存在した dirty 状態」と誤認され得るため。Bugbot High 指摘)。 書き込みスコープの制限( agent universal とスコープ外検出) npx skills add にエージェント/パス制限を付けずに実行すると、CLI が検出した各エージェント向けツリー( .claude/skills/ 等)へも書き込み得る(Issue 410)。しかし clean ガード(前提条件節・Step 4)・プレビュー(Step 5)・リバート(Step 6)・承認 git add (Step 7)はいずれも skills lock.json と .agents/skills/<name / のみを対象としているため、スコープ外への書き込みが発生すると (1) WIP 上書き、(2) レビュー(プレビュー)迂回、(3) 「clean と報告した後の dirty 残留」が起き得る。 2 層で防ぐ: 1. 一次防御( agent universal ) : Step 4 の npx 呼び出しに agent universal を付け、書き込み先を union ストア( .agents/skills/<name / )と skills lock.json のみへ限定する。個別 agent 指定( claude code 等)は .agents/skills/ を経由せ