microsoft/apmでAgent Skillsを管理しつつ、Renovateで自動更新する

microsoft/apmでAgent Skillsを管理しつつ、Renovateで自動更新する

2026.05.11

はじめに

チームでサードパーティの Agent Skills を使う際、メンバー間で同じバージョンが入っていることが望ましいです。一方でスキル自体をリポジトリにコミットするのも作業が増えるため出来れば避けたいです。

ここでいうサードパーティスキルというのは以下のようなものです。プロジェクトに必要なスキルだけを選んで導入し、かつ上流の更新も追えるようにしたい、というニーズがあります。

https://github.com/vercel-labs/agent-skills

https://github.com/aws/agent-toolkit-for-aws

以上の理由から、Agent Skills をパッケージとしてバージョン管理したいと感じるケースは多いと思います。

各大手ベンダーが Agent Skills を管理する CLI を提供しており、microsoft/apmvercel-labs/skillsgh CLI 拡張の gh skills の 3 つがあります。

https://github.com/microsoft/apm

https://www.npmjs.com/package/skills

https://github.com/cli/cli

npm のように設定ファイルを利用してパッケージマネージャ的に扱えるのは、microsoft/apmvercel-labs/skills の 2 つです。

その中でも vercel-labs/skills (v1.5.6) は experimental_install の通り実験的です。lock がバージョン固定として機能せず、毎回上流の HEAD を取りに行くため、実行タイミングでの最新がローカルに展開されます。詳しくは 付録: experimental_install の挙動メモ を参照ください。

本記事では apm + Renovate の運用を紹介します。

apmのバージョンは以下を利用します。

$ apm --version
Agent Package Manager (APM) CLI version 0.12.4 (6aceef7)

設定

apmの設定ファイル作成

apm.yml を 0 から作る場合は apm init で雛形を作り、apm install で依存パッケージを追加する。

# 1. apm.yml の雛形を作成(targets: claude を直書き)
apm init . -y --target claude

# 2. 依存スキルをまとめて追加(commit SHA で版を固定)
apm install \
  'aws/agent-toolkit-for-aws/skills/developer-tools-skills/aws-cdk#750230758fbf23acd60d075dedd7ead4092127ce' \
  'aws/agent-toolkit-for-aws/skills/management-tools-skills/aws-cloudformation#750230758fbf23acd60d075dedd7ead4092127ce' \
  'aws/agent-toolkit-for-aws/skills/security-and-identity-skills/aws-iam#750230758fbf23acd60d075dedd7ead4092127ce' \
  'aws/agent-toolkit-for-aws/skills/operations-skills/aws-observability#750230758fbf23acd60d075dedd7ead4092127ce' \
  'aws/agent-toolkit-for-aws/skills/developer-tools-skills/aws-sdk-js-v3-usage#750230758fbf23acd60d075dedd7ead4092127ce' \
  'aws/agent-toolkit-for-aws/skills/compute-skills/connecting-lambda-to-api-gateway#750230758fbf23acd60d075dedd7ead4092127ce' \
  'aws/agent-toolkit-for-aws/skills/compute-skills/creating-api-gateway-stage#750230758fbf23acd60d075dedd7ead4092127ce' \
  'aws/agent-toolkit-for-aws/skills/security-and-identity-skills/creating-secrets-using-best-practices#750230758fbf23acd60d075dedd7ead4092127ce' \
  'aws/agent-toolkit-for-aws/skills/compute-skills/debugging-lambda-timeouts#750230758fbf23acd60d075dedd7ead4092127ce' \
  'aws/agent-toolkit-for-aws/skills/compute-skills/routing-traffic-with-route53-and-cloudfront#750230758fbf23acd60d075dedd7ead4092127ce' \
  'aws/agent-toolkit-for-aws/skills/operations-skills/setting-up-cloudwatch-alarm-notifications#750230758fbf23acd60d075dedd7ead4092127ce' \
  'aws/agent-toolkit-for-aws/skills/operations-skills/troubleshooting-application-failures#750230758fbf23acd60d075dedd7ead4092127ce' \
  'vercel-labs/agent-skills/skills/composition-patterns#ce3e64e468f8fa09a2d075d102771838061fdac0' \
  'vercel-labs/agent-skills/skills/react-best-practices#ce3e64e468f8fa09a2d075d102771838061fdac0' \
  'vercel-labs/agent-skills/skills/web-design-guidelines#ce3e64e468f8fa09a2d075d102771838061fdac0'

