🤖

Devin開発を効率化するClineとMCP Server活用術

に公開

前提知識

この記事を読むにあたり、以下のツールやコンセプトに関する基本的な知識があると理解が深まるだろう。

  • Devin: AIソフトウェアエンジニア。基本的な使用経験があることが望ましい。
  • Cline: AI開発環境。ターミナル操作やファイル編集が可能なAIアシスタントである。
  • MCP Server: モデルコンテキストプロトコルサーバー。AIアシスタントが外部ツールやリソースと連携するための仕組み。
  • GitHub Actions: CI/CDなどの自動化ワークフロー。

必須ではないが、これらの知識があれば、提案する手法の意図や効果をより深く理解できるはず。

Devin活用の課題と効率化への道筋

Devinは強力なAIエージェントだが、現状では万能ではない。特に以下のような課題に直面することがある。

  • 指示の曖昧さによる非効率: 雑な指示(例: 「TODOアプリ作って」)では、Devinが意図を正確に汲み取れず、不要な機能を作ったり、期待と異なる実装になったりすることがある。逐一指示を渡せば正確な実装にはなるが、それをやるなら最初からClineを使った方がいい。自律的に動かすのであれば詳細な指示が必要。
  • コストの問題: 試行錯誤が増えると、ACU (Agent Compute Units) の消費量がかさみ、開発コストが増大する。個人で使って破産しかけた。
  • コンテキスト維持の難しさ: 複数のタスクを連続して実行させる場合、前のタスクの結果や依存関係を正確に引き継げず、環境構築を繰り返したり、不整合な状態になったりすることがある。Devin本人は同時実行中のタスク同士の依存関係を理解していると言うが、セッション同士は独立してるので普通に妄言だった。
  • 特定タスクでのループ: 特定の種類のタスク、例えば国際化(i18n)の細かい文言調整などで、延々と作業を繰り返してしまうような挙動を見せることもある。現状ではまだ万能ではなく、得意不得意があることを理解しておく必要がある。

これらの課題は、Devinへの指示が不明確であったり、タスクの依存関係が考慮されていなかったりする場合に特に顕著になる。
したがって、Devinの能力を最大限に引き出すためには、タスク実行前の綿密なプランニングと、それを支援する自動化の仕組みが不可欠になる。
本記事では、Cline、MCP Server、GitHub Actionsを連携させ、これらの課題に対処し、Devinによる開発を効率化する具体的な手法を提案する。

では、具体的な計画の立て方を見ていこう。
あくまで一例として、TODOリストアプリの設計ドキュメント(Design Doc)を示す。これによって指示を明確にし、開発を効率化する。

Todoリストの Design Doc

**TODOリストアプリケーション 設計ドキュメント (コア機能版)**

**1. 概要 (Overview)**

このドキュメントは、基本的なTODOリストアプリケーションのコア機能(タスクの追加、表示、完了、削除)に関する要件と設計概要を定義するものです。

**2. 目標 (Goals)**

*   ユーザーがタスクを簡単に追加、表示できるようにする。
*   タスクの完了状態を管理し、不要なタスクを削除できるようにする。
*   アプリケーションのデータが永続化されるようにする。

**3. 機能要件 (Functional Requirements)**

*   **FR1: タスクの追加**
    *   ユーザーは新しいタスクの名前を入力してリストに追加できる。
*   **FR2: タスクの一覧表示**
    *   ユーザーは現在登録されているすべてのタスクを一覧で確認できる。
    *   各タスクには、名前と完了状態が表示される。
*   **FR3: タスクの完了/未完了切り替え**
    *   ユーザーはタスクを「完了」または「未完了」にマークできる。
    *   完了したタスクは視覚的に区別される(例: 取り消し線)。
*   **FR4: タスクの削除**
    *   ユーザーは不要になったタスクをリストから削除できる。

**4. 非機能要件 (Non-Functional Requirements)**

*   **NFR1: ユーザビリティ**
    *   直感的で基本的な操作が容易なインターフェースを提供する。
*   **NFR2: データストレージ**
    *   バックエンドのPostgreSQLデータベースでデータを永続化する。

