mcpField
D
discover-tasks

discover-tasks

github.com/agent-sh開発1.0k85 回インストール2 日前に更新
リポジトリ

discover-tasks について

開発ワークフロー向けのタスク検出スキルです。設定済みのソースからタスクを集め、絞り込みと評価を行い、候補をユーザーに提示します。

主な機能

  1. 01設定済みソースからタスクを取得
  2. 02担当済みタスクとPR関連の課題を除外
  3. 03優先度フィルターとスコアリング
  4. 04最大5件のタスクを選択肢として提示
  5. 05選択したタスクをワークフロー状態に反映

活用シーン

  1. 01ワークフロー用のGitHub Issueの選択
  2. 02GitLabやローカルMarkdownからのタスク検索
  3. 03設定済みのカスタムソースからの作業項目選択

MCP 対応

カスタムソースでは、CLI、MCP、またはスキルのツールを使って課題を一覧できます。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/agent-sh/agentsys --skill discover-tasks

インストール後、Claude Code で/discover-tasksを入力して読み込まれたか確認します。

詳細

discover-tasks

設定されたソースからタスクを見つけて検証し、ユーザーが選択できるように提示します。

使用するタイミング

ポリシー選択後、/next-task ワークフローのフェーズ 2 で呼び出されます。設定されたソースからタスクを見つけて選択したい場合は、単独でも使用できます。

ワークフロー

フェーズ 1:ポリシーとクレーム済みタスクを読み込む
// Use relative path from skill directory to plugin lib
// Path: skills/discover-tasks/ -> ../../lib/state/workflow-state.js
const workflowState = require('../../lib/state/workflow-state.js');

const state = workflowState.readState();
const policy = state.policy;

// Load claimed task IDs from registry (tasks[] contains worktree-manager claims)
const tasksRegistry = workflowState.readTasks();
const claimedIds = new Set(tasksRegistry.tasks.map(t => t.id));
フェーズ 2:ソースごとにタスクを取得する

ソースの種類:

  • github / gh-issues:GitHub CLI
  • gh-projects:GitHub Projects(v2 ボード)
  • gitlab:GitLab CLI
  • local / tasks-md:ローカルの Markdown ファイル
  • custom:CLI/MCP/スキルツール
  • other:エージェントが説明を解釈

GitHub Issues:

# Fetch with pagination awareness
gh issue list --state open \
  --json number,title,body,labels,assignees,createdAt,url \
  --limit 100 > /tmp/gh-issues.json

GitLab Issues:

glab issue list --state opened --output json --per-page 100 > /tmp/glab-issues.json

ローカルの tasks.md:

for f in PLAN.md tasks.md TODO.md; do
  [ -f "$f" ] && grep -n '^\s*- \[ \]' "$f"
done

GitHub Projects(v2):

// Extract gh-projects parameters from policy
const projectNumber = policy.taskSource.projectNumber;
const owner = policy.taskSource.owner;
if (!projectNumber || !owner) {
  throw new Error('gh-projects source missing projectNumber or owner in policy.taskSource');
}
# Requires 'project' token scope. If permission error: gh auth refresh -s project
gh project item-list "$PROJECT_NUMBER" --owner "$OWNER" --format json --limit 100 > /tmp/gh-project-items.json
const fs = require('fs');
const raw = JSON.parse(fs.readFileSync('/tmp/gh-project-items.json', 'utf8'));
const items = (raw.items || []);

// Filter to ISSUE type only (exclude PULL_REQUEST, DRAFT_ISSUE)
const issues = items
  .filter(item => item.content && item.content.type === 'ISSUE')
  .map(item => ({
    number: item.content.number,
    title: item.content.title,
    body: item.content.body || '',
    labels: (item.content.labels || []).map(l => typeof l === 'object' ? l.name || '' : l).filter(Boolean),
    url: item.content.url,
    createdAt: item.content.createdAt
  }));

[WARN] gh project item-list が権限エラーを返した場合は、ユーザーに次のように伝えてください: Run: gh auth refresh -s project

カスタムソース:

const { sources } = require('../../lib');
const capabilities = sources.getToolCapabilities(toolName);
// Execute capabilities.commands.list_issues
フェーズ 2.5:PR に関連付けられた Issue を収集する(GitHub のみ)
// Default for non-GitHub sources - always defined so Phase 3 filter is safe
let prLinkedIssues = new Set();

GitHub ソース(policy.taskSource?.source === 'github'、'gh-issues'、または 'gh-projects')では、すべてのオープン PR を取得し、すでに PR が関連付けられている Issue 番号の Set を作成します。それ以外のすべてのソースでは、フェーズ 3 に進みます。

