
Discover Tasks
- 62 installs
- 931 repo stars
- Updated July 26, 2026
- avifenesh/agentsys
discover-tasks is a Claude skill that discovers and ranks open tasks from GitHub, GitLab, and local files, then presents them for selection.
About
This skill discovers tasks from configured sources such as GitHub issues, GitHub Projects, GitLab, and local markdown files, validates them, and presents a ranked list for the user to pick from. It excludes already-claimed tasks and issues that already have an open PR. Developers use it to decide what to work on next or to list open issues across sources.
- Fetches tasks from GitHub issues and Projects, GitLab, local markdown, and custom sources
- Filters out already-claimed tasks and issues with an existing open PR
- Runs inside the /next-task workflow or standalone for task selection
Discover Tasks by the numbers
- 62 all-time installs (skills.sh)
- Ranked #1,543 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 triage · task prioritization
- Works with
- github · gitlab
- Use cases
- project management · planning
What discover-tasks says it does
Discover tasks from configured sources, validate them, and present for user selection.
Discovers and ranks tasks from GitHub, GitLab, local files, and custom sources.
npx skills add https://github.com/avifenesh/agentsys --skill discover-tasksAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 62 |
|---|---|
| repo stars | ★ 931 |
| Last updated | July 26, 2026 |
| Repository | avifenesh/agentsys ↗ |
What it does
Discover and rank open tasks from GitHub, GitLab, and local files, then present them for selection.
Who is it for?
Deciding what to work on next by pulling and ranking open issues across sources
Skip if: Executing a task or writing code once one is chosen
When should I use this skill?
The user asks to discover tasks, find the next task, prioritize issues, or list open issues
What you get
A validated, ranked list of open tasks ready to select, excluding claimed or PR-linked ones.
- ranked task list
- validated selectable tasks
By the numbers
- 6 task source types
- up to 100 issues fetched per source
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 task IDs from registry (tasks[] contains worktree-manager claims)
const tasksRegistry = workflowState.readTasks();
const claimedIds = new Set(tasksRegistry.tasks.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 are supported?
GitHub issues, GitHub Projects v2, GitLab, local markdown files like PLAN.md and TODO.md, and custom CLI/MCP/skill sources.
Does it show tasks already in progress?
No, it excludes already-claimed task IDs and GitHub issues that already have an associated open PR.