0Pricing
AI Agents · レッスン

Issueの一覧表示と管理

プログラムからIssueを取得、作成、ラベル付け、クローズします。

「Issueの一覧表示と管理」はCoddyKit上の無料AI Agentsレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはAI Agents学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 AI Agentsコースには全4レッスンが含まれています。

リポジトリからissueを取得する

repo.get_issues()を使ってissueを一覧取得します。デフォルトではオープンなissueが返されます。ほかの状態を取得するには、state='closed'またはstate='all'を渡してください。結果はPaginatedListなので、反復処理して各issueを処理します。

from github import Github
import os

g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')

# Open issues (default)
open_issues = repo.get_issues(state='open')
print(f'Open issues: {open_issues.totalCount}')

# Process first 20
for issue in open_issues[:20]:
    print(f'#{issue.number}: {issue.title}')
    print(f'  Labels: {[l.name for l in issue.labels]}')
    print(f'  Assignee: {issue.assignee.login if issue.assignee else None}')
    print(f'  Created: {issue.created_at.date()}')

issueの絞り込み

get_issues()は、state、labels、assignee、milestone、since、sortなど、複数のフィルターパラメーターに対応しています。これらを組み合わせることで、エージェントが処理する必要のあるissueだけを正確に取得できます。

import datetime
from github import Github
import os

g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')

# Issues with the 'bug' label, sorted by newest
bug_issues = repo.get_issues(
    state='open',
    labels=['bug'],
    sort='created',
    direction='desc'
)

# Issues updated in the last 7 days
cutoff = datetime.datetime.now() - datetime.timedelta(days=7)
recent_issues = repo.get_issues(
    state='open',
    since=cutoff
)

# Issues assigned to a specific user
my_issues = repo.get_issues(
    state='open',
    assignee='username'
)

print(f'Bug issues: {bug_issues.totalCount}')
print(f'Recent issues: {recent_issues.totalCount}')

issueプロパティへのアクセス

各Issueオブジェクトには、番号、タイトル、本文、ラベル、担当者、マイルストーン、状態、コメント数、タイムスタンプなど、豊富なプロパティがあります。これらにはPythonの属性としてアクセスします。本文は生のMarkdownテキストです。

from github import Github
import os

g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')
issue = repo.get_issue(42)  # get by number

print(f'Number: #{issue.number}')
print(f'Title: {issue.title}')
print(f'State: {issue.state}')        # 'open' or 'closed'
print(f'Author: {issue.user.login}')
print(f'Body length: {len(issue.body or "")}')
print(f'Labels: {[l.name for l in issue.labels]}')
print(f'Assignees: {[a.login for a in issue.assignees]}')
print(f'Milestone: {issue.milestone.title if issue.milestone else None}')
print(f'Comments: {issue.comments}')
print(f'Reactions: {issue.get_reactions().totalCount}')
print(f'URL: {issue.html_url}')

新しいissueの作成

repo.create_issue()を使って、新しいissueをプログラムから作成します。必須フィールドはtitleだけです。body(Markdown)、labels(あらかじめ存在している必要があります)、assignees(GitHubユーザー名)、milestone(マイルストーンオブジェクト)を追加できます。

from github import Github
import os

g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')

# Create a bug report
bug_body = (
    '## Bug Report\n\n'
    '**Observed:** Login fails with 500 error\n'
    '**Expected:** Successful login\n'
    '**Steps to reproduce:**\n'
    '1. Navigate to /login\n'
    '2. Enter valid credentials\n'
    '3. Click Submit\n\n'
    '**Environment:** Production, v2.3.1'
)

issue = repo.create_issue(
    title='Login endpoint returns 500 for valid credentials',
    body=bug_body,
    labels=['bug', 'priority:high'],
    assignees=['alice'],       # must be repo collaborators
)

print(f'Created issue #{issue.number}: {issue.html_url}')

issueの編集(更新)

issue.edit()を使って、既存のissueの任意のプロパティを更新します。1回の呼び出しで、タイトル、本文、状態、ラベル、担当者、マイルストーンを変更できます。変更したいフィールドだけを渡してください。それ以外のフィールドは保持されます。

from github import Github
import os

g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')
issue = repo.get_issue(42)

# Update title and add new labels
issue.edit(
    title='[URGENT] Login endpoint returns 500',
    labels=['bug', 'priority:critical', 'needs-hotfix']
)

# Close the issue
issue.edit(state='closed')
print(f'Issue #{issue.number} closed')