これによって追加される apm 関連のファイル・ディレクトリは以下のとおり。

.
├── apm.yml          # 既存(`apm install` で dependencies.apm に追記)
├── apm.lock.yaml    # NEW: 解決済み commit と content_hash を保存
├── .gitignore       # NEW (or 追記): `apm_modules/` が書き込まれる
├── apm_modules/     # NEW: 上流リポジトリのソースキャッシュ(gitignore 済み)
   ├── aws/agent-toolkit-for-aws/
   └── vercel-labs/agent-skills/
├── .agents/skills/  # NEW: apm が常に作る共通配置(universal agents 用)
   ├── aws-cdk/
   ├── aws-cloudformation/
   ├── aws-iam/
   ├── aws-observability/
   ├── aws-sdk-js-v3-usage/
   ├── composition-patterns/
   ├── connecting-lambda-to-api-gateway/
   ├── creating-api-gateway-stage/
   ├── creating-secrets-using-best-practices/
   ├── debugging-lambda-timeouts/
   ├── react-best-practices/
   ├── routing-traffic-with-route53-and-cloudfront/
   ├── setting-up-cloudwatch-alarm-notifications/
   ├── troubleshooting-application-failures/
   └── web-design-guidelines/
└── .claude/skills/  # NEW: targets: claude の配置先(`.agents/skills/` と同内容のフルコピー)
    ├── aws-cdk/
    ├── aws-cloudformation/
    ├── aws-iam/
    ├── aws-observability/
    ├── aws-sdk-js-v3-usage/
    ├── composition-patterns/
    ├── connecting-lambda-to-api-gateway/
    ├── creating-api-gateway-stage/
    ├── creating-secrets-using-best-practices/
    ├── debugging-lambda-timeouts/
    ├── react-best-practices/
    ├── routing-traffic-with-route53-and-cloudfront/
    ├── setting-up-cloudwatch-alarm-notifications/
    ├── troubleshooting-application-failures/
    └── web-design-guidelines/

余談: 設定ファイルは apm.ymlapm.lock.yaml なのは違和感があり、apm.yaml にrenameしましたが対応してませんでした。

展開済みのスキルを消して、apm.yml + apm.lock.yaml から apm install(引数なし)で復元できるかを確認する。

