Skip to content

tango238/code-detective

Repository files navigation

Code Detective

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 ツールが使用

API キーの設定

環境変数で設定する。.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_KEYWORKFLOW.md${LINEAR_API_KEY} で参照される。 GITHUB_TOKEN が設定されている場合、WORKFLOW.mdrepos[].url が SSH 形式 (git@github.com:...) でも自動的に HTTPS + トークン認証に変換される。未設定の場合は URL がそのまま使われる。

LINEAR_API_KEY の取得

  1. Linear にログイン
  2. 左下の Settings (歯車アイコン) → AccountSecurity & Access
  3. Personal API keys セクションで Create key をクリック
  4. ラベル(例: investigation-service)を入力
  5. 権限を設定:
    • Read — イシューの取得に必要
    • Write — コメント投稿とステート変更に必要
    • 必要に応じて特定チームに限定可能
  6. 作成後に表示されるキー (lin_api_...) をコピーして .env に設定

キーは作成時に一度だけ表示される。紛失した場合は再作成が必要。

GITHUB_TOKEN の取得

  1. GitHub にログイン
  2. 右上のアイコン → Settings → 左メニュー最下部の Developer settings
  3. Personal access tokensFine-grained tokensGenerate new token
  4. 設定:
    • Token name: investigation-service
    • Expiration: 任意(90 days 等)
    • Repository access: Only select repositories → 調査対象のリポジトリを選択
    • PermissionsContents: Read-only(clone に必要な最小権限)
  5. Generate token をクリックし、表示されるトークン (github_pat_...) をコピーして .env に設定

Classic token (ghp_...) でも動作するが、Fine-grained token の方がリポジトリ単位で権限を絞れるため推奨。

セットアップ

1. 環境変数を設定

.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.mdrepos[].url が SSH 形式 (git@github.com:...) でも自動的に HTTPS + トークン認証に変換される。

2. WORKFLOW.md を編集

WORKFLOW.md は YAML front matter(設定)+ Markdown body(プロンプトテンプレート)で構成される。 環境変数は ${ENV_VAR} の形式で参照できる。

tracker — Linear 連携

tracker:
  api_key: ${LINEAR_API_KEY}
  project_slugs: ["your-team/investigation"]  # 対象プロジェクト
  active_states: ["Investigating"]            # ポーリング対象のステート
  terminal_states: ["Done", "Canceled"]
  polling_interval_ms: 30000                  # ポーリング間隔(30秒)

agent — LLM プロバイダ

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

workspace — 調査対象リポジトリ

複数のリポジトリをワークスペースに 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 — 許可コマンド

ホワイトリスト方式。ここに定義されたコマンドのみエージェントが実行できる。

tools:
  bash:
    allowed_commands:
      - "grep"
      - "find"
      - "cat"
      - "head"
      - "tail"
      - "wc"
      - "ls"
      - "git log"
      - "git show"
      - "git diff"
      - "git blame"

tools.mysql — DB 接続

tools:
  mysql:
    host: ${MYSQL_HOST}
    port: 3306
    user: ${MYSQL_USER}
    password: ${MYSQL_PASSWORD}
    database: ${MYSQL_DATABASE}

tools.mysql.ssh_tunnel — SSH トンネル経由の DB 接続

本番 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 で新設定が反映される。

3. Linear にカスタムステートを追加

チームのワークフローに以下を追加する。

ステート タイプ 用途
Investigating Started デーモンがポーリングする対象
Human Review Started 調査完了、人間のレビュー待ち
Rework Started 追加情報が必要

イシューを Investigating に移動すると、デーモンが自動的に検知して調査を開始する。

4. 起動

Docker(推奨)

# ビルド & 起動
docker compose up -d

# ログを確認
docker compose logs -f

# 停止
docker compose down

SSH トンネル用の鍵は .envSSH_TUNNEL_KEY_PATH_HOST に指定したホスト側パスからコンテナ内 /root/.ssh/tunnel_key にマウントされる。コンテナ内パスは WORKFLOW.md にハードコードされているため設定不要。 WORKFLOW.mdssh_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.mdallowed_commands に定義されたコマンドのみ実行可能
  • mysql_query: SELECT / SHOW のみ許可。UPDATE, DELETE, DROP 等は拒否
  • read_file: ワークスペースディレクトリ外へのアクセスを拒否(パストラバーサル防止)
  • API キー: 全て環境変数経由。コードやファイルにハードコードしない
  • MySQL ユーザー: READ ONLY 権限のユーザーを使うことを推奨

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors