discover-tasks

Discover tasks from configured sources, validate them, and present for user selection.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "discover-tasks" with this command: npx skills add avifenesh/agentsys/avifenesh-agentsys-discover-tasks

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 CLI

  • gh-projects : GitHub Projects (v2 boards)

  • gitlab : GitLab CLI

  • local / tasks-md : Local markdown files

  • custom : CLI/MCP/Skill tool

  • other : 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.json

GitLab Issues:

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

Local 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] 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_issues

Phase 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.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(); }

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:

  • Suggest creating issues

  • Suggest running /audit-project

  • 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

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Automation

debate

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

consult

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

validate-delivery

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

orchestrate-review

No summary provided by upstream source.

Repository SourceNeeds Review