# スキル本体を削除(lock とマニフェストは残す)
rm -rf .agents/skills .claude/skills
$ apm install
[>] Installing dependencies from apm.yml...
[i] Targets: claude  (source: apm.yml)
  [+] github.com/aws/agent-toolkit-for-aws/skills/compute-skills/connecting-lambda-to-api-gateway#750230758fbf23acd60d075dedd7ead4092127ce #750230758fbf23acd60d075dedd7ead4092127ce @75023075 (cached)
  |-- Skill integrated -> .agents/skills/, .claude/skills/
  [+] github.com/aws/agent-toolkit-for-aws/skills/compute-skills/creating-api-gateway-stage#750230758fbf23acd60d075dedd7ead4092127ce #750230758fbf23acd60d075dedd7ead4092127ce @75023075 (cached)
  |-- Skill integrated -> .agents/skills/, .claude/skills/
  [+] github.com/aws/agent-toolkit-for-aws/skills/compute-skills/debugging-lambda-timeouts#750230758fbf23acd60d075dedd7ead4092127ce #750230758fbf23acd60d075dedd7ead4092127ce @75023075 (cached)
  |-- Skill integrated -> .agents/skills/, .claude/skills/
  [+] github.com/aws/agent-toolkit-for-aws/skills/compute-skills/routing-traffic-with-route53-and-cloudfront#750230758fbf23acd60d075dedd7ead4092127ce #750230758fbf23acd60d075dedd7ead4092127ce @75023075 (cached)
  |-- Skill integrated -> .agents/skills/, .claude/skills/
  [+] github.com/aws/agent-toolkit-for-aws/skills/developer-tools-skills/aws-cdk#750230758fbf23acd60d075dedd7ead4092127ce #750230758fbf23acd60d075dedd7ead4092127ce @75023075 (cached)
  |-- Skill integrated -> .agents/skills/, .claude/skills/
  [+] github.com/aws/agent-toolkit-for-aws/skills/developer-tools-skills/aws-sdk-js-v3-usage#750230758fbf23acd60d075dedd7ead4092127ce #750230758fbf23acd60d075dedd7ead4092127ce @75023075 (cached)
  |-- Skill integrated -> .agents/skills/, .claude/skills/
  [+] github.com/aws/agent-toolkit-for-aws/skills/management-tools-skills/aws-cloudformation#750230758fbf23acd60d075dedd7ead4092127ce #750230758fbf23acd60d075dedd7ead4092127ce @75023075 (cached)
  |-- Skill integrated -> .agents/skills/, .claude/skills/
  [+] github.com/aws/agent-toolkit-for-aws/skills/operations-skills/aws-observability#750230758fbf23acd60d075dedd7ead4092127ce #750230758fbf23acd60d075dedd7ead4092127ce @75023075 (cached)
  |-- Skill integrated -> .agents/skills/, .claude/skills/
  [+] github.com/aws/agent-toolkit-for-aws/skills/operations-skills/setting-up-cloudwatch-alarm-notifications#750230758fbf23acd60d075dedd7ead4092127ce #750230758fbf23acd60d075dedd7ead4092127ce @75023075 (cached)
  |-- Skill integrated -> .agents/skills/, .claude/skills/
  [+] github.com/aws/agent-toolkit-for-aws/skills/operations-skills/troubleshooting-application-failures#750230758fbf23acd60d075dedd7ead4092127ce #750230758fbf23acd60d075dedd7ead4092127ce @75023075 (cached)
  |-- Skill integrated -> .agents/skills/, .claude/skills/
  [+] github.com/aws/agent-toolkit-for-aws/skills/security-and-identity-skills/aws-iam#750230758fbf23acd60d075dedd7ead4092127ce #750230758fbf23acd60d075dedd7ead4092127ce @75023075 (cached)
  |-- Skill integrated -> .agents/skills/, .claude/skills/
  [+] github.com/aws/agent-toolkit-for-aws/skills/security-and-identity-skills/creating-secrets-using-best-practices#750230758fbf23acd60d075dedd7ead4092127ce #750230758fbf23acd60d075dedd7ead4092127ce @75023075 (cached)
  |-- Skill integrated -> .agents/skills/, .claude/skills/
  [+] github.com/vercel-labs/agent-skills/skills/composition-patterns#ce3e64e468f8fa09a2d075d102771838061fdac0 #ce3e64e468f8fa09a2d075d102771838061fdac0 @ce3e64e4 (cached)
  |-- Skill integrated -> .agents/skills/, .claude/skills/
  [+] github.com/vercel-labs/agent-skills/skills/react-best-practices#ce3e64e468f8fa09a2d075d102771838061fdac0 #ce3e64e468f8fa09a2d075d102771838061fdac0 @ce3e64e4 (cached)
  |-- Skill integrated -> .agents/skills/, .claude/skills/
  [+] github.com/vercel-labs/agent-skills/skills/web-design-guidelines#ce3e64e468f8fa09a2d075d102771838061fdac0 #ce3e64e468f8fa09a2d075d102771838061fdac0 @ce3e64e4 (cached)
  |-- Skill integrated -> .agents/skills/, .claude/skills/

[*] Installed 15 APM dependencies in 15.2s.

各行に (cached) が付いているのは apm_modules/ のキャッシュが効いて fetch が走っていないため。配置先は初回 install と同じく .agents/skills/.claude/skills/ の両方で、apm.yml + apm.lock.yaml だけで状態を再現できる。

lockの整合性確認

apm install には --frozen-lockfile 相当が無く、install 単体では lock の整合性を検証しない。代わりに apm audit --ci がロックファイルと展開済みファイルの drift を見るので、CI で改ざんを止めたい場合はこれを噛ませる。

