Linear イシューを自動調査する Python デーモン。 「Investigating」状態のイシューをポーリングし、LLM の tool_use でGitHubリポジトリ上に管理されているソースコードや DB を調査して、結果を Linear コメントに投稿する。 Claude (Anthropic)、GPT-5.2 (OpenAI)、GLM-5.0 (Zhipu AI) 等、複数の LLM プロバイダに対応。
| ツール | バージョン | 用途 |
|---|---|---|
| Python | 3.12+ | ランタイム |
| Git | 2.x | ワークスペースの git clone |
MySQL client (mysql CLI) |
8.x | mysql_query ツールが使用 |
環境変数で設定する。.env ファイルは使わず、シェルの export かデプロイ基盤の secrets で注入する。
# 必須
export LINEAR_API_KEY="lin_api_..." # Linear GraphQL API
export GITHUB_TOKEN="ghp_..." # private repo の git clone 用
# LLM プロバイダ(デフォルト: Anthropic Claude)
export AGENT_PROVIDER="anthropic" # anthropic | openai | openai_compat
export AGENT_MODEL="claude-sonnet-4-20250514"
export ANTHROPIC_API_KEY="sk-ant-..." # Anthropic の場合
# OpenAI / OpenAI 互換 API の場合
# export AGENT_PROVIDER="openai"
# export AGENT_MODEL="gpt-5.2"
# export OPENAI_API_KEY="sk-..."
# export OPENAI_BASE_URL="https://..." # openai_compat の場合のみ
# MySQL 接続(DB 調査を使う場合)
export MYSQL_HOST="127.0.0.1"
export MYSQL_USER="readonly_user"
export MYSQL_PASSWORD="..."
export MYSQL_DATABASE="production_db"
# SSH トンネル経由で MySQL に接続する場合
export SSH_TUNNEL_ENABLED="true"
export SSH_TUNNEL_HOST="bastion.example.com"
export SSH_TUNNEL_USER="deploy"ANTHROPIC_API_KEY / OPENAI_API_KEY は各 SDK が自動的に参照するため、WORKFLOW.md には書かない。
LINEAR_API_KEY は WORKFLOW.md の ${LINEAR_API_KEY} で参照される。
GITHUB_TOKEN が設定されている場合、WORKFLOW.md の repos[].url が SSH 形式 (git@github.com:...) でも自動的に HTTPS + トークン認証に変換される。未設定の場合は URL がそのまま使われる。
- Linear にログイン
- 左下の Settings (歯車アイコン) → Account → Security & Access
- Personal API keys セクションで Create key をクリック
- ラベル(例:
investigation-service)を入力 - 権限を設定:
- Read — イシューの取得に必要
- Write — コメント投稿とステート変更に必要
- 必要に応じて特定チームに限定可能
- 作成後に表示されるキー (
lin_api_...) をコピーして.envに設定
キーは作成時に一度だけ表示される。紛失した場合は再作成が必要。
- GitHub にログイン
- 右上のアイコン → Settings → 左メニュー最下部の Developer settings
- Personal access tokens → Fine-grained tokens → Generate new token
- 設定:
- Token name:
investigation-service - Expiration: 任意(90 days 等)
- Repository access: Only select repositories → 調査対象のリポジトリを選択
- Permissions → Contents: Read-only(clone に必要な最小権限)
- Token name:
- Generate token をクリックし、表示されるトークン (
github_pat_...) をコピーして.envに設定
Classic token (ghp_...) でも動作するが、Fine-grained token の方がリポジトリ単位で権限を絞れるため推奨。
.env.example をコピーして値を埋める。Docker の場合は .env を、ローカル実行の場合は export で設定する。
cp .env.example .env| 環境変数 | 必須 | 用途 |
|---|---|---|
AGENT_PROVIDER |
No | LLM プロバイダ(anthropic / openai / openai_compat、デフォルト: anthropic) |
AGENT_MODEL |
No | モデル ID(デフォルト: claude-sonnet-4-20250514) |
AGENT_MAX_TURNS |
No | tool_use 最大ターン数(デフォルト: 30) |
AGENT_MAX_RETRY_ATTEMPTS |
No | スタール時のリトライ回数(デフォルト: 2) |
ANTHROPIC_API_KEY |
provider=anthropic | Claude API(anthropic SDK が自動参照) |
OPENAI_API_KEY |
provider=openai/openai_compat | OpenAI / OpenAI 互換 API キー |
OPENAI_BASE_URL |
provider=openai_compat | OpenAI 互換 API のベース URL |
LINEAR_API_KEY |
Yes | Linear GraphQL API |
GITHUB_TOKEN |
Yes | private repo の git clone(HTTPS + トークン認証) |
MYSQL_HOST |
DB 調査時 | MySQL ホスト(SSH トンネル時は自動設定) |
MYSQL_USER |
DB 調査時 | MySQL ユーザー(READ ONLY 推奨) |
MYSQL_PASSWORD |
DB 調査時 | MySQL パスワード |
MYSQL_DATABASE |
DB 調査時 | MySQL データベース名 |
SSH_TUNNEL_HOST |
トンネル時 | 踏み台サーバーのホスト |
SSH_TUNNEL_USER |
トンネル時 | SSH ユーザー |
SSH_TUNNEL_KEY_PATH_HOST |
Docker 時 | ホスト側の SSH 鍵パス(コンテナにマウント) |
GITHUB_TOKEN が設定されていると、WORKFLOW.md の repos[].url が SSH 形式 (git@github.com:...) でも自動的に HTTPS + トークン認証に変換される。
WORKFLOW.md は YAML front matter(設定)+ Markdown body(プロンプトテンプレート)で構成される。
環境変数は ${ENV_VAR} の形式で参照できる。
tracker:
api_key: ${LINEAR_API_KEY}
project_slugs: ["your-team/investigation"] # 対象プロジェクト
active_states: ["Investigating"] # ポーリング対象のステート
terminal_states: ["Done", "Canceled"]
polling_interval_ms: 30000 # ポーリング間隔(30秒)agent:
provider: ${AGENT_PROVIDER} # anthropic | openai | openai_compat
max_concurrent_agents: 3 # 同時実行エージェント数
model: ${AGENT_MODEL} # モデル ID
max_tokens: 4096
max_turns: 30 # ツール呼び出しの最大ターン数
stall_timeout_ms: 600000 # 10分間無応答でスタル判定
max_retry_attempts: 2
max_retry_backoff_ms: 60000対応プロバイダ:
| provider | 用途 | 必要な環境変数 |
|---|---|---|
anthropic (デフォルト) |
Claude Haiku / Sonnet / Opus | ANTHROPIC_API_KEY |
openai |
OpenAI GPT-5.2 等 | OPENAI_API_KEY |
openai_compat |
OpenAI 互換 API (GLM-5.0 等) | OPENAI_API_KEY, OPENAI_BASE_URL |
複数のリポジトリをワークスペースに clone できる。イシューが複数リポジトリにまたがる場合でも、エージェントが横断検索できる。
方法 A: WORKFLOW.md に直接書く
リポジトリ URL は秘密情報ではないので、直接記述してよい(認証は GITHUB_TOKEN で分離されている)。
workspace:
root: "/var/investigation/workspaces"
default_branch: "main"
repos:
- url: "git@github.com:your-org/backend.git"
path: "backend" # ワークスペース内のディレクトリ名
- url: "git@github.com:your-org/frontend.git"
path: "frontend"
- url: "git@github.com:your-org/infra.git"
path: "infra"
branch: "develop" # リポジトリごとにブランチ指定可(省略時は default_branch)方法 B: .env から参照する
リポジトリ URL も環境変数で管理したい場合は ${VAR} で参照できる。
# WORKFLOW.md
workspace:
root: "/var/investigation/workspaces"
default_branch: "main"
repos:
- url: ${REPO_URL_BACKEND}
path: "backend"
branch: ${REPO_BRANCH_BACKEND}
- url: ${REPO_URL_FRONTEND}
path: "frontend"
branch: ${REPO_BRANCH_FRONTEND}# .env
REPO_URL_BACKEND=git@github.com:your-org/backend.git
REPO_BRANCH_BACKEND=main
REPO_URL_FRONTEND=git@github.com:your-org/frontend.git
REPO_BRANCH_FRONTEND=developイシューごとに作られるワークスペースの構造:
/var/investigation/workspaces/INV-123/
├── backend/ ← backend リポジトリ
├── frontend/ ← frontend リポジトリ
└── infra/ ← infra リポジトリ
ホワイトリスト方式。ここに定義されたコマンドのみエージェントが実行できる。
tools:
bash:
allowed_commands:
- "grep"
- "find"
- "cat"
- "head"
- "tail"
- "wc"
- "ls"
- "git log"
- "git show"
- "git diff"
- "git blame"tools:
mysql:
host: ${MYSQL_HOST}
port: 3306
user: ${MYSQL_USER}
password: ${MYSQL_PASSWORD}
database: ${MYSQL_DATABASE}本番 DB に直接接続できない環境(踏み台サーバー経由)では、SSH トンネルを有効にする。
enabled: true にすると、デーモン起動時に自動で SSH トンネルを確立する。
MySQL の host / port はトンネル設定から自動で 127.0.0.1:{local_port} に上書きされるため、手動で合わせる必要はない。
ssh_tunnel:
enabled: true
ssh_host: ${SSH_TUNNEL_HOST} # 踏み台サーバー
ssh_port: 22
ssh_user: ${SSH_TUNNEL_USER} # SSH ユーザー
ssh_key_path: "/root/.ssh/tunnel_key" # コンテナ内固定パス
remote_host: "127.0.0.1" # 踏み台から見た DB のアドレス
remote_port: 3306 # 踏み台から見た DB のポート
local_port: 13306 # ローカル側のポートコンテナ (13306) ──SSH──▶ 踏み台サーバー ──▶ 127.0.0.1:3306 (RDS)
トンネルの動作:
- デーモン起動時に
ssh -N -Lで自動確立 - tick ごとに生存確認し、切断時は自動再接続
- デーモン停止時に自動切断
設定ファイルはホットリロード対応。デーモン実行中に WORKFLOW.md を変更すると、次の tick で新設定が反映される。
チームのワークフローに以下を追加する。
| ステート | タイプ | 用途 |
|---|---|---|
| Investigating | Started | デーモンがポーリングする対象 |
| Human Review | Started | 調査完了、人間のレビュー待ち |
| Rework | Started | 追加情報が必要 |
イシューを Investigating に移動すると、デーモンが自動的に検知して調査を開始する。
# ビルド & 起動
docker compose up -d
# ログを確認
docker compose logs -f
# 停止
docker compose downSSH トンネル用の鍵は .env の SSH_TUNNEL_KEY_PATH_HOST に指定したホスト側パスからコンテナ内 /root/.ssh/tunnel_key にマウントされる。コンテナ内パスは WORKFLOW.md にハードコードされているため設定不要。
WORKFLOW.md の ssh_key_path にはコンテナ内パス (/root/.ssh/tunnel_key) を指定する。
source .venv/bin/activate
pip install -e ".[dev]"
python -m src.mainログは構造化 JSON で stdout に出力される。Ctrl+C または SIGTERM でグレースフルシャットダウンする。
# テスト
pytest tests/ -v
# 型チェック
mypy src/
# リント
ruff check src/├── SPEC.md # 技術仕様
├── WORKFLOW.md # ランタイム設定 + プロンプトテンプレート
├── HANDOFF.md # 設計決定の経緯
├── pyproject.toml
├── src/
│ ├── main.py # asyncio daemon entrypoint
│ ├── orchestrator.py # tick() ループ
│ ├── classifier.py # イシュー分類(Phase 1 はスタブ)
│ ├── config.py # WORKFLOW.md パーサー + ホットリロード
│ ├── workspace.py # git clone + cleanup
│ ├── tracker/
│ │ ├── linear.py # Linear GraphQL クライアント
│ │ └── models.py # Issue, AgentSession, InvestigationResult
│ ├── llm/
│ │ ├── protocol.py # LLMProvider Protocol + 共通データ型
│ │ ├── anthropic.py # Anthropic Claude アダプタ
│ │ ├── openai_compat.py # OpenAI / GLM-5.0 アダプタ
│ │ └── factory.py # プロバイダファクトリ
│ ├── agents/
│ │ ├── base.py # LLM tool_use ループ(共通基盤)
│ │ └── source_code.py # Phase 1 ソースコード調査エージェント
│ └── tools/
│ ├── bash_exec.py # シェルコマンド実行(ホワイトリスト)
│ ├── mysql_query.py # MySQL SELECT 実行
│ └── read_file.py # ファイル読み取り(パストラバーサル防止)
├── prompts/
│ ├── system.md # 調査システムプロンプト
│ └── classifier.md # 分類プロンプト
└── tests/
- bash_exec:
WORKFLOW.mdのallowed_commandsに定義されたコマンドのみ実行可能 - mysql_query:
SELECT/SHOWのみ許可。UPDATE,DELETE,DROP等は拒否 - read_file: ワークスペースディレクトリ外へのアクセスを拒否(パストラバーサル防止)
- API キー: 全て環境変数経由。コードやファイルにハードコードしない
- MySQL ユーザー: READ ONLY 権限のユーザーを使うことを推奨