discover-tasks

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

discover-tasks

任务发现

Discover tasks from configured sources, validate them, and present for user selection.
从已配置的来源中发现任务,验证任务并呈现给用户选择。

Workflow

工作流程

Phase 1: Load Policy and Claimed Tasks

阶段1:加载策略与已认领任务

javascript
// Use relative path from skill directory to plugin lib
// Path: skills/task-discovery/ -> ../../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));
javascript
// Use relative path from skill directory to plugin lib
// Path: skills/task-discovery/ -> ../../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

阶段2:按来源获取任务

Source types:
  • github
    /
    gh-issues
    : GitHub CLI
  • gitlab
    : GitLab CLI
  • local
    /
    tasks-md
    : Local markdown files
  • custom
    : CLI/MCP/Skill tool
  • other
    : Agent interprets description
GitHub Issues:
bash
undefined
来源类型:
  • github
    /
    gh-issues
    : GitHub CLI
  • gitlab
    : GitLab CLI
  • local
    /
    tasks-md
    : 本地Markdown文件
  • custom
    : CLI/MCP/Skill工具
  • other
    : Agent解析描述
GitHub Issues:
bash
undefined

Fetch with pagination awareness

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:**
```bash
glab issue list --state opened --output json --per-page 100 > /tmp/glab-issues.json
Local tasks.md:
bash
for f in PLAN.md tasks.md TODO.md; do
  [ -f "$f" ] && grep -n '^\s*- \[ \]' "$f"
done
Custom Source:
javascript
const { sources } = require('../../lib');
const capabilities = sources.getToolCapabilities(toolName);
// Execute capabilities.commands.list_issues
gh issue list --state open
--json number,title,body,labels,assignees,createdAt,url
--limit 100 > /tmp/gh-issues.json

**GitLab Issues:**
```bash
glab issue list --state opened --output json --per-page 100 > /tmp/glab-issues.json
本地tasks.md文件:
bash
for f in PLAN.md tasks.md TODO.md; do
  [ -f "$f" ] && grep -n '^\s*- \[ \]' "$f"
done
自定义来源:
javascript
const { sources } = require('../../lib');
const capabilities = sources.getToolCapabilities(toolName);
// Execute capabilities.commands.list_issues

Phase 3: Filter and Score

阶段3:过滤与评分

Exclude claimed tasks:
javascript
const available = tasks.filter(t => !claimedIds.has(String(t.number || t.id)));
Apply priority filter:
javascript
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)));
  });
}
Score tasks:
javascript
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;
}
排除已认领任务:
javascript
const available = tasks.filter(t => !claimedIds.has(String(t.number || t.id)));
应用优先级过滤:
javascript
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)));
  });
}
任务评分:
javascript
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

阶段4:通过AskUserQuestion呈现给用户

CRITICAL: Labels MUST be max 30 characters (OpenCode limit).
javascript
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
  }]
});
重要提示: 标签长度必须不超过30个字符(OpenCode限制)。
javascript
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

阶段5:更新状态

javascript
workflowState.updateState({
  task: {
    id: String(selectedTask.number),
    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
});
javascript
workflowState.updateState({
  task: {
    id: String(selectedTask.number),
    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)

阶段6:发布评论(仅支持GitHub)

bash
gh issue comment "$TASK_ID" --body "[BOT] Workflow started for this issue."
bash
gh issue comment "$TASK_ID" --body "[BOT] Workflow started for this issue."

Output Format

输出格式

markdown
undefined
markdown
undefined

Task Selected

Task Selected

Task: #{id} - {title} Source: {source} URL: {url}
Proceeding to worktree setup...
undefined
Task: #{id} - {title} Source: {source} URL: {url}
Proceeding to worktree setup...
undefined

Error Handling

错误处理

If no tasks found:
  1. Suggest creating issues
  2. Suggest running /audit-project
  3. Suggest using 'all' priority filter
如果未找到任务:
  1. 建议创建问题
  2. 建议运行/audit-project
  3. 建议使用“all”优先级过滤器

Constraints

约束条件

  • MUST use AskUserQuestion for task selection (not plain text)
  • Labels MUST be max 30 characters
  • Exclude tasks already claimed by other workflows
  • Top 5 tasks only
  • 必须使用AskUserQuestion进行任务选择(不能使用纯文本)
  • 标签长度必须不超过30个字符
  • 排除已被其他工作流认领的任务
  • 仅展示排名前5的任务