既存ソースコードからAgent Skills を抽出作成する
はじめに
Agent Skills、すごい便利ですよね。皆様はAgent Skills お使いでしょうか。
私は他力本願なので、最近は開発作業も含め、なんでもAI Agentに任せて実施させたいと考えてしまいます。
AI駆動開発といえば、新規の開発案件が得意領域な印象ですが、古今東西の開発プロジェクトは、新規開発!刷新開発!イケイケどんどん開発!という案件ばかりではなく、
コストなどのやんごとなき事情により、既存ソースコードをベースにしつつモダナイズ、という方向の案件もまだまだ存在します。
そのほうがストーリー的に通しやすい点も痛いほどわかります。
しかしながら、いざいざ既存コードベースの案件が開始となると、既存コードの解読という、百歩譲って、、いや、百万歩ぐらい譲って、控えめな地獄、がありまして。
そんななか、
「既存コードからこのプロジェクト特有のコーディングなどをAgent Akillsにすれば良いのでは。。?」
と思い、本記事の作成に至ります。
伝えたいこと
そんなこんなで、先に結論です。
- 既存コードから
skill-creatorを使用してSKILL.mdを生成することで、一定のナレッジ化が可能!
技術記事というよりはアイデアの備忘録としてみていただければと。
Agent Skills とは
GitHub Copilot や Anthropic Claude のエージェントが、特定のタスクを実行する際に参照する「指示書」のようなものです。SKILL.md という Markdown ファイルに、YAML フロントマター(name / description)と本文を書いておき、エージェントが状況に応じて自動的に読み込みます。
<skill-name>/
├── SKILL.md # 必須: フロントマター + 本文
├── references/ # 任意: 詳細ドキュメント
├── scripts/ # 任意: 実行スクリプト
└── assets/ # 任意: テンプレート等
GitHub Copilot では、プロジェクトルートディレクトリの.github/skills/フォルダを作成し、さらにskill-nameフォルダの中にスキル一式を格納します。

