gitea_workflow_sync_status
Sync issue status labels with project board columns in Gitea repositories. Choose label-to-board, board-to-label, or bidirectional synchronization to maintain workflow consistency.
Instructions
Synchronize issue status labels with project board column positions. Supports label-to-board, board-to-label, or bidirectional sync.
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 | |
| direction | Yes | Sync direction |
Implementation Reference
- src/tools/workflow.ts:851-930 (handler)The core handler function implementing the 'gitea_workflow_sync_status' tool. Loads workflow config, initializes BoardSyncManager, fetches projects and issues, calculates sync actions based on direction (label-to-board, board-to-label, or both), and returns sync report.export async function workflowSyncStatus( ctx: WorkflowToolsContext, args: { owner?: string; repo?: string; config?: WorkflowConfig; direction: 'label-to-board' | 'board-to-label' | 'both'; } ): Promise<{ success: boolean; synced_count: number; actions: SyncAction[]; error?: string; }> { logger.debug({ args: { ...args, config: args.config ? '[provided]' : undefined } }, 'Syncing status'); const { owner, repo } = ctx.contextManager.resolveOwnerRepo(args.owner, args.repo); // 获取配置 let config = args.config; if (!config) { const loadResult = await workflowLoadConfig(ctx, { owner, repo }); if (!loadResult.success || !loadResult.config) { return { success: false, synced_count: 0, actions: [], error: loadResult.error || '无法加载配置', }; } config = loadResult.config; } const boardSyncManager = new BoardSyncManager(config); const actions: SyncAction[] = []; try { // 获取项目和列 const projects = await ctx.client.get<Array<{ id: number; title: string }>>( `/repos/${owner}/${repo}/projects` ); const workflowProject = projects.find((p) => p.title === config.board.name); if (!workflowProject) { return { success: false, synced_count: 0, actions: [], error: `未找到项目看板: ${config.board.name}`, }; } // 获取开放的 Issue const issues = await ctx.client.get<Issue[]>(`/repos/${owner}/${repo}/issues?state=open&limit=100`); // 计算同步操作(这里只返回建议的操作,实际同步需要更复杂的实现) for (const issue of issues) { const syncActions = boardSyncManager.calculateSyncActions(issue, null, args.direction); actions.push(...syncActions); } logger.info({ owner, repo, actions: actions.length }, 'Status sync calculated'); return { success: true, synced_count: actions.length, actions, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); logger.error({ owner, repo, error: errorMessage }, 'Failed to sync status'); return { success: false, synced_count: 0, actions: [], error: errorMessage, }; } }
- src/tools-registry/workflow-registry.ts:287-319 (registration)Registers the 'gitea_workflow_sync_status' tool with the MCP server, defining its title, description, input schema, and wiring the handler from WorkflowTools.mcpServer.registerTool( 'gitea_workflow_sync_status', { title: '状态双向同步', description: 'Synchronize issue status labels with project board column positions. Supports label-to-board, board-to-label, or bidirectional sync.', 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'), direction: z .enum(['label-to-board', 'board-to-label', 'both']) .describe('Sync direction'), }), }, async (args) => { try { const result = await WorkflowTools.workflowSyncStatus( { 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 validation for the tool, defining parameters: owner (optional string), repo (optional string), direction (enum: 'label-to-board', 'board-to-label', 'both').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'), direction: z .enum(['label-to-board', 'board-to-label', 'both']) .describe('Sync direction'), }),
- src/utils/board-sync.ts:78-140 (helper)Key helper method in BoardSyncManager used by the handler to compute synchronization actions between issue labels and board columns based on the specified direction.calculateSyncActions( issue: Issue, currentColumn: ProjectColumn | null, direction: SyncDirection ): SyncAction[] { const actions: SyncAction[] = []; const statusLabel = this.getStatusLabel(issue); const expectedColumn = statusLabel ? this.findColumnByLabel(statusLabel) : null; if (direction === 'label-to-board' || direction === 'both') { // 标签 → 看板:根据状态标签移动卡片 if (statusLabel && expectedColumn) { if (!currentColumn) { // Issue 不在看板上,需要创建卡片 actions.push({ type: 'create_card', issue_number: issue.number, details: { to_column: expectedColumn.name, }, reason: `根据标签 ${statusLabel} 添加到看板列 ${expectedColumn.name}`, }); } else if (currentColumn.name !== expectedColumn.name) { // 卡片在错误的列,需要移动 actions.push({ type: 'move_card', issue_number: issue.number, details: { from_column: currentColumn.name, to_column: expectedColumn.name, }, reason: `根据标签 ${statusLabel} 移动卡片`, }); } } } if (direction === 'board-to-label' || direction === 'both') { // 看板 → 标签:根据卡片位置更新标签 if (currentColumn) { const expectedLabel = this.findLabelByColumn(currentColumn.name); if (expectedLabel && expectedLabel !== statusLabel) { // 需要更新状态标签 if (statusLabel) { actions.push({ type: 'remove_label', issue_number: issue.number, details: { label: statusLabel }, reason: `移除旧状态标签 ${statusLabel}`, }); } actions.push({ type: 'add_label', issue_number: issue.number, details: { label: expectedLabel }, reason: `根据看板位置 ${currentColumn.name} 添加标签`, }); } } } return actions; }