**5. 技術スタック案 (Proposed Technology Stack)**

*   **フロントエンド**: React
*   **バックエンド**: Hono (Node.js / Bun / Deno / Cloudflare Workers などで動作)
*   **データベース**: PostgreSQL
*   **ORM**: Drizzle ORM
*   **API通信**: RPC (HonoのRPCモードを利用)

Clineによるプランニングとタスク分割:Devinへの的確な指示出し

Devinに複雑なタスクを直接指示した場合、期待通りの成果が得られなかったり、非効率な試行錯誤が発生したりすることがある。これを避けるためには、開発に着手する前に、人間(開発者とCline)が協力して綿密な計画を立てることが重要だ。

  • ClineのPLAN MODEの活用: Clineには、対話を通じて開発計画を策定するためのPLAN MODEが用意されている。
    このモードでは、AIアシスタントと対話形式で、開発対象の機能、実装順序、使用技術、コンポーネントの粒度や命名などを具体的に詰めていくことができる。
    こうして出来上がった成果物(タスク分割されたMarkdownなど)はコミットしておくことで、次回以降のタスク分割の学習データとしても活用可能だ
    最終的に、このプロセスを通じて手戻りを削減し、Devinに対してより的確な指示を与えることが可能になる。これは、コーディング前に詳細な設計を作成するプロセスに似ている。
  • Trunk-Based Development: 開発プロセスとしては、トランクベース開発(変更を頻繁にmainブランチへマージするスタイル)を推奨する。
    Devinがタスクを開始する際に常に最新のコードベース (mainをpull) から作業を開始できるため、コンフリクトや状態の不整合が発生しにくくなる。
  • タスク分割フォーマット: 計画された開発作業は、以下のようなMarkdown形式を用いて具体的なタスクに分割する。タスク間の依存関係を明記することが重要。
## [feature:task] タスク名 (例: [todo:be-db-schema] Define Todo DB Schema)

**Description:**
[タスクの詳細な説明。元の機能要件を参考に、具体的な実装内容を箇条書きなどで記述]
Depends on: #[依存タスクID] (例: `#[todo:be-db-schema]`。依存がない場合はこの行自体を削除。複数ある場合は改行区切り)

このフォーマットで分割したタスク一覧は以下の通り。

実装タスク分割例 (TODOアプリ)
**バックエンド (Hono/Drizzle/PostgreSQL)**

## [todo:be-db-schema] Define Todo DB Schema

**Description:**
*   Drizzle ORMを使用して、PostgreSQLデータベース用の `todos` テーブルスキーマを定義する。
*   スキーマには最低限、`id` (主キー), `name` (テキスト), `completed` (真偽値) カラムを含む。
*   マイグレーションファイルを生成する。

## [todo:be-rpc-list-todos] Implement List Todos RPC Endpoint

**Description:**
*   HonoのRPCモードを使用して、全てのTODOを取得するエンドポイント (`/api/todos/list`) を実装する。
*   Drizzle ORMを使ってデータベースからTODO一覧を取得する。
*   取得したTODOのリストを返す。
Depends on: #[todo:be-db-schema]

## [todo:be-rpc-create-todo] Implement Create Todo RPC Endpoint

**Description:**
*   HonoのRPCモードを使用して、新しいTODOを作成するエンドポイント (`/api/todos/create`) を実装する。
*   リクエストボディからTODO名を受け取る。
*   Drizzle ORMを使ってデータベースに新しいTODO(`completed``false` で)を挿入する。
*   作成されたTODO情報を返す。
Depends on: #[todo:be-db-schema]

## [todo:be-rpc-update-todo] Implement Update Todo RPC Endpoint

**Description:**
*   HonoのRPCモードを使用して、指定されたTODOの情報を更新するエンドポイント (`/api/todos/update/:id`) を実装する。
*   パスパラメータからTODO IDを受け取る。
*   リクエストボディから更新する情報(例: `name`, `completed`)を受け取る。
*   Drizzle ORMを使って該当TODOのカラムを更新する。
*   更新後のTODO情報を返す。
Depends on: #[todo:be-db-schema]