SKILL.md のdescription フィールドで、「いつこのスキルを使うべきか」を具体的に書いておくと、エージェントがそれをトリガー条件として参照します。
要するに、AI Agentと自然言語のやりとりで、AI Agent が「スキル使おう」、と判断すれば適宜Agent Skillを使用してくれる、ということです。
前提
想定読者
- プロジェクトの既存コードの扱いに苦慮されている方
環境
本記事では以下を使用します。
- Visual Studio Code
- GitHub Copilot Pro+
- モデル: Claude Opus 4.7
- 利用するメタAgent Skill: [skill-creator]
上記のGitHubを参考して、以下のようにGitHub Copilot用として少し書き換えてます。
GitHub Copilot用skill-creator例
---
name: skill-creator
description: >
GitHub Copilot 用のエージェントスキルを新規作成・編集・改善するためのスキル。
ユーザーが「スキルを作りたい」「SKILL.md を書いて」「スキルを改善したい」「スキルをテストしたい」
などと言った場合は必ずこのスキルを使用すること。スキルの設計、YAML フロントマターの記述、
スキルディレクトリ構成、テストケースの作成、品質評価まで一貫してサポートする。
---
# Skill Creator for GitHub Copilot
GitHub Copilot(クラウドエージェント・Copilot CLI・VS Code エージェントモード)で動作する
エージェントスキルを作成・改善するためのガイド。
---
## エージェントスキルの基本構造
GitHub Copilot のエージェントスキルは、以下の場所に配置する:
| 種類 | 配置場所 |
|------|----------|
| プロジェクトスキル | `.github/skills/<skill-name>/` または `.claude/skills/<skill-name>/` |
| 個人スキル(共有) | `~/.copilot/skills/<skill-name>/` または `~/.claude/skills/<skill-name>/` |
各スキルのディレクトリ構成:
```
<skill-name>/
├── SKILL.md # 必須:スキルの本体(YAML フロントマター + Markdown 本文)
├── scripts/ # 任意:実行スクリプト
├── references/ # 任意:参照ドキュメント
└── assets/ # 任意:テンプレートや画像などのリソース
```
> **命名規則**: ディレクトリ名・`name` フィールドは小文字 + ハイフン区切り(例: `webapp-testing`)
---
## SKILL.md の書き方
### YAML フロントマター(必須)
```yaml
---
name: <skill-name> # 必須:スキルの一意な識別子(小文字・ハイフン区切り)
description: > # 必須:スキルの説明とトリガー条件
このスキルが何をするか、いつ使うべきかを記述する。
「〜と言われたら必ずこのスキルを使う」という形で明示的に書くと
Copilot がより確実にスキルを選択する。
license: MIT # 任意:ライセンス情報
---
```
### description フィールドのコツ
Copilot はスキルの `description` を見てスキルを使うか判断する。
**アンダートリガー(使われない)を防ぐため、使うべき状況を具体的に列挙する:**
```yaml
# 悪い例(曖昧すぎる)
description: コードのレビューを行うスキル。
# 良い例(具体的でプッシュが強い)
description: >
コードレビューを自動化するスキル。ユーザーが「レビューして」「コードを確認して」
「PR をチェックして」「品質を見て」などと言ったら必ずこのスキルを使うこと。
コーディング規約の確認、バグの検出、改善提案まで一貫して行う。
```
### Markdown 本文のベストプラクティス
- 500 行以内に収める(超える場合は references/ に分割してポインタを書く)
- Copilot への指示は具体的なステップで書く(番号付きリスト推奨)
- スクリプトや参照ファイルを使うタイミングと方法を明示する
- 大きな参照ファイル(300 行超)には目次を付ける
---
## スキル作成の手順
### ステップ 1:目的と要件の明確化
以下を確認してからスキルを書く:
1. このスキルで Copilot に何をさせたいか?
2. どんなユーザープロンプトでトリガーさせたいか?
3. 期待する出力フォーマットは何か?
4. 外部ツール・MCP サーバーへの依存はあるか?
5. エッジケースや例外処理はどうするか?
### ステップ 2:SKILL.md の初稿を書く
```markdown
---
name: <skill-name>
description: >
<スキルの目的> 。ユーザーが「<トリガーワード1>」「<トリガーワード2>」などと
言った場合は必ずこのスキルを使うこと。
---
# <スキル名>
## 概要
<このスキルが何をするかの簡潔な説明>
## 手順
1. <ステップ1>
2. <ステップ2>
3. <ステップ3>
## 注意事項
- <重要な制約や注意点>
```
### ステップ 3:スキルのテスト
スキルを配置した後、以下のようなテストプロンプトで動作確認する:
- スキルが意図通りトリガーされるか?
- 出力の品質は期待通りか?
- エッジケースで正しく動作するか?
テストプロンプトの例(`github-actions-failure-debugging` スキルの場合):
```
PR の GitHub Actions ワークフローが失敗している。原因を調査して修正してほしい。
```
### ステップ 4:反復改善
テスト結果を見てスキルを改善する。特に以下を確認:
- `description` がスキルのトリガーに十分明示的か?
- 手順が具体的で Copilot が迷わず実行できるか?
- 不要な曖昧さや重複はないか?
---
## スキルの例
### シンプルなスキル例(GitHub Actions デバッグ)
ファイルパス: `.github/skills/github-actions-failure-debugging/SKILL.md`
```markdown
---
name: github-actions-failure-debugging
description: >
失敗した GitHub Actions ワークフローをデバッグするためのガイド。
ユーザーが「Actions が失敗した」「CI が落ちた」「ワークフローのエラーを調べて」
などと言ったら必ずこのスキルを使うこと。
---
## GitHub Actions 失敗時のデバッグ手順
GitHub MCP サーバーのツールを使い、以下の手順でデバッグする:
1. `list_workflow_runs` ツールで PR の最近のワークフロー実行状況を確認する
2. `summarize_job_log_failures` ツールで失敗したジョブのログ要約を取得する
3. 必要に応じて `get_job_logs` または `get_workflow_run_logs` で詳細ログを確認する
4. 自分の環境で失敗を再現できるか試みる
5. 修正を実装し、再現できた場合は修正後に失敗しないことを確認してからコミットする
```
### 複数ドメインに対応するスキル例(クラウドデプロイ)
ファイルパス: `.github/skills/cloud-deploy/`
```
cloud-deploy/
├── SKILL.md # ワークフロー全体と使うリファレンスの選択ロジック
└── references/
├── aws.md # AWS 固有の手順
├── gcp.md # GCP 固有の手順
└── azure.md # Azure 固有の手順
```
`SKILL.md` 本文で「ユーザーが指定したクラウドに応じて references/ の該当ファイルを読む」と指示する。
---
## スキルとカスタム命令の使い分け
| 用途 | 推奨する方法 |
|------|-------------|
| ほぼ全タスクに関係する簡潔な指示(コーディング規約など) | カスタム命令(`.github/copilot-instructions.md`) |
| 特定タスクにのみ必要な詳細な手順 | エージェントスキル(`SKILL.md`) |
---
## チェックリスト
スキルを公開・共有する前に確認:
- [ ] `name` フィールドが小文字・ハイフン区切りになっているか
- [ ] `description` にトリガー条件が明示されているか(「〜のときは必ずこのスキルを使う」)
- [ ] Markdown 本文が 500 行以内か(超える場合は references/ に分割したか)
- [ ] スクリプトや外部ファイルを使う場合、いつ・どのように使うかを本文に書いたか
- [ ] テストプロンプトでスキルが正しくトリガーされることを確認したか
- [ ] ディレクトリ名と `name` フィールドが一致しているか
実践
サンプル既存コードの準備
完全無欠のBlazorの初期サンプルプログラムで恐縮ですが、.NET 10 で 作成した直後のBlazor初期テンプレートを例に進めます。
このBlazorの初期サンプルプログラムを既設コードとして、Agent Skillsの抽出を図ります。

