✨ feat(vscode): handoff 纯决策与 state/UI payload
Claude-Session: https://claude.ai/code/session_011cEyL6k351U2BzX1Qmygph
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
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<string, unknown> {
|
||||
const extra: Record<string, unknown> = { 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<string, unknown> {
|
||||
return {
|
||||
handoffAttachmentId: String(attachmentId),
|
||||
handoffFrom: from,
|
||||
assignees: [to],
|
||||
sessionId: null,
|
||||
implementSessionId: null,
|
||||
reviewSessionId: null,
|
||||
testSessionId: null,
|
||||
worktreePath: null,
|
||||
worktreeExists: false,
|
||||
}
|
||||
}
|
||||
|
||||
function definedEntries(o: Record<string, any>): Record<string, string> {
|
||||
const out: Record<string, string> = {}
|
||||
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<string, unknown> {
|
||||
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<string, unknown> {
|
||||
return {
|
||||
handoffAttachmentId: null,
|
||||
handoffFrom: null,
|
||||
...definedEntries(local.sessions),
|
||||
...definedEntries(local.profiles),
|
||||
...(local.worktreePath ? { worktreePath: local.worktreePath } : {}),
|
||||
worktreeExists: local.worktreeExists,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user