## [todo:be-rpc-delete-todo] Implement Delete Todo RPC Endpoint

**Description:**
*   HonoのRPCモードを使用して、指定されたTODOを削除するエンドポイント (`/api/todos/delete/:id`) を実装する。
*   パスパラメータからTODO IDを受け取る。
*   Drizzle ORMを使って該当TODOをデータベースから削除する。
*   成功したかどうかを示す情報を返す (例: ステータスコード 204 No Content)。
Depends on: #[todo:be-db-schema]

**フロントエンド (React)**

## [todo:fe-api-client] Setup Hono RPC Client for Todos

**Description:**
*   Reactアプリケーション内でHonoのRPCクライアント (`hono/client`) を設定する。
*   バックエンドの `/api/todos/*` RPCエンドポイントと通信するための型定義付きクライアントインスタンスを作成する。
*   このクライアントをアプリケーション全体で利用できるように準備する (例: Context APIやカスタムフック)。
Depends on: #[todo:be-rpc-list-todos]
Depends on: #[todo:be-rpc-create-todo]
Depends on: #[todo:be-rpc-update-todo]
Depends on: #[todo:be-rpc-delete-todo]

## [todo:fe-comp-item] Implement TodoItem Component (Presentational)

**Description:**
*   個々のTODOを表示するための `TodoItem` Reactコンポーネントを作成する (主に表示とイベント通知を担当)。
*   PropsとしてTODOオブジェクト (`id`, `name`, `completed`) と、各種アクションを実行するためのコールバック関数 (`onToggleComplete`, `onDelete`, `onEditStart`) を受け取る。
*   TODO名を表示する。
Depends on: #[todo:be-db-schema]
*   完了状態を切り替えるためのチェックボックスを表示する。チェックボックスが変更されたら、受け取った `onToggleComplete(id, newCompletedState)` を呼び出す。
*   TODOを削除するためのボタンを表示する。ボタンがクリックされたら、受け取った `onDelete(id)` を呼び出す。
*   TODOを編集するためのボタンを表示する。ボタンがクリックされたら、受け取った `onEditStart(id)` を呼び出す。
*   完了したTODOには取り消し線などのスタイルを適用する。
*   **(Note:** このコンポーネントはAPI呼び出しを直接行わない)

## [todo:fe-comp-list] Implement TodoList Component (Container)

**Description:**
*   TODOのリスト全体を表示し、アイテムごとのアクションを管理する `TodoList` Reactコンポーネントを作成する。
*   `[fe-api-client]` を使用して `[be-rpc-list-todos]` を呼び出し、TODOデータを取得し、状態 (`todos`) として保持する。
*   現在編集中のTODO ID (`editingTodoId`) を状態として保持する。
*   `handleToggleComplete(id, completed)`: `[be-rpc-update-todo]` を呼び出してTODOの完了状態を更新する関数を定義する。成功したら `todos` 状態を更新する。
*   `handleDelete(id)`: `[be-rpc-delete-todo]` を呼び出してTODOを削除する関数を定義する。成功したら `todos` 状態を更新する。
*   `handleEditStart(id)`: `editingTodoId` 状態を更新する関数を定義する。
*   取得した `todos` データをループ処理し、各TODOに対して `[todo:fe-comp-item]` コンポーネントを描画する。その際、上記で定義したハンドラ関数 (`handleToggleComplete`, `handleDelete`, `handleEditStart`) をPropsとして渡す。
Depends on: #[todo:fe-api-client]
Depends on: #[todo:fe-comp-item]

## [todo:fe-comp-todo-form] Implement TodoForm Component (Create/Edit)

**Description:**
*   TODOの新規作成と編集を行うための `TodoForm` Reactコンポーネントを作成する。
*   Propsとして、編集対象のTODOオブジェクト (`todoToEdit`, オプショナル) と、フォーム完了時のコールバック関数 (`onSubmit`) を受け取る。
*   TODO名を入力するためのテキスト入力フィールドと、保存ボタンを含む。
Depends on: #[todo:be-db-schema]
*   `todoToEdit` が渡された場合、フォームにそのTODO名を初期表示する (編集モード)。
*   フォームが送信された場合、入力されたTODO名と編集モードかどうかを `onSubmit` コールバックに渡す。
*   **(Note:** このコンポーネントはAPI呼び出しを直接行わない。親コンポーネントが `onSubmit` を処理する)