skill-creator スキルの起動
いざいざ既存コードからAgent Skillsを抽出します。
おもむろにVS Code を立ち上げ、GitHub Copilotチャットにて、エージェントモードを指定、モデルは Claude Opus 4.7 を選択します。
そのうえで、以下のようなプロンプトを投げます。
このプロジェクト内の既存コードから、コーディング規約や設計パターンのノウハウを抽出して SKILL.md を作ってください。

skill-creator が起動し、無事、SKILL.mdが作成されました。

抽出された SKILL.md の例
出力された SKILL.md は、以下となりました。
抽出された SKILL.md
---
name: blazorapp15-conventions
description: >
BlazorApp15 (Blazor WebAssembly / .NET 10) プロジェクトのコーディング規約と
設計パターンを示すスキル。ユーザーが「ページを追加して」「コンポーネントを作って」
「Blazor の規約に従って書いて」「このプロジェクトのスタイルで実装して」
などと言った場合は必ずこのスキルを使うこと。プロジェクト構成、命名規則、
コンポーネント記法、ルーティング、HTTP データ取得の作法を一貫して提供する。
---
# BlazorApp15 コーディング規約スキル
このプロジェクトは **Blazor WebAssembly スタンドアロン (.NET 10)** のテンプレート構成。
新規ページ・コンポーネント・サービスを追加する際は、以下の規約に従うこと。
---
## 1. プロジェクト構成
```
BlazorApp15/
├── Program.cs # WebAssemblyHostBuilder のエントリポイント
├── App.razor # <Router> 定義(NotFoundPage 付き)
├── _Imports.razor # 全 .razor で共有する using
├── BlazorApp15.csproj # SDK=Microsoft.NET.Sdk.BlazorWebAssembly
├── Layout/ # MainLayout, NavMenu とそれぞれの .razor.css
├── Pages/ # @page ディレクティブを持つルーティング対象
└── wwwroot/ # 静的アセット (index.html, css/, lib/, sample-data/)
```
- ルーティング対象(`@page` 持ち)は必ず [BlazorApp15/Pages/](BlazorApp15/Pages/) に置く。
- レイアウト系コンポーネントは [BlazorApp15/Layout/](BlazorApp15/Layout/) に置く。
- 静的データ・JSON・画像は [BlazorApp15/wwwroot/](BlazorApp15/wwwroot/) 配下に置く(例: `sample-data/weather.json`)。
---
## 2. csproj とフレームワーク前提
[BlazorApp15/BlazorApp15.csproj](BlazorApp15/BlazorApp15.csproj) で以下が有効:
- `TargetFramework`: `net10.0`
- `Nullable`: `enable`(参照型に `?` を付けて null 許容を明示)
- `ImplicitUsings`: `enable`(基本 using は省略可)
- SDK: `Microsoft.NET.Sdk.BlazorWebAssembly`
新規コードを書くときも、これらの前提(NRT 有効・ImplicitUsings 有効)を維持すること。
---
## 3. コンポーネント記法(Razor)
### 3.1 構造のパターン
[BlazorApp15/Pages/Counter.razor](BlazorApp15/Pages/Counter.razor) や [BlazorApp15/Pages/Weather.razor](BlazorApp15/Pages/Weather.razor) に倣い、1 ファイル内で
**ディレクティブ → マークアップ → `@code` ブロック** の順で書く。
```razor
@page "/your-route"
@inject HttpClient Http
<PageTitle>Your Page</PageTitle>
<h1>見出し</h1>
@if (data == null)
{
<p><em>Loading...</em></p>
}
else
{
<!-- データ表示 -->
}
@code {
private YourModel[]? data;
protected override async Task OnInitializedAsync()
{
data = await Http.GetFromJsonAsync<YourModel[]>("sample-data/your.json");
}
}
```
### 3.2 規約
- ルーティング可能なページは必ず `@page "/..."` を先頭行に書く。
- ブラウザのタブタイトルは `<PageTitle>` で設定([BlazorApp15/Pages/Counter.razor](BlazorApp15/Pages/Counter.razor) と同様)。
- ページ最上部に `<h1>` を 1 つ置く(`App.razor` の `FocusOnNavigate` がこれにフォーカスする)。
- コードブロックは `@code { ... }` を **ファイル末尾** に置く。
- フィールドは `private` + camelCase(例: `currentCount`, `forecasts`)。
- イベントハンドラ・メソッドは PascalCase(例: `IncrementCount`, `ToggleNavMenu`)。
- 計算プロパティは式メンバーで簡潔に書く(例: `TemperatureC` から `TemperatureF` を導出)。
### 3.3 using の追加場所
新しい名前空間をすべての .razor から使いたい場合は [BlazorApp15/_Imports.razor](BlazorApp15/_Imports.razor) に追加。
1 ファイルだけで使う場合はそのファイル先頭に `@using` を書く。
---
## 4. ルーティングと NotFound
[BlazorApp15/App.razor](BlazorApp15/App.razor) は以下のパターン:
```razor
<Router AppAssembly="@typeof(App).Assembly" NotFoundPage="typeof(Pages.NotFound)">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/>
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
</Router>
```
- 404 ページは [BlazorApp15/Pages/NotFound.razor](BlazorApp15/Pages/NotFound.razor) を編集(新規 NotFound コンポーネントを作らない)。
- 既定レイアウトは [BlazorApp15/Layout/MainLayout.razor](BlazorApp15/Layout/MainLayout.razor)。個別ページでレイアウト変更する場合のみ `@layout` を使う。
---
## 5. ナビゲーションメニューへの追加
新しいページを追加したら [BlazorApp15/Layout/NavMenu.razor](BlazorApp15/Layout/NavMenu.razor) に `<NavLink>` を追記する:
```razor
<div class="nav-item px-3">
<NavLink class="nav-link" href="your-route">
<span class="bi bi-xxx-nav-menu" aria-hidden="true"></span> ラベル
</NavLink>
</div>
```
- ホームのみ `Match="NavLinkMatch.All"` を付ける(他のリンクは前方一致でよい)。
- アイコンは Bootstrap Icons の `bi bi-*-nav-menu` クラスを使う。
---
## 6. HTTP / データ取得
[BlazorApp15/Program.cs](BlazorApp15/Program.cs) で `HttpClient` が `BaseAddress = HostEnvironment.BaseAddress` でスコープド登録されている。
- ページからは `@inject HttpClient Http` で受け取る。
- データ取得は `OnInitializedAsync` 内で `Http.GetFromJsonAsync<T>(...)` を使う([BlazorApp15/Pages/Weather.razor](BlazorApp15/Pages/Weather.razor) 参照)。
- エンドポイントは `wwwroot/` 配下からの相対パス(例: `"sample-data/weather.json"`)。
- 取得結果は nullable フィールド(`T[]? data`)で保持し、`null` のうちはローディング表示を出す。
### DI サービスの追加場所
新しいサービスは [BlazorApp15/Program.cs](BlazorApp15/Program.cs) の `builder.Build()` より前に登録する:
```csharp
builder.Services.AddScoped<IMyService, MyService>();
```
---
## 7. スタイル / CSS
- グローバル CSS: [BlazorApp15/wwwroot/css/app.css](BlazorApp15/wwwroot/css/app.css)
- Bootstrap 5: `wwwroot/lib/bootstrap/` を `index.html` から参照済み
- コンポーネント固有スタイルは **CSS 分離** を使う:コンポーネントと同じディレクトリに `<Component>.razor.css` を置く(例: `MainLayout.razor.css`, `NavMenu.razor.css`)。
- マークアップ側のクラスは Bootstrap のユーティリティ(`btn btn-primary`, `px-4`, `nav-link` など)を優先利用。
---
## 8. モデル/POCO の置き場所
ページでしか使わない小さい DTO は、そのページの `@code` 内に `public class` で書いてよい
([BlazorApp15/Pages/Weather.razor](BlazorApp15/Pages/Weather.razor) の `WeatherForecast` がその例)。
複数ページで共有する型を作るときのみ、別ファイル(例: `Models/`)に切り出す。
---
## 9. 新規ページ追加の標準手順
1. [BlazorApp15/Pages/](BlazorApp15/Pages/) に `<Name>.razor` を新規作成。
2. 先頭に `@page "/<route>"`、必要なら `@inject` を書く。
3. `<PageTitle>` と `<h1>` を含めるマークアップを書く。
4. `@code { ... }` をファイル末尾に置き、`private` フィールド + PascalCase メソッドで状態とハンドラを定義。
5. データ取得が必要なら `OnInitializedAsync` で `HttpClient` を使い、null チェックでローディング UI を出す。
6. [BlazorApp15/Layout/NavMenu.razor](BlazorApp15/Layout/NavMenu.razor) に `<NavLink>` を追加。
7. コンポーネント固有スタイルが必要なら同名の `.razor.css` を併置。
---
## 10. やってはいけないこと
- ルーティング対象を `Pages/` 以外に置く。
- `Program.cs` の `HttpClient` 登録方法を変更して `BaseAddress` を外す。
- 既存の `_Imports.razor` の using を削除する(既存ページが壊れる)。
- `App.razor` の `<Router>` 構造を変えて `FocusOnNavigate` や `NotFoundPage` を外す。
- nullable 警告を抑制するためにファイル単位で `#nullable disable` を入れる(プロジェクト方針は `Nullable=enable`)。

