
Discover Tasks
- 2 installs
- 931 repo stars
- Updated July 26, 2026
- avifenesh/awesome-slash
discover-tasks is a Claude Code skill that discovers and ranks open tasks from GitHub, GitLab, and local files so a developer can pick what to work on next.
About
discover-tasks pulls open work items from GitHub Issues, GitHub Projects v2, GitLab, and local markdown task files, then filters out claimed tasks and issues that already have an open PR. It scores the remaining tasks with a priority filter using label maps for bugs, security, and features, and presents them for the developer to pick. It runs standalone or as Phase 2 of the /next-task workflow.
- Discovers open tasks from GitHub Issues, GitHub Projects v2, GitLab, and local PLAN.md/TODO.md files
- Excludes already-claimed tasks and issues that already have an open PR
- Scores tasks by priority using label maps for bugs, security, and features
Discover Tasks by the numbers
- 2 all-time installs (skills.sh)
- Ranked #2,419 of 3,282 Productivity & Planning skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
discover-tasks capabilities & compatibility
- Capabilities
- task discovery · issue prioritization · backlog triage
- Works with
- github · gitlab
- Use cases
- planning · project management
What discover-tasks says it does
Discover tasks from configured sources, validate them, and present for user selection.
Exclude issues with open PRs (GitHub only)
npx skills add https://github.com/avifenesh/awesome-slash --skill discover-tasksAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 2 |
|---|---|
| repo stars | ★ 931 |
| Last updated | July 26, 2026 |
| Repository | avifenesh/awesome-slash ↗ |
What it does
Find and rank the next task to work on across GitHub, GitLab, and local task files.
Who is it for?
Deciding the next issue to work on across GitHub, GitLab, and local task files
Skip if: Writing the actual fix or feature code for a chosen task
When should I use this skill?
The user asks what to work on next or to list and prioritize open issues
What you get
A ranked, deduplicated shortlist of open tasks ready for selection.
- Ranked list of selectable tasks
By the numbers
- Fetches up to 100 open PRs when filtering PR-linked issues
- Priority label maps cover 3 categories: bugs, security, features
Files
discover-tasks
Discover tasks from configured sources, validate them, and present for user selection.
When to Use
Invoked during Phase 2 of /next-task workflow, after policy selection. Also usable standalone when the user wants to discover and select tasks from configured sources.
Workflow
Phase 1: Load Policy and Claimed Tasks
// 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 tasks from registry
const claimedTasks = workflowState.readTasks().tasks || [];
const claimedIds = new Set(claimedTasks.map(t => t.id));Phase 2: Fetch Tasks by Source
Source types:
github/gh-issues: GitHub CLIgh-projects: GitHub Projects (v2 boards)gitlab: GitLab CLIlocal/tasks-md: Local markdown filescustom: CLI/MCP/Skill toolother: Agent interprets description
GitHub Issues:
# Fetch with pagination awareness
gh issue list --state open \
--json number,title,body,labels,assignees,createdAt,url \
--limit 100 > /tmp/gh-issues.jsonGitLab Issues:
glab issue list --state opened --output json --per-page 100 > /tmp/glab-issues.jsonLocal tasks.md:
for f in PLAN.md tasks.md TODO.md; do
[ -f "$f" ] && grep -n '^\s*- \[ \]' "$f"
doneGitHub 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.jsonconst 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] If gh project item-list returns a permission error, tell the user: Run: gh auth refresh -s project
Custom Source:
const { sources } = require('../../lib');
const capabilities = sources.getToolCapabilities(toolName);
// Execute capabilities.commands.list_issuesPhase 2.5: Collect PR-Linked Issues (GitHub only)
// Default for non-GitHub sources - always defined so Phase 3 filter is safe
let prLinkedIssues = new Set();For GitHub sources (policy.taskSource?.source === 'github', 'gh-issues', or 'gh-projects'), fetch all open PRs and build a Set of issue numbers that already have an associated PR. Skip to Phase 3 for all other sources.
# 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.jsonconst 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();
}Phase 3: Filter and Score
Exclude claimed tasks:
const available = tasks.filter(t => !claimedIds.has(String(t.number || t.id)));Exclude issues with open PRs (GitHub only):
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;
});Apply priority filter (pass filtered through scoring pipeline):
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);Score tasks:
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;
}Phase 4: Present to User via AskUserQuestion
CRITICAL: Labels MUST be max 30 characters (OpenCode limit).
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
}]
});Phase 5: Update State
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
});Phase 6: Post Comment (GitHub only)
Skip this phase entirely for non-GitHub sources (GitLab, local, custom). Run for github, gh-issues, and gh-projects sources.
# 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."Output Format
## Task Selected
**Task**: #{id} - {title}
**Source**: {source}
**URL**: {url}
Proceeding to worktree setup...Error Handling
If no tasks found: 1. Suggest creating issues 2. Suggest running /audit-project 3. Suggest using 'all' priority filter
Constraints
- MUST use AskUserQuestion for task selection (not plain text)
- Labels MUST be max 30 characters
- Exclude tasks already claimed by other workflows
- Exclude issues that already have an open PR (GitHub and GitHub Projects sources)
- PR-link detection covers up to 100 open PRs (--limit 100 is the fetch cap)
- Top 5 tasks only
Related skills
FAQ
Which task sources does discover-tasks support?
GitHub Issues, GitHub Projects v2 boards, GitLab, local markdown files (PLAN.md, tasks.md, TODO.md), and custom CLI/MCP/Skill tools.
Does it skip issues that already have a PR?
Yes, for GitHub sources it builds a set of issue numbers linked to open PRs and excludes them from the results.