## [todo:fe-app-integration] Integrate Components and API Logic in App

**Description:**
*   メインのアプリケーションコンポーネント (`App.tsx` など) を作成または編集する。
*   `[fe-api-client]` で設定したRPCクライアントを初期化・提供する。
*   `[fe-comp-list]` コンポーネントを配置する。
*   `[fe-comp-todo-form]` コンポーネントを配置する。
*   `TodoList` から `editingTodoId` を受け取り、該当するTODOデータを `TodoForm``todoToEdit` として渡すロジックを実装する。
*   `TodoForm``onSubmit` コールバックを処理する関数を定義する:
    *   編集モードの場合: `[be-rpc-update-todo]` を呼び出す。
    *   新規作成モードの場合: `[be-rpc-create-todo]` を呼び出す。
*   API呼び出し成功後、`TodoList` のデータを再取得または更新する仕組みを実装する (例: `TodoList` に再取得用の関数を渡すか、状態管理ライブラリを使う)。
*   編集モードを終了するロジックを実装する (例: `editingTodoId``null` にする)。
Depends on: #[todo:fe-api-client]
Depends on: #[todo:fe-comp-list]
Depends on: #[todo:fe-comp-todo-form]

MCP Server × GitHub:自動Issue化

計画したタスクをGitHub Issuesで管理するのは良いが、分割したタスクを一つ一つ手作業でIssueにするのは正直言って面倒だ。
Clineを採用する大きな理由の一つは、まさにこの種の面倒な作業をMCP (Model Context Protocol) Serverと連携して自動化できる点にある。

具体的には、Clineから公式のGitHub MCP Serverが提供する create_issue ツールを呼び出すことで、分割されたタスクを一括でGitHub Issuesに変換できる。

