# 機能設計書 50-スキル検出・実行

## 概要

本ドキュメントは、OpenCodeにおけるスキル検出・実行機能の設計を記述する。.opcode/skills/や.claude/skills/ディレクトリからカスタムスキル（コマンド）を検出し、ユーザーからの呼び出しに応じて実行する機能を提供する。

### 本機能の処理概要

スキル検出・実行機能は、複数のスキルソース（.opencodeディレクトリ、.claude/.agentsディレクトリ、設定ファイルで指定されたパス、リモートURL）からSKILL.mdファイルを検索し、スキル情報を読み込む。スキルはMarkdown形式で定義され、フロントマターでメタデータ（name, description）を、本文でプロンプトテンプレートを記述する。

**業務上の目的・背景**：OpenCodeは汎用的なAIコーディングエージェントだが、プロジェクト固有のワークフローや組織のベストプラクティスを組み込むニーズがある。スキル機能により、カスタムコマンドをMarkdownファイルとして定義でき、チーム間での共有やバージョン管理が容易になる。また、Claude CodeやAI agentsとの互換性を持つことで、エコシステム全体でのスキル再利用が可能になる。

**機能の利用シーン**：
- プロジェクト固有のコードレビュースキル
- ドキュメント生成スキル
- テスト作成支援スキル
- リファクタリング支援スキル
- チーム共通のワークフロー自動化

**主要な処理内容**：
1. スキルディレクトリの検出（複数ソース）
2. SKILL.mdファイルの検索（Glob）
3. フロントマターとMarkdown本文のパース
4. スキル情報のキャッシュ
5. スキル実行時のプロンプト展開

**関連システム・外部連携**：
- ConfigMarkdown: フロントマターパーサー
- Glob: ファイル検索
- Discovery: リモートスキルのダウンロード

**権限による制御**：OPENCODE_DISABLE_EXTERNAL_SKILLS で.claude/.agentsスキルを無効化可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 25 | ホーム | 補助機能 | コマンドダイアログによるスキル登録・実行 |

## 機能種別

検出処理 / コマンド実行

## 入力仕様

### スキル定義形式（SKILL.md）

```markdown
---
name: skill-name
description: スキルの説明文
---

スキルのプロンプトテンプレート本文
```

### 入力パラメータ（Info型）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| name | string | Yes | スキル名 | 一意 |
| description | string | Yes | スキル説明 | - |
| location | string | Yes | SKILL.mdファイルパス | 有効なパス |
| content | string | Yes | プロンプトテンプレート | - |

### スキル検索ディレクトリ（優先順位：低→高）

1. グローバル外部ディレクトリ（~/.claude/skills/, ~/.agents/skills/）
2. プロジェクト外部ディレクトリ（.claude/skills/, .agents/skills/）
3. .opencode/skills/ディレクトリ
4. 設定ファイルで指定されたパス（config.skills.paths）
5. リモートURL（config.skills.urls）

### 入力データソース

- ファイルシステム（SKILL.mdファイル）
- 設定ファイル（skills設定）
- リモートURL

## 出力仕様

### 出力データ（Skill.Info型）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| name | string | スキル名 |
| description | string | スキル説明 |
| location | string | ファイルパス |
| content | string | プロンプトテンプレート |

### 出力先

- メモリ（Instance.state経由でキャッシュ）
- API（GET /skill）

## 処理フロー

### 処理シーケンス

```
1. スキル検出開始（Skill.state）
   └─ スキルマップ初期化

2. 外部スキル検索（.claude/.agents）
   ├─ OPENCODE_DISABLE_EXTERNAL_SKILLS チェック
   ├─ グローバル外部ディレクトリ検索
   └─ プロジェクト外部ディレクトリ検索

3. OpenCodeスキル検索
   └─ Config.directories() からskills/**/SKILL.md検索

4. 追加パス検索
   └─ config.skills.paths からSKILL.md検索

5. リモートスキル取得
   └─ config.skills.urls からダウンロードして検索

6. スキル読み込み（各ファイル）
   ├─ ConfigMarkdown.parse でフロントマター解析
   ├─ Info スキーマ検証
   └─ 重複チェックと登録
```

### フローチャート