# Reopen it
issue.edit(state='open')

# Assign to a user
from github import NamedUser
issue.edit(assignees=['bob', 'carol'])

# Update the body
issue.edit(body=issue.body + '\n\n**Update:** Hotfix deployed to production.')

ラベルの管理

ラベルはissueを整理するために使用します。repo.get_labels()でリポジトリ内のすべてのラベルを取得し、repo.create_label()で新しいラベルを作成します。issueへのラベルの追加と削除には、issue.add_to_labels()とissue.remove_from_labels()を使用します。

from github import Github
import os

g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')

# List all labels
for label in repo.get_labels():
    print(f'{label.name}: #{label.color}')

# Create a new label if it doesn't exist
existing_names = {l.name for l in repo.get_labels()}
if 'agent-reviewed' not in existing_names:
    repo.create_label(
        name='agent-reviewed',
        color='0075ca',       # hex without #
        description='Reviewed by AI agent'
    )
    print('Created label: agent-reviewed')

# Add a label to an issue
issue = repo.get_issue(42)
issue.add_to_labels('agent-reviewed')

# Remove a label
issue.remove_from_labels('needs-triage')

issueコメントの読み取り

issue.get_comments()を使って、issue上のすべてのコメントを取得します。各コメントには、作成者(user.login)、本文、タイムスタンプがあります。順番に反復処理すると、会話の履歴を確認できます。

from github import Github
import os

g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')
issue = repo.get_issue(42)

# Get all comments
comments = list(issue.get_comments())
print(f'Comments on #{issue.number}: {len(comments)}')

for comment in comments:
    print(f'\n{comment.user.login} ({comment.created_at.date()}):')
    print(comment.body[:200])

# Get only recent comments
for comment in issue.get_comments(since=cutoff):
    print(f'Recent: {comment.user.login}: {comment.body[:100]}')

issueへのコメント追加

issue.create_comment(body)を使ってコメントを投稿します。本文はMarkdownに対応しています。エージェントのコメントは自動投稿であることが明確に分かるようにし、期待値を設定するために「🤖 Automated analysis」というヘッダーを含めてください。

from github import Github
import os
import datetime

g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')
issue = repo.get_issue(42)

# Add an agent comment with clear attribution
agent_comment = (
    '**Automated Analysis** (via code-review agent)\n\n'
    '---\n'
    f'**Issue classified as:** Critical Bug\n'
    f'**Suggested priority:** P0\n'
    f'**Suggested assignee:** @backend-team\n\n'
    '**Analysis:**\n'
    'The login 500 error matches pattern #12 in our known issues database.\n'
    'Likely cause: database connection pool exhaustion during peak traffic.\n\n'
    '**Suggested fix:** Increase pool size in `db_config.py` or add circuit breaker.\n\n'
    f'_Generated at {datetime.datetime.now().strftime("%Y-%m-%d %H:%M UTC")}_'
)

comment = issue.create_comment(agent_comment)
print(f'Comment added: {comment.html_url}')

issue一括トリアージエージェント

トリアージエージェントは、新しいissueを自動的に処理します。タイトルと本文を読み取り、LLMで分類し、適切なラベルを追加し、適切なチームに割り当て、最初のコメントを投稿します。これにより、エンジニアが手作業でトリアージする時間を大幅に削減できます。

import anthropic
import json
import os
from github import Github

g = Github(token=os.environ['GITHUB_TOKEN'])
client = anthropic.Anthropic(api_key=os.environ['ANTHROPIC_API_KEY'])

def triage_issue(repo, issue):
    prompt = (
        'Classify this GitHub issue. Return JSON only:\n'
        '{"type": "bug|feature|question|docs", '
        '"priority": "critical|high|medium|low", '
        '"team": "backend|frontend|infra|ml", '
        '"summary": "one sentence"}\n\n'
        f'Title: {issue.title}\n'
        f'Body: {(issue.body or "")[:1000]}'
    )
    response = client.messages.create(
        model='claude-opus-4-5',
        max_tokens=200,
        messages=[{'role': 'user', 'content': prompt}]
    )
    try:
        return json.loads(response.content[0].text)
    except json.JSONDecodeError:
        return {'type': 'question', 'priority': 'medium', 'team': 'backend', 'summary': 'Needs manual review'}

issueへのトリアージ結果の適用

