import type { HandoffProfiles, HandoffSessions } from '../../cc/handoffManifest' import type { IssueColumn } from '../../gitea/types' import { LOCAL_STATE_FIELDS } from '../../issues/localState' export function canStartHandoff(issue: { source?: 'gitea' | 'youtrack' column: IssueColumn handoffAttachmentId?: string }): boolean { return issue.source !== 'youtrack' && issue.column !== 'done' && !issue.handoffAttachmentId } export function canAcceptHandoff(issue: { source?: 'gitea' | 'youtrack' handoffAttachmentId?: string assignees?: string[] }, me: string | undefined): boolean { return issue.source !== 'youtrack' && !!issue.handoffAttachmentId && me !== undefined && (issue.assignees ?? []).includes(me) } /** 移交前 worktree 必须「干净 + 全部 push」,否则接管方从远端重建会丢工作。 */ export function checkWorktreePushed(input: { statusPorcelain: string localHead: string remoteHead: string }): { ok: true } | { ok: false, reason: string } { if (input.statusPorcelain.trim().length > 0) return { ok: false, reason: '工作区有未提交改动,请先提交并 push' } if (input.localHead.trim() !== input.remoteHead.trim()) return { ok: false, reason: '本地分支与远端不一致,请先 push' } return { ok: true } } export function handoffStartedStateExtra(attachmentId: number, from: string): Record { const extra: Record = { handoffAttachmentId: String(attachmentId), handoffFrom: from } for (const f of LOCAL_STATE_FIELDS) extra[f] = '' return extra } export function handoffStartedUiPatch(attachmentId: number, from: string, to: string): Record { return { handoffAttachmentId: String(attachmentId), handoffFrom: from, assignees: [to], sessionId: null, implementSessionId: null, reviewSessionId: null, testSessionId: null, worktreePath: null, worktreeExists: false, } } function definedEntries(o: Record): Record { const out: Record = {} for (const [k, v] of Object.entries(o)) { if (v) out[k] = v } return out } export function handoffAcceptedStateExtra(local: { sessions: HandoffSessions profiles: HandoffProfiles worktreePath?: string }): Record { return { handoffAttachmentId: '', handoffFrom: '', ...definedEntries(local.sessions), ...definedEntries(local.profiles), ...(local.worktreePath ? { worktreePath: local.worktreePath } : {}), } } export function handoffAcceptedUiPatch(local: { sessions: HandoffSessions profiles: HandoffProfiles worktreePath?: string worktreeExists: boolean }): Record { return { handoffAttachmentId: null, handoffFrom: null, ...definedEntries(local.sessions), ...definedEntries(local.profiles), ...(local.worktreePath ? { worktreePath: local.worktreePath } : {}), worktreeExists: local.worktreeExists, } }