完全にこのプロジェクト固有なスキルになってしまった感がありますが、、良しとしましょう。
実際に抽出されたスキルを使ってみた
せっかくなので、抽出作成されたスキルを使用してみます。
以下のように、簡素なサンプルページを作成するよう指示をしてみます。
このプロジェクトのスタイルで、すごく簡素で良いのでサンプルページを追加してください。

ちゃんとスキルも使用していただいてます。

以下は、作成されたサンプルページです。

スキルを使うほどでもないのは重々承知ですが、既存コードの規約を理解して編集を進めてくれる点は感動です。。
まとめ
ということで、既存コードから Agent Skills を抽出してみました。
既存コードから Agent Skills を抽出することで、プロジェクト固有のコーディング規約や設計ノウハウを、AI が再利用しやすい形に落とし込めることが見えてきました。
汎用的な Skill にはなりにくいものの、既存資産を活かすという観点では、むしろそのプロジェクト専用であることに意味があります。暗黙知の共有や、既存コード読解の負荷軽減という点でも、十分試す価値のあるアプローチだと感じています。
本記事の内容が少しでも何かのお役に立てれば幸いです。
参考
参考記事
- Claude API Docs Agent Skills
- GitHub ドキュメント Agent Skillについて
- Microsoft Learn Agent Framework Agent Skills
Discussion