クリーンな状態では 9 個のチェックが全部 pass する。

$ apm audit --ci
[!] No org policy found at org:shuntaka9576/.github; enforcement skipped (set policy.fetch_failure_default=block in apm.yml to fail closed)
[>] Replaying install (cache-only)...
[+] Replayed 15 package(s)
[>] Diffing scratch vs working tree...
[+] No drift detected

                                      [>] APM Policy Compliance
┏━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
 Status Check Message
┡━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
 [+]      │ lockfile-exists          │ Lockfile present                                            │
 [+]      │ ref-consistency          │ All dependency refs match lockfile                          │
 [+]      │ deployed-files-present   │ All deployed files present on disk                          │
 [+]      │ no-orphaned-packages     │ No orphaned packages in lockfile                            │
 [+]      │ skill-subset-consistency │ Skill subset selections match lockfile                      │
 [+]      │ config-consistency       │ No MCP configs to check                                     │
 [+]      │ content-integrity        │ No critical hidden Unicode or hash drift detected           │
 [+]      │ includes-consent         │ No local content deployed -- includes consent check skipped │
 [+]      │ drift                    │ no drift detected against lockfile                          │
└──────────┴──────────────────────────┴─────────────────────────────────────────────────────────────┘

[*] All 9 check(s) passed

vercel-labs/skillscomputedHash 改ざんと同じ流れで、apm.lock.yamlresolved_commit をダミー値に書き換えてみる。aws 系の 12 エントリは同じ commit を指しているので、sed で一括置換すれば 12 エントリ全部が改ざんされる。

# バックアップ
cp apm.lock.yaml /tmp/apm.lock.yaml.bak

# resolved_commit をダミー値(all-zero SHA)に書き換え
sed -i.bak 's/resolved_commit: 750230758fbf23acd60d075dedd7ead4092127ce/resolved_commit: 0000000000000000000000000000000000000000/g' apm.lock.yaml

diff apm.lock.yaml /tmp/apm.lock.yaml.bak

この状態で audit にかけると drift チェックで落ちる。

$ apm audit --ci
[!] No org policy found at org:shuntaka9576/.github; enforcement skipped (set policy.fetch_failure_default=block in apm.yml to fail closed) [>] Replaying install (cache-only)...

                                                                                                                                              [>] APM Policy Compliance
┏━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
 Status Check Message
┡━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
 [+]      │ lockfile-exists          │ Lockfile present                                                                                                                                                                                                                                                            │
 [+]      │ ref-consistency          │ All dependency refs match lockfile                                                                                                                                                                                                                                          │
 [+]      │ deployed-files-present   │ All deployed files present on disk                                                                                                                                                                                                                                          │
 [+]      │ no-orphaned-packages     │ No orphaned packages in lockfile                                                                                                                                                                                                                                            │
 [+]      │ skill-subset-consistency │ Skill subset selections match lockfile                                                                                                                                                                                                                                      │
 [+]      │ config-consistency       │ No MCP configs to check                                                                                                                                                                                                                                                     │
 [+]      │ content-integrity        │ No critical hidden Unicode or hash drift detected                                                                                                                                                                                                                           │
 [+]      │ includes-consent         │ No local content deployed -- includes consent check skipped                                                                                                                                                                                                                 │
 drift drift replay aborted: cannot replay aws/agent-toolkit-for-aws: lockfile entry has no resolved_commit (cache freshness unverifiable). Re-run 'apm install' with a pinned ref (commit, tag, or specific branch HEAD) before audit.; run 'apm install' to refresh apm_modules
 cache
└──────────┴──────────────────────────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

[x] 1 of 9 check(s) failed
$ echo $?
1

検証後は cp /tmp/apm.lock.yaml.bak apm.lock.yaml で戻すか apm install で再生成する。

drift チェックは「lock を頼りに apm_modules/ のキャッシュからスキルを一旦別の場所に再構成し、ワーキングツリーと差分を取る」もの。lock の resolved_commit が改ざんされていると、その commit がキャッシュに無いので再構成ができず落ちる。