この自動化を実現するために、create_issue ツールを使用する際には以下のルールを適用する。

  • Title: 各タスクの見出し (## [feature:task] タスク名) をIssueのタイトルとして使用する。
  • Body: 各タスクの **Description:** および Depends on: の内容をIssueの本文にコピーする。
    • 重要: Depends on: に記載されたタスクID (#[feature:task-id]) は、対応する実際のIssue番号 (#<issue-number>) に必ず置き換えること (例: Depends on: #[todo:be-db-schema]Depends on: #123 のようにする)。依存関係が複数ある場合は、それぞれ改行して記載する。
  • Labels: 関連するラベル (feature, backend など) を付与する。
  • devin ラベル: Depends on: が存在しないタスクにのみ付与する。それ以外の場合は自動処理に任せるため付与しない。

これらのルールに従うことで、タスク管理と自動化が適切に行われる。

GitHub ActionsによるDevin起動

以下の図は、前述のタスク分割例におけるタスク間の依存関係を示している。この依存関係に基づき、GitHub ActionsとDevinは以下のように連携してタスクを自律的に実行していくことを想定している。

  • まず、GitHub Actionsは依存関係のないタスク(図の [be-db-schema] など)に devin ラベルを付与し、Devinを起動する。
  • 依存関係のないタスクが複数同時に存在する場合、それらは並列でDevinによって実行される可能性がある。
  • あるタスクが完了すると、そのタスクに依存していた次のタスクの依存関係が解消される。
  • GitHub Actionsは依存関係が解消されたタスクに自動で devin ラベルを付与し、Devinが次のタスクを実行する。
  • このプロセスを繰り返し、タスクが依存関係の順序に従って(あるいは依存関係が許す限り並列で)実行されていく。

この依存関係に基づいたDevinの理想的な実行順序(期待値)は以下のようになる。

  1. [be-db-schema] (A): 最初に、依存関係のないデータベーススキーマ定義タスクが実行される。
  2. 並列実行フェーズ1: スキーマ定義(A)完了後、以下のタスク群が並列で実行可能になる。
    • [be-rpc-*] (B, C, D, E): バックエンドRPCエンドポイント実装タスク群。
    • [fe-comp-item] (G): 表示用の TodoItem コンポーネント実装タスク。
    • [fe-comp-todo-form] (I): TodoForm コンポーネント実装タスク。
  3. [fe-api-client] (F): 全てのバックエンドRPCタスク(B, C, D, E)が完了すると、APIクライアント設定タスクが実行可能になる。
  4. [fe-comp-list] (H): APIクライアント(F)と TodoItem コンポーネント(G)の両方が完了すると、TodoList コンポーネント実装タスクが実行可能になる。
  5. [fe-app-integration] (J): 最後に、APIクライアント(F)、TodoList コンポーネント(H)、TodoForm コンポーネント(I)の全てが完了すると、これらを統合するメインアプリケーション実装タスクが実行される。

このように依存関係を定義することで、Devinは可能な限りタスクを並列実行しつつ、正しい順序で開発を進めることが期待できる。

前述のフローに基づき、Issueに devin ラベルが付与された際にDevinの処理を開始させるためのGitHub Actionを以下に示す。このActionは、トリガーとなったIssueの情報をDevin APIに渡し、処理を依頼する。

name: Devin Task Automation
on:
  issues:
    types: [labeled]
jobs:
  process-issue:
    runs-on: ubuntu-latest
    if: github.event.label.name == 'devin'
    steps:
      - name: Call Devin API
        run: |
          curl --request POST \
            --url https://api.devin.ai/v1/sessions \
            --header 'Authorization: Bearer ${{ secrets.DEVIN_API_KEY }}' \
            --header 'Content-Type: application/json' \
            --data '{
              "prompt": "Process GitHub Issue ${{ github.repository }}#${{ github.event.issue.number }}",
              "idempotent": true, // 同一Issueに対する複数回のトリガーでDevinセッションが重複作成されるのを防ぐ
              "title": "${{ github.event.issue.title }}"
            }'
  • PR作成(期待値): うまくいけば、Devinは対応するPull Requestを作成する。ただし、現状では詰まることも多いので、開発者の介入が必要になる場合もある。
  • レビューとマージ: Devinが作成したPRは人間がレビューする。
    事前にタスクが適切に分割されていれば、PRの範囲も小さくなり、レビューが容易になるという利点がある。
    問題なければマージし、対応するIssueをクローズする。

依存関係のあるIssueが閉じたとき

Devinがタスクを完了し、対応するIssueがクローズされると、次のタスクを自動的に開始させる必要がある。
以下のGitHub Actionは、devin ラベルが付いたIssueがクローズされたことをトリガーとして実行される。
このActionは、クローズされたIssueに依存していた他のIssueを探し、依存関係が解消された場合は devin ラベルを付与して次のDevinタスクを起動する(あるいは、まだ他の依存関係が残っている場合はIssue本文を更新する)。

name: Label Dependent Devin Issues

on:
  issues:
    types: [closed]

jobs:
  label_dependents:
    runs-on: ubuntu-latest
    # このジョブは 'devin' ラベルを持つ Issue がクローズされた場合にのみ実行される
    if: contains(github.event.issue.labels.*.name, 'devin')
    steps:
      # GitHub API をスクリプトから操作するための Action
      - name: Add devin label to dependent issues
        uses: actions/github-script@v7
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            // クローズされた Issue の情報
            const closedIssueNumber = context.issue.number;
            const owner = context.repo.owner;
            const repo = context.repo.repo;
            const labelToAdd = 'devin'; // 依存関係が解消されたIssueに付与するラベル

            console.log(`Issue #${closedIssueNumber} closed with 'devin' label. Checking for dependent issues...`);

            // 1. クローズされたIssueへの依存行だけを正確にマッチさせる正規表現 (例: "Depends on: #123")
            //    - `^`: 行頭, `im`: 大文字小文字無視&複数行モード
            const specificDependencyLineRegex = new RegExp(`^Depends on: #${closedIssueNumber}$`, 'im');
            // 2. Issue本文中の "Depends on: #<数字>" という形式の行を全て見つける正規表現
            //    - `gm`: グローバル検索&複数行モード
            const anyDependencyLineRegex = /^Depends on: #\d+$/gm;

            // リポジトリ内のOpenなIssueを全て取得
            const { data: openIssues } = await github.rest.issues.listForRepo({
              owner,
              repo,
              state: 'open'
            });

            console.log(`Found ${openIssues.length} open issues. Checking bodies for dependency on #${closedIssueNumber}...`);

            // 取得したOpenなIssueを一つずつチェック
            for (const issue of openIssues) {
              // Issue本文が存在し、かつクローズされたIssueへの依存行 (specificDependencyLineRegex) が含まれるか?
              if (issue.body && specificDependencyLineRegex.test(issue.body)) {
                console.log(`Found potential dependency in Issue #${issue.number}. Analyzing further...`);

                // このIssueが依存している全ての "Depends on: #<数字>" 行を検索し、その数を数える
                const allDependencyLines = issue.body.match(anyDependencyLineRegex);
                const dependencyCount = allDependencyLines ? allDependencyLines.length : 0;

                console.log(`Issue #${issue.number} has ${dependencyCount} 'Depends on:' lines.`);

                // 依存行が複数ある場合 (今回クローズされたもの以外にも依存が残っている)
                if (dependencyCount > 1) {
                  console.log(`Issue #${issue.number} has other dependencies. Removing 'Depends on: #${closedIssueNumber}' line.`);
                  // クローズされたIssueへの依存行だけを削除し、余分な空行も削除して本文を更新
                  const updatedBody = issue.body.replace(specificDependencyLineRegex, '').replace(/^\s*[\r\n]/gm, '');

                  try {
                    // Issue本文を更新
                    await github.rest.issues.update({
                      owner,
                      repo,
                      issue_number: issue.number,
                      body: updatedBody,
                    });
                    console.log(`Successfully updated body for Issue #${issue.number}.`);
                  } catch (error) {
                    console.error(`Failed to update body for Issue #${issue.number}:`, error);
                  }
                // 依存行が1つだけだった場合 (今回クローズされたのが最後の依存関係)
                } else if (dependencyCount === 1) {
                  console.log(`Issue #${issue.number}'s last dependency #${closedIssueNumber} was closed.`);

                  // 既に 'devin' ラベルが付いていないかチェック (二重実行防止)
                  const hasLabel = issue.labels.some(label => label.name === labelToAdd);
                  if (hasLabel) {
                    console.log(`Issue #${issue.number} already has the '${labelToAdd}' label. Skipping label addition and dispatch.`);
                    continue; // 次のIssueへ
                  }

                  console.log(`Adding '${labelToAdd}' label and dispatching event for Issue #${issue.number}...`);
                  try {
                    // 1. 'devin' ラベルを追加
                    await github.rest.issues.addLabels({
                      owner,
                      repo,
                      issue_number: issue.number,
                      labels: [labelToAdd]
                    });
                    console.log(`Successfully added '${labelToAdd}' label to Issue #${issue.number}.`);

                    // 2. Devin起動ワークフローをトリガーするためのカスタムイベント(repository_dispatch)を発行
                    //    ※ Actionからのラベル付与では `on.issues.types: [labeled]` がトリガーされないための措置
                    console.log(`Dispatching repository_dispatch event for Issue #${issue.number}...`);
                    await github.rest.repos.createDispatchEvent({
                      owner,
                      repo,
                      event_type: 'trigger-devin-automation', // 任意のイベントタイプ名
                      client_payload: { // 後続 Action に渡す情報
                        issue_number: issue.number,
                        issue_title: issue.title
                      }
                    });
                    console.log(`Successfully dispatched event for Issue #${issue.number}.`);

                  } catch (error) {
                    console.error(`Failed operation for Issue #${issue.number}:`, error);
                  }
                } else {
                  // specificDependencyLineRegex にマッチしたが、依存行カウントが0の場合 (通常は発生しない)
                  console.log(`Issue #${issue.number} matched specific dependency but count is 0. Skipping.`);
                }
              }
            }

            console.log('Finished checking dependent issues.');

Devin起動トリガーの修正

上記の Label Dependent Devin Issues ワークフローでは、依存関係が解消されたIssueに devin ラベルを付与した後、後続のDevin起動ワークフローをトリガーするために repository_dispatch イベントを発行している。
これは、GitHub Actionsの仕様上、actions/github-script などでAPI経由でラベルを付与しても、on.issues.types: [labeled] トリガーが起動しないためである。

この問題を回避するため、Devinを起動するワークフロー側では、手動でのラベル付与 (on.issues.types: [labeled]) に加えて、repository_dispatch イベント (on.repository_dispatch.types: [trigger-devin-automation]) もトリガーとして受け付けるように修正する必要がある。
これにより、Actionによる自動的なラベル付与後も、後続のDevinタスクが確実に開始される。

以下に、修正後のDevin起動ワークフロー (Devin Task Automation (Trigger)) の例を示す。

name: Devin Task Automation (Trigger)

on:
  issues:
    types: [labeled] # 手動で 'devin' ラベルが付与された場合
  repository_dispatch:
    types: [trigger-devin-automation] # 依存関係解消ワークフローからのイベント発行

jobs:
  process-issue:
    runs-on: ubuntu-latest
    # 手動でのラベル付与、または repository_dispatch イベントによってトリガーされた場合に実行
    if: (github.event_name == 'issues' && github.event.label.name == 'devin') || github.event_name == 'repository_dispatch'
    steps:
      - name: Call Devin API
        env:
          DEVIN_API_KEY: ${{ secrets.DEVIN_API_KEY }}
          # トリガーとなったイベントに応じて Issue 番号とタイトルを環境変数に設定
          ISSUE_NUMBER: ${{ github.event_name == 'issues' && github.event.issue.number || github.event.client_payload.issue_number }}
          ISSUE_TITLE: ${{ github.event_name == 'issues' && github.event.issue.title || github.event.client_payload.issue_title }}
          REPO_FULL_NAME: ${{ github.repository }}
        run: |
          # 実行トリガーと対象 Issue をログに出力
          echo "Triggered by: ${{ github.event_name }}"
          echo "Processing Issue: ${{ env.REPO_FULL_NAME }}#${{ env.ISSUE_NUMBER }} - ${{ env.ISSUE_TITLE }}"

          # Devin API を呼び出して Issue 処理を依頼
          curl --request POST \
            --url https://api.devin.ai/v1/sessions \
            --header 'Authorization: Bearer ${{ env.DEVIN_API_KEY }}' \
            --header 'Content-Type: application/json' \
            --data '{
              "prompt": "Process GitHub Issue ${{ env.REPO_FULL_NAME }}#${{ env.ISSUE_NUMBER }}",
              "idempotent": true, // 同一 Issue に対する重複セッション作成を防止
              "title": "${{ env.ISSUE_TITLE }}"
            }'

まとめ

Devinは強力なAIソフトウェアエンジニアだが、現状では指示の曖昧さによる非効率、コスト増大、コンテキスト維持の難しさといった課題も抱えている。
本記事で提案した、Clineによる綿密な計画立案、MCP Serverを用いたGitHub Issue連携、そしてGitHub Actionsによるタスク実行の自動化は、これらのDevin特有の課題に対処し、その能力を最大限に引き出すための実践的なアプローチである。
この連携を通じて、手戻りを減らし、開発プロセス全体を効率化することで、より信頼性の高い自律的なAI開発の実現が期待できる。
また、GitHubのSub-issue機能(タスクリストのIssue化)が正式に利用可能になったため、将来的にはこれを利用して依存関係をよりネイティブに表現・管理できる可能性に期待している。
利用できるものは全て利用して、自律的なAI開発プロセスをより洗練させていきたい。

おわりに

この記事で紹介したようなAI開発の計画、自動化、効率化にご興味があれば、ぜひお声がけください。
スポットでのコンサルティングから継続的な技術顧問まで、ご要望に合わせて柔軟に対応し、Devinのようなツールを最大限活用して開発プロセスを改善するお手伝いができます。
ご連絡はこちらまで

Discussion