gitea_workflow_escalate_priority
Automatically escalate priority for aged issues in Gitea repositories based on predefined time thresholds, ensuring timely attention to pending tasks and security concerns.
Instructions
Automatically escalate priority for aged issues. P3→P2 after 30 days, P2→P1 after 14 days, P1→P0 after 3 days. Security issues are always P0.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| owner | No | Repository owner. Uses context if not provided | |
| repo | No | Repository name. Uses context if not provided | |
| dry_run | No | Preview changes without applying (default: false) |
Implementation Reference
- src/tools/workflow.ts:708-844 (handler)Core handler function implementing the tool logic. Scans open issues, checks age and type against escalation rules (P3-30d->P2, P2-14d->P1, P1-3d->P0, security->P0), updates labels accordingly (dry-run option).export async function workflowEscalatePriority( ctx: WorkflowToolsContext, args: { owner?: string; repo?: string; config?: WorkflowConfig; dry_run?: boolean; } ): Promise<{ success: boolean; escalated: Array<{ number: number; title: string; from_priority: string; to_priority: string; reason: string; }>; error?: string; }> { logger.debug({ args: { ...args, config: args.config ? '[provided]' : undefined } }, 'Escalating priorities'); const { owner, repo } = ctx.contextManager.resolveOwnerRepo(args.owner, args.repo); const dryRun = args.dry_run ?? false; // 获取配置 let config = args.config; if (!config) { const loadResult = await workflowLoadConfig(ctx, { owner, repo }); if (!loadResult.success || !loadResult.config) { return { success: false, escalated: [], error: loadResult.error || '无法加载配置', }; } config = loadResult.config; } const prefixes = getLabelPrefixes(config); const escalated: Array<{ number: number; title: string; from_priority: string; to_priority: string; reason: string; }> = []; try { // 获取开放的 Issue const issues = await ctx.client.get<Issue[]>(`/repos/${owner}/${repo}/issues?state=open&limit=100`); // 获取标签 ID 映射 const repoLabels = await ctx.client.get<Array<{ id: number; name: string }>>( `/repos/${owner}/${repo}/labels` ); const labelIdMap = new Map(repoLabels.map((l) => [l.name, l.id])); const idToLabelMap = new Map(repoLabels.map((l) => [l.id, l.name])); // 定义升级映射 const upgradeMap: Record<string, { days: number; to: string }> = { P3: { days: 30, to: 'P2' }, P2: { days: 14, to: 'P1' }, P1: { days: 3, to: 'P0' }, }; for (const issue of issues) { const currentPriority = getIssuePriority(issue, prefixes); const issueTypeLabel = issue.labels.find((l) => matchLabel(prefixes.type, l.name) !== null); const issueType = issueTypeLabel ? matchLabel(prefixes.type, issueTypeLabel.name) : null; const ageDays = calculateIssueAgeDays(issue); let newPriority: string | null = null; let reason = ''; // 安全问题强制 P0 if (issueType === 'security' && currentPriority !== 'P0') { newPriority = 'P0'; reason = '安全问题自动升级为紧急'; } else if (currentPriority && upgradeMap[currentPriority]) { const upgrade = upgradeMap[currentPriority]; if (ageDays > upgrade.days) { newPriority = upgrade.to; reason = `超过 ${upgrade.days} 天未解决`; } } if (newPriority && currentPriority) { if (!dryRun) { // 移除旧优先级标签 const oldLabelId = labelIdMap.get(buildLabel(prefixes.priority, currentPriority)); if (oldLabelId) { try { await ctx.client.delete( `/repos/${owner}/${repo}/issues/${issue.number}/labels/${oldLabelId}` ); } catch { // 忽略删除失败 } } // 添加新优先级标签 const newLabelId = labelIdMap.get(buildLabel(prefixes.priority, newPriority)); if (newLabelId) { await ctx.client.post(`/repos/${owner}/${repo}/issues/${issue.number}/labels`, { labels: [newLabelId], }); } } escalated.push({ number: issue.number, title: issue.title, from_priority: currentPriority, to_priority: newPriority, reason, }); } } logger.info({ owner, repo, escalated: escalated.length, dry_run: dryRun }, 'Priorities escalated'); return { success: true, escalated, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); logger.error({ owner, repo, error: errorMessage }, 'Failed to escalate priorities'); return { success: false, escalated: [], error: errorMessage, }; } }
- src/tools-registry/workflow-registry.ts:254-284 (registration)MCP tool registration including name, title, description, input schema (Zod), and wrapper handler calling the core workflowEscalatePriority function.mcpServer.registerTool( 'gitea_workflow_escalate_priority', { title: '优先级升级', description: 'Automatically escalate priority for aged issues. P3→P2 after 30 days, P2→P1 after 14 days, P1→P0 after 3 days. Security issues are always P0.', inputSchema: z.object({ owner: z.string().optional().describe('Repository owner. Uses context if not provided'), repo: z.string().optional().describe('Repository name. Uses context if not provided'), dry_run: z.boolean().optional().describe('Preview changes without applying (default: false)'), }), }, async (args) => { try { const result = await WorkflowTools.workflowEscalatePriority( { client: ctx.client, contextManager: ctx.contextManager }, args ); return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }], isError: !result.success, }; } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : String(error); return { content: [{ type: 'text' as const, text: `Error: ${errorMessage}` }], isError: true, }; } } );
- Zod input schema defining parameters: owner/repo (optional), dry_run (boolean optional).inputSchema: z.object({ owner: z.string().optional().describe('Repository owner. Uses context if not provided'), repo: z.string().optional().describe('Repository name. Uses context if not provided'), dry_run: z.boolean().optional().describe('Preview changes without applying (default: false)'), }),