# Only run when policy.taskSource?.source is 'github', 'gh-issues', or 'gh-projects'
# Note: covers up to 100 open PRs. If repo has more, some linked issues may not be excluded.
gh pr list --state open --json number,title,body,headRefName --limit 100 > /tmp/gh-prs.json
const fs = require('fs');
try {
  const prs = JSON.parse(fs.readFileSync('/tmp/gh-prs.json', 'utf8') || '[]');

  for (const pr of prs) {
    // 1. Branch name suffix: fix/some-thing-123 extracts 123
    // Note: heuristic - branches like "release-2026" will false-positive on issue #2026.
    // Patterns 2 and 3 are more precise; this is a best-effort supplement.
    const branchMatch = (pr.headRefName || '').match(/-(\d+)$/);
    if (branchMatch) prLinkedIssues.add(branchMatch[1]);

    // 2. PR body closing keywords (GitHub's full keyword set, with word boundary)
    if (pr.body) {
      const bodyMatches = pr.body.matchAll(/\b(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s+#(\d+)/gi);
      for (const m of bodyMatches) prLinkedIssues.add(m[1]);
    }

    // 3. PR title (#N) convention - capture all occurrences
    const titleMatches = (pr.title || '').matchAll(/\(#(\d+)\)/g);
    for (const m of titleMatches) prLinkedIssues.add(m[1]);
  }
} catch (e) {
  console.log('[WARN] Could not parse open PRs, skipping PR-link filter:', e.message);
  prLinkedIssues = new Set();
}
フェーズ 3:フィルタリングとスコア付け

クレーム済みタスクを除外する:

const available = tasks.filter(t => !claimedIds.has(String(t.number || t.id)));

オープン PR がある Issue を除外する(GitHub のみ):

const filtered = available.filter(t => {
  const id = String(t.number || t.id);
  if (prLinkedIssues.has(id)) {
    console.log(`[INFO] Skipping #${id} - already has an open PR`);
    return false;
  }
  return true;
});

優先度フィルターを適用する(filtered をスコア付けパイプラインに渡す):

const LABEL_MAPS = {
  bugs: ['bug', 'fix', 'error', 'defect'],
  security: ['security', 'vulnerability', 'cve'],
  features: ['enhancement', 'feature', 'improvement']
};

function filterByPriority(tasks, filter) {
  if (filter === 'continue' || filter === 'all') return tasks;
  const targetLabels = LABEL_MAPS[filter] || [];
  return tasks.filter(t => {
    const labels = (t.labels || []).map(l => (l.name || l).toLowerCase());
    return targetLabels.some(target => labels.some(l => l.includes(target)));
  });
}

const prioritized = filterByPriority(filtered, policy.priorityFilter);
// Assign score to each task so it is available for display in the UI
const topTasks = prioritized.map(t => ({ ...t, score: scoreTask(t) })).sort((a, b) => b.score - a.score);

タスクにスコアを付ける:

function scoreTask(task) {
  let score = 0;
  const labels = (task.labels || []).map(l => (l.name || l).toLowerCase());

  // Priority labels
  if (labels.some(l => l.includes('critical') || l.includes('p0'))) score += 100;
  if (labels.some(l => l.includes('high') || l.includes('p1'))) score += 50;
  if (labels.some(l => l.includes('security'))) score += 40;

  // Quick wins
  if (labels.some(l => l.includes('small') || l.includes('quick'))) score += 20;

  // Age (older bugs get priority)
  if (task.createdAt) {
    const ageInDays = (Date.now() - new Date(task.createdAt)) / 86400000;
    if (labels.includes('bug') && ageInDays > 30) score += 10;
  }

  return score;
}
フェーズ 4:AskUserQuestion でユーザーに提示する

重要:ラベルは最大 30 文字にする必要があります(OpenCode の制限)。

function truncateLabel(num, title) {
  const prefix = `#${num}: `;
  const maxLen = 30 - prefix.length;
  return title.length > maxLen
    ? prefix + title.substring(0, maxLen - 1) + '...'
    : prefix + title;
}

const options = topTasks.slice(0, 5).map(task => ({
  label: truncateLabel(task.number, task.title),
  description: `Score: ${task.score} | ${(task.labels || []).slice(0, 2).join(', ')}`
}));

AskUserQuestion({
  questions: [{
    header: "Select Task",
    question: "Which task should I work on?",
    options,
    multiSelect: false
  }]
});
フェーズ 5:状態を更新する
workflowState.updateState({
  task: {
    id: String(selectedTask.number),
    source: policy.taskSource?.source || policy.taskSource,
    title: selectedTask.title,
    description: selectedTask.body || '',
    labels: selectedTask.labels?.map(l => l.name || l) || [],
    url: selectedTask.url
  }
});

workflowState.completePhase({
  tasksAnalyzed: tasks.length,
  selectedTask: selectedTask.number
});
フェーズ 6:コメントを投稿する(GitHub のみ)

GitHub 以外のソース(GitLab、ローカル、カスタム)では、このフェーズ全体をスキップしてください。 github、gh-issues、gh-projects ソースの場合に実行します。

# Only run for GitHub sources (github, gh-issues, gh-projects). Use policy.taskSource?.source from Phase 1 to check.
gh issue comment "$TASK_ID" --body "[BOT] Workflow started for this issue."

出力形式

## Task Selected

**Task**: #{id} - {title}
**Source**: {source}
**URL**: {url}

Proceeding to worktree setup...

エラー処理

タスクが見つからない場合:

  1. Issue の作成を提案します
  2. /audit-project の実行を提案します
  3. 優先度フィルターに「すべて」を使用するよう提案します

制約

  • タスクの選択には AskUserQuestion を必ず使用してください(プレーンテキストは不可)
  • ラベルは最大 30 文字にする必要があります
  • 他のワークフローがすでにクレームしているタスクを除外します
  • すでにオープン PR がある Issue を除外します(GitHub および GitHub Projects ソース)
  • PR との関連付けの検出対象は、最大 100 件のオープン PR です(取得上限は --limit 100)
  • 上位 5 件のタスクのみ

コメント