LLMによるトリアージ分類をissueに適用します。種別と優先度に基づいてラベルを設定し、適切なチームリードに割り当て、トリアージコメントを投稿します。再処理を避けるため、カスタムラベルを使ってトリアージ済みのissueを記録します。

TEAM_ASSIGNEES = {
    'backend': 'alice',
    'frontend': 'bob',
    'infra': 'carol',
    'ml': 'dave'
}

def apply_triage(repo, issue, classification):
    issue_type = classification.get('type', 'question')
    priority = classification.get('priority', 'medium')
    team = classification.get('team', 'backend')
    summary = classification.get('summary', '')

    # Apply labels
    labels_to_add = [f'type:{issue_type}', f'priority:{priority}', 'agent-triaged']
    issue.edit(labels=labels_to_add)

    # Assign to team lead
    assignee = TEAM_ASSIGNEES.get(team, 'alice')
    issue.edit(assignees=[assignee])

    # Post triage comment
    comment = (
        f'**Automated Triage**\n\n'
        f'Type: `{issue_type}` | Priority: `{priority}` | Team: `{team}`\n'
        f'Summary: {summary}\n\n'
        f'Assigned to @{assignee} for follow-up.'
    )
    issue.create_comment(comment)
    print(f'Triaged #{issue.number}: {issue_type}/{priority} -> {assignee}')

# --- demo: minimal stand-in for a GitHub Issue object ---
class _FakeIssue:
    number = 501
    def edit(self, **kwargs):
        print(f'[github] issue.edit({kwargs})')
    def create_comment(self, body):
        print(f'[github] comment posted:\n{body}')

classification = {'type': 'bug', 'priority': 'high', 'team': 'backend',
                   'summary': 'Login fails with 500 when email has a plus sign'}
apply_triage('coddy-agents', _FakeIssue(), classification)

古いissueの自動クローズ

古いissueの管理は、よくある自動化処理です。N日間活動のないオープンなissueを見つけて警告コメントを投稿し、猶予期間内に返信がなければクローズします。活動状況の確認にはissue.updated_atを使用し、クローズにはissue.edit(state='closed')を使用します。

import datetime
from github import Github
import os

g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')

STALE_DAYS = 90
GRACE_DAYS = 14

now = datetime.datetime.utcnow()
stale_cutoff = now - datetime.timedelta(days=STALE_DAYS)

for issue in repo.get_issues(state='open'):
    last_updated = issue.updated_at.replace(tzinfo=None)
    if last_updated > stale_cutoff:
        continue

    # Check if stale warning already posted
    comments = list(issue.get_comments())
    stale_warned = any('stale' in c.body.lower() for c in comments)

    if not stale_warned:
        issue.create_comment(
            'This issue has been inactive for 90 days. '
            'It will be closed in 14 days unless there is activity.'
        )
        issue.add_to_labels('stale')
        print(f'Stale warning: #{issue.number}')

クイックチェック: issue.edit()とcreate_issue()の違い

GitHub issueの管理についての理解度を確認しましょう。

issue管理の振り返り

これで、エージェントからGitHub issueを最初から最後まで管理できるようになりました:

  • repo.get_issues(state, labels, assignee, since) — issueを絞り込んで一覧取得します
  • repo.get_issue(number) — 番号を指定して特定のissueを取得します
  • repo.create_issue(title, body, labels, assignees) — 新しいissueを作成します
  • issue.edit(state, title, labels, assignees) — 任意のフィールドを更新します
  • issue.create_comment(body) — Markdownコメントを追加します
  • repo.create_label() / issue.add_to_labels() — ラベルを管理します
  • エージェントのコメントは、人間による活動と区別できるよう、常に自動投稿であることを明確に示します
  • 再処理を防ぐために'agent-triaged'ラベルを使用します

よくある質問

「Issueの一覧表示と管理」レッスンは無料ですか?

はい。「Issueの一覧表示と管理」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、AI Agentsコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 AI Agentsコースには全4レッスンが含まれています。

「Issueの一覧表示と管理」で何を学びますか?

プログラムからIssueを取得、作成、ラベル付け、クローズします。 ブラウザで直接実行するハンズオンコードでAI Agentsを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

AI Agentsを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのAI Agentsは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「Issueの一覧表示と管理」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このAI Agentsレッスンでコードを書いて実行できますか?

はい。すべてのAI Agentsレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. GitHub REST API概要
  2. Issueの一覧表示と管理
  3. PRレビューコメントの自動化
  4. コミット履歴と差分の分析
← AI Agentsに戻る