なお再構成ループは for lock_dep in lock.get_all_dependencies() の中で最初の例外をそのまま投げる作りなので、1 件失敗した時点で残りのエントリは検証されない(src/apm_cli/install/drift.py#L448)。

他のフィールドも 1 個ずつ書き換えて audit にかけた結果は以下。

改ざんフィールド 役割 検知
content_hash install 後に書き残された skill フォルダの sha256(事後スナップショット) されない
resolved_commit 依存スペックを解決した結果の commit SHA される
resolved_ref 解決に使った ref(#sha 指定時はその値) される
deployed_files スキルが実際に展開されたパスの一覧 される
virtual_path 上流リポジトリ内のスキルのサブパス される

content_hash だけは audit の判定に使われておらず素通りする(事後スナップショット用途)。それ以外の改ざんは apm audit --ci で落ちる。CI で頼るのは content_hash ではなく drift / ref-consistency / deployed-files-present の組み合わせ。

Renovate 設定

apm.ymlowner/repo/.../skill#<sha> 形式の依存を Renovate に拾わせるには、customManagers で正規表現マッチを追加する。apm.yml 内に各依存ごとの PR を作りたくない場合は packageRules でグルーピングしておく。

renovate.json
{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": ["config:recommended"],
  "customManagers": [
    {
      "customType": "regex",
      "description": "Update APM dependencies pinned by commit sha in apm.yml",
      "managerFilePatterns": ["/(^|/)apm\\.yml$/"],
      "matchStrings": [
        "- (?<depName>[^/\\s]+/[^/\\s#]+)/[^#\\s]+#(?<currentDigest>[a-f0-9]{40})"
      ],
      "packageNameTemplate": "https://github.com/{{depName}}",
      "datasourceTemplate": "git-refs",
      "currentValueTemplate": "main"
    }
  ],
  "packageRules": [
    {
      "description": "Group all APM dependency updates (apm.yml)",
      "matchFileNames": ["apm.yml"],
      "groupName": "apm"
    }
  ]
}

regex は - <owner>/<repo>/<...任意のパス...>#<40桁sha> から depName = owner/repocurrentDigest = sha を取り出す。datasource: git-refs + currentValue: main で各リポジトリの main HEAD を追いに行き、SHA が動いていればまとめて PR を上げる。

実際に Renovate が apm.yml の commit SHA を見つけて PR を作ってくれる様子。aws/agent-toolkit-for-awsvercel-labs/agent-skills の 2 リポジトリの digest 更新が 1 PR にグルーピングされている。

Renovate が apm.yml の依存を digest 更新する PR

apm.yml の各依存行末の commit SHA だけが置き換わる差分が確認できる。

apm.yml の commit SHA だけが置き換わる diff

実際のPRはこちらです。

https://github.com/shuntaka9576/shuntaka-dev/pull/366

Renovate PR で lock を自動同期する GitHub Actions

Renovateのregexマネージャーは「ファイル内の文字列パターンを置換する」だけなので、apm.yml の SHA は書き換えてくれるが apm.lock.yaml は更新されません。そのままマージすると apm.ymlapm.lock.yamlのSHAがズレた状態になります。Renovate PRに対してapm installを走らせてlockの更新をしてcommitするワークフローを定義します。

.github/workflows/renovate-apm-update.yaml
name: Renovate APM Update

on:
  pull_request:
    paths:
      - 'apm.yml'

permissions:
  contents: write

jobs:
  update-and-audit:
    if: github.actor == 'renovate[bot]'
    runs-on: ubuntu-latest
    timeout-minutes: 15
    env:
      APM_VERSION: 0.13.0
      APM_SHA256: ed02b1103ef4bc49559a81f7e109b424a49d95cf978515ad224e5eeb701b1f8a
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          ref: ${{ github.head_ref }}

      - name: Install APM
        run: |
          set -euo pipefail
          curl -sL "https://github.com/microsoft/apm/releases/download/v${APM_VERSION}/apm-linux-x86_64.tar.gz" -o apm.tar.gz
          echo "${APM_SHA256}  apm.tar.gz" | sha256sum -c -
          mkdir -p "${HOME}/.apm"
          tar xzf apm.tar.gz -C "${HOME}/.apm" --strip-components=1
          rm apm.tar.gz
          echo "${HOME}/.apm" >> "${GITHUB_PATH}"

      - name: apm install
        run: apm install -t claude

      - name: apm audit (lockfile + drift + content scan)
        run: apm audit --ci

      - name: Commit apm.lock.yaml
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add apm.lock.yaml
          git diff --staged --quiet || git commit -m "chore: sync apm.lock.yaml"
          git push

ポイントは installauditcommit の順序。apm installで取得・展開した時点ではまだローカルにしか変更が無いので、apm audit --ci で隠し Unicode・drift・lockfile 整合性に問題があれば exit 1 で止まり、commit には進まない。検証を通過した lock だけが push されます。

実際にRenovate PRに対してこのワークフローが動作し、apm.lock.yamlの同期コミットが追加されている様子を確認できます。

https://github.com/shuntaka9576/shuntaka-dev/pull/375/commits/22f7a30dcd820cb032e6504cb1c3895497b6501d

さいごに

apm は fetch がかなり遅く、まだまだ experimental なところも多い印象です。現状チームである程度厳密に管理したい場合はいい手段かなと思っています。各社からツールが提供されており、今後も定期的に試してみたいと思います。

付録: vercel-labs/skills の experimental_install の挙動メモ

experimental_ プレフィックスのとおり将来変わる前提。2026-05-11(v1.5.6)の挙動を示します。

  • lock の computedHash は install 時に検証されず、改ざんしても上流の値で黙って上書きされる
  • owner/repo で追加すると lock に ref が記録されず、experimental_install のたびにデフォルトブランチの HEAD を取得する
  • computedHash は skill フォルダ内容の sha256 で外部から再現できるが、上流の特定コミットには紐付かないため「lock の版を後追い検証する」用途には使えない

computedHash は検証されない

lock の computedHash を別の値に書き換えても experimental_install がエラーにならず、正規の値で黙って上書きされることを示す。手順は次の 4 ステップ。

  1. スキルを 1 つ install し、正規の lock を作る
  2. lock をバックアップしてから computedHash をダミー値に改ざん、ローカルのスキル本体も削除
  3. 改ざんした lock で experimental_install を再実行
  4. lock の差分とスキル本体の有無を確認

1. セットアップ(正規の lock を作る)

bunx skills add aws/agent-toolkit-for-aws --skill aws-cdk -a claude-code -y
cp skills-lock.json /tmp/skills-lock.json.bak

この時点で skills-lock.json の中身は次のようになる。computedHash が正規値。

skills-lock.json
{
  "version": 1,
  "skills": {
    "aws-cdk": {
      "source": "aws/agent-toolkit-for-aws",
      "sourceType": "github",
      "computedHash": "4de7d2614f4884c8f0c502061388ef3595ae457a0a0859070c5cd27141f757c9"
    }
  }
}

2. lock の改ざん + スキル本体の削除

computedHash をダミー値 (00...deaddead) に書き換え、ローカルのスキルディレクトリも消す。jq は同じファイルへ直書きできないので、temp に出してから上書きする。

jq '.skills["aws-cdk"].computedHash = "00000000000000000000000000000000000000000000000000000000deaddead"' \
  skills-lock.json > /tmp/skills-lock.tampered.json

mv /tmp/skills-lock.tampered.json skills-lock.json

rm -rf .claude/skills/aws-cdk/

この時点で skills-lock.jsoncomputedHash はダミー値になっている。

skills-lock.json
{
  "version": 1,
  "skills": {
    "aws-cdk": {
      "source": "aws/agent-toolkit-for-aws",
      "sourceType": "github",
      "computedHash": "00000000000000000000000000000000000000000000000000000000deaddead"
    }
  }
}

3. 改ざん後の再 install

bunx skills experimental_install

4. 結果の確認

experimental_install は exit 0 で成功し、改ざん検知の警告もエラーも出ない。

lock が正規値に戻っているかを確認すると、diff も exit 0 で差分なし。computedHash は黙って正規値に上書きされている。

$ diff /tmp/skills-lock.json.bak skills-lock.json
$ echo $?
0

スキル本体も上流から再取得されてディレクトリが復活している。なお experimental_install は元の -a claude-code を引き継がず、getUniversalAgents() の返す universal agent 群に対して .agents/skills/ 配下にのみ展開する (src/install.ts#L30-L31)。そのため .claude/skills/aws-cdk/ は復元されない。

$ ls .agents/skills/aws-cdk/
references  SKILL.md

lock の computedHash は install 時の検証には使われず、install 後にスナップショットとして書き残されているだけ、ということがわかる。

上流の HEAD を取っているが lock からは版を特定できない

lock には ref も commit hash も書かれていない。source / sourceType / computedHash だけ。

skills-lock.json
{
  "version": 1,
  "skills": {
    "aws-cdk": {
      "source": "aws/agent-toolkit-for-aws",
      "sourceType": "github",
      "computedHash": "4de7d2614f4884c8f0c502061388ef3595ae457a0a0859070c5cd27141f757c9"
    }
  }
}

ソース上、lock entry は ref?: string を持ち、bunx skills add owner/repo#<ref> のように指定したときだけ値が入る。#ref 無しで追加すると ref は undefined になり、experimental_install はデフォルトブランチ HEAD をそのまま取得。

https://github.com/vercel-labs/skills/blob/c99a72b371b5b4da865f5afa87c5a686f3a46766/src/install.ts#L43

実際、ローカルに展開された SKILL.md と上流デフォルトブランチ main の raw を sha256 で比較すると一致する。

$ shasum -a 256 .agents/skills/aws-cdk/SKILL.md
4641ea1414ca38307edd97ad71f02dc5d382d7f75130ca037ceef7418c22631d

$ curl -fsSL "https://raw.githubusercontent.com/aws/agent-toolkit-for-aws/main/plugins/aws-core/skills/aws-cdk/SKILL.md" | shasum -a 256
4641ea1414ca38307edd97ad71f02dc5d382d7f75130ca037ceef7418c22631d

lock の computedHash は単一ファイルの sha256 や git blob sha ではなく、skill フォルダ内のファイルを相対パスでソートして「相対パス + 中身」を順に sha256 に流し込んだフォルダハッシュとなる。

https://github.com/vercel-labs/skills/blob/c99a72b371b5b4da865f5afa87c5a686f3a46766/src/local-lock.ts#L108-L123

このアルゴリズムを node でなぞるとローカルでも同じ値が出る。

$ node -e '
const { readdir, readFile } = require("fs/promises");
const { join, relative } = require("path");
const { createHash } = require("crypto");
async function collect(b,c,r){const es=await readdir(c,{withFileTypes:true});await Promise.all(es.map(async e=>{const f=join(c,e.name);if(e.isDirectory()){if(e.name===".git"||e.name==="node_modules")return;await collect(b,f,r);}else if(e.isFile()){r.push({relativePath:relative(b,f).split("\\").join("/"),content:await readFile(f)});}}));}
(async()=>{const d=".agents/skills/aws-cdk";const fs=[];await collect(d,d,fs);fs.sort((a,b)=>a.relativePath.localeCompare(b.relativePath));const h=createHash("sha256");for(const f of fs){h.update(f.relativePath);h.update(f.content);}console.log(h.digest("hex"));})();
'
4de7d2614f4884c8f0c502061388ef3595ae457a0a0859070c5cd27141f757c9

つまり computedHash 自体は外部から再現可能だが、ハッシュ対象が「ローカルに展開済みのフォルダ」であって特定の upstream コミットではないため、「lock 単独でどの版を取ってきたか」を特定できないことが分かる。


生成AI活用はクラスメソッドにお任せ

過去に支援してきた生成AIの支援実績100+を元にホワイトペーパーを作成しました。御社が抱えている課題のうち、どれが解決できて、どのようなサービスが受けられるのか?4つのフェーズに分けてまとめています。どうぞお気軽にご覧ください。

生成AI資料イメージ

無料でダウンロードする

この記事をシェアする

関連記事