```mermaid
flowchart TD
    A[Skill.state開始] --> B{外部スキル有効?}
    B -->|No| C[OpenCodeスキル検索]
    B -->|Yes| D[グローバル外部スキル検索]
    D --> E[プロジェクト外部スキル検索]
    E --> C
    C --> F[Config.directories取得]
    F --> G[各ディレクトリでGlob検索]
    G --> H{追加パスあり?}
    H -->|Yes| I[追加パス検索]
    H -->|No| J{リモートURLあり?}
    I --> J
    J -->|Yes| K[Discovery.pull]
    J -->|No| L[スキルマップ返却]
    K --> L
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-50-01 | 優先順位 | 後から読み込まれたスキルが同名を上書き | 重複時 |
| BR-50-02 | 外部スキル無効化 | OPENCODE_DISABLE_EXTERNAL_SKILLS で無効化 | 外部検索時 |
| BR-50-03 | 重複警告 | 同名スキルが存在する場合警告ログ出力 | 登録時 |
| BR-50-04 | パース失敗時スキップ | フロントマターパース失敗時はスキップ | 読み込み時 |
| BR-50-05 | 相対パス展開 | ~/で始まるパスはホームディレクトリに展開 | パス処理時 |

### 計算ロジック

外部スキルディレクトリ：
```typescript
const EXTERNAL_DIRS = [".claude", ".agents"]
const EXTERNAL_SKILL_PATTERN = "skills/**/SKILL.md"
const OPENCODE_SKILL_PATTERN = "{skill,skills}/**/SKILL.md"
```

## データベース操作仕様

本機能はデータベースを操作しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | フロントマターエラー | YAML構文エラー | 警告ログ出力、スキップ |
| - | スキーマエラー | name/description 不足 | スキップ |
| - | ディレクトリ不在 | 追加パスが存在しない | 警告ログ出力 |

### リトライ仕様

リトライは実装されていない。エラー発生時はスキップして継続。

## トランザクション仕様

トランザクション管理なし。スキル情報はInstance.stateでキャッシュ。

## パフォーマンス要件

- スキルはインスタンス初期化時に1回だけ検出
- Instance.stateでキャッシュしてアクセス高速化
- Glob.scanで効率的なファイル検索

## セキュリティ考慮事項

- スキルはプロンプトテンプレートとして実行
- 信頼できるソースからのスキルのみ使用推奨
- リモートURLは設定で明示的に指定が必要

## 備考

スキル検索パターン：
- .claude/skills/**/SKILL.md
- .agents/skills/**/SKILL.md
- .opencode/skills/**/SKILL.md
- .opencode/skill/**/SKILL.md

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

スキル情報の構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | skill.ts | `packages/opencode/src/skill/skill.ts` | Info型（19-25行目）を確認 |
| 1-2 | skill.ts | `packages/opencode/src/skill/skill.ts` | エラー型（27-43行目）を確認 |

**読解のコツ**: Info型はname, description, location, contentの4フィールドで構成。

#### Step 2: エントリーポイントを理解する

スキル検出のメインフローを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | skill.ts | `packages/opencode/src/skill/skill.ts` | state関数（52-176行目）がメイン処理 |

**主要処理フロー**:
1. **53-54行目**: スキルマップと検出ディレクトリセットの初期化
2. **56-88行目**: addSkill関数（SKILL.mdのパースと登録）
3. **90-102行目**: scanExternal関数（外部ディレクトリ検索）
4. **104-120行目**: 外部スキル検索（グローバル→プロジェクト）
5. **122-133行目**: OpenCodeスキル検索
6. **135-153行目**: 追加パス検索
7. **155-170行目**: リモートスキル取得

#### Step 3: スキル読み込みを理解する

個別スキルの読み込みとパースを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | skill.ts | `packages/opencode/src/skill/skill.ts` | addSkill関数内（56-88行目） |

**主要処理フロー**:
- **57-65行目**: ConfigMarkdown.parse でフロントマター解析
- **68行目**: Info.pick({name, description}).safeParse で検証
- **71-78行目**: 重複チェックと警告ログ
- **82-87行目**: スキル情報の登録

#### Step 4: APIを理解する

スキル取得のAPIを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | skill.ts | `packages/opencode/src/skill/skill.ts` | get関数（178-180行目）、all関数（182-184行目） |
| 4-2 | server.ts | `packages/opencode/src/server/server.ts` | /skill エンドポイント（421-442行目） |

**主要処理フロー**:
- **178-180行目**: 名前でスキル取得
- **182-184行目**: 全スキル取得
- **186-188行目**: スキルディレクトリ一覧取得

### プログラム呼び出し階層図

```
Skill.all() / Skill.get(name)
    │
    └─ Skill.state()
           │
           ├─ OPENCODE_DISABLE_EXTERNAL_SKILLS チェック
           │
           ├─ scanExternal(root, scope)
           │      │
           │      └─ Glob.scan(EXTERNAL_SKILL_PATTERN)
           │             │
           │             └─ addSkill(match)
           │
           ├─ Glob.scan(OPENCODE_SKILL_PATTERN)
           │      │
           │      └─ addSkill(match)
           │
           ├─ config.skills.paths 検索
           │      │
           │      └─ Glob.scan(SKILL_PATTERN)
           │
           └─ Discovery.pull(url)
                  │
                  └─ Glob.scan(SKILL_PATTERN)
```

### データフロー図

```
[入力]               [処理]                    [出力]

~/.claude/skills/
~/.agents/skills/
.claude/skills/
.agents/skills/
.opencode/skills/
config.skills.paths
config.skills.urls
       │
       ▼
Glob.scan() ───▶ SKILL.mdパス一覧
       │
       ▼
ConfigMarkdown.parse() ───▶ フロントマター + 本文
       │
       ▼
Info.safeParse() ───▶ スキル情報
       │
       ▼
skills[name] = {...} ───▶ キャッシュ
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| skill.ts | `packages/opencode/src/skill/skill.ts` | ソース | スキル検出・管理のメインモジュール |
| discovery.ts | `packages/opencode/src/skill/discovery.ts` | ソース | リモートスキルのダウンロード |
| markdown.ts | `packages/opencode/src/config/markdown.ts` | ソース | フロントマターパーサー |
| glob.ts | `packages/opencode/src/util/glob.ts` | ソース | ファイル検索ユーティリティ |
| config.ts | `packages/opencode/src/config/config.ts` | ソース | skills設定の読み込み |
