Add mobile Tasks parity (#2452)

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
Jinwoo Hong
2026-05-21 20:26:07 -07:00
committed by GitHub
co-authored by Orca
parent 3efc669bd4
commit b1973657ea
123 changed files with 26492 additions and 1461 deletions
+5
View File
@@ -21,6 +21,11 @@ vi.mock('./gh-utils', () => ({
getOwnerRepo: getOwnerRepoMock,
getIssueOwnerRepo: vi.fn(),
getOwnerRepoForRemote: vi.fn(),
githubRepoContext: vi.fn((repoPath: string, connectionId?: string | null) => ({
repoPath,
connectionId: connectionId ?? null
})),
ghRepoExecOptions: vi.fn((context: { repoPath: string }) => ({ cwd: context.repoPath })),
gitExecFileAsync: vi.fn(),
extractExecError: extractExecErrorMock,
parseGitHubOwnerRepo: vi.fn(),
+68 -8
View File
@@ -1362,7 +1362,9 @@ async function findOpenPRByHeadBase(args: {
ownerRepo: OwnerRepo
head: string
base: string
connectionId?: string | null
}): Promise<{ number: number; url: string } | null> {
const context = githubRepoContext(args.repoPath, args.connectionId)
const { stdout } = await ghExecFileAsync(
[
'pr',
@@ -1380,7 +1382,7 @@ async function findOpenPRByHeadBase(args: {
'--json',
'number,url'
],
{ cwd: args.repoPath }
ghRepoExecOptions(context)
)
const list = JSON.parse(stdout) as { number?: number; url?: string }[]
if (list.length !== 1 || !list[0]?.number || !list[0]?.url) {
@@ -1391,7 +1393,8 @@ async function findOpenPRByHeadBase(args: {
export async function createGitHubPullRequest(
repoPath: string,
input: CreateHostedReviewInput
input: CreateHostedReviewInput,
connectionId?: string | null
): Promise<CreateHostedReviewResult> {
if (input.provider !== 'github') {
return {
@@ -1401,7 +1404,7 @@ export async function createGitHubPullRequest(
}
}
const ownerRepo = await getOwnerRepo(repoPath)
const ownerRepo = await getOwnerRepo(repoPath, connectionId)
if (!ownerRepo) {
return {
ok: false,
@@ -1452,8 +1455,9 @@ export async function createGitHubPullRequest(
createArgs.push('--draft')
}
try {
const context = githubRepoContext(repoPath, connectionId)
const { stdout } = await ghExecFileAsync(createArgs, {
cwd: repoPath,
...ghRepoExecOptions(context),
timeout: 60_000,
idempotent: false
})
@@ -1462,7 +1466,9 @@ export async function createGitHubPullRequest(
return { ok: true, ...created }
}
const found = head
? await findOpenPRByHeadBase({ repoPath, ownerRepo, head, base }).catch(() => null)
? await findOpenPRByHeadBase({ repoPath, ownerRepo, head, base, connectionId }).catch(
() => null
)
: null
if (found) {
return { ok: true, ...found }
@@ -1479,9 +1485,13 @@ export async function createGitHubPullRequest(
(classified.code === 'already_exists' || classified.code === 'unknown_completion') &&
head
) {
const existing = await findOpenPRByHeadBase({ repoPath, ownerRepo, head, base }).catch(
() => null
)
const existing = await findOpenPRByHeadBase({
repoPath,
ownerRepo,
head,
base,
connectionId
}).catch(() => null)
if (existing) {
return {
ok: false,
@@ -3006,3 +3016,53 @@ export async function updatePRTitle(
release()
}
}
export async function updatePRDetails(
repoPath: string,
prNumber: number,
updates: { title?: string; body?: string },
connectionId?: string | null,
prRepo?: OwnerRepo | null
): Promise<{ ok: true } | { ok: false; error: string }> {
const ghOptions = ghRepoExecOptions(githubRepoContext(repoPath, connectionId))
const ownerRepo = prRepo ?? (await getOwnerRepo(repoPath, connectionId))
if (!ownerRepo) {
return { ok: false, error: 'Could not resolve GitHub owner/repo for this repository' }
}
const fields: string[] = []
if (updates.title !== undefined) {
const title = updates.title.trim()
if (!title) {
return { ok: false, error: 'Title is required' }
}
fields.push(`title=${title}`)
}
if (updates.body !== undefined) {
fields.push(`body=${updates.body}`)
}
if (fields.length === 0) {
return { ok: true }
}
await acquire()
try {
await ghExecFileAsync(
[
'api',
'-X',
'PATCH',
`repos/${ownerRepo.owner}/${ownerRepo.repo}/pulls/${prNumber}`,
...fields.flatMap((field) => ['--raw-field', field])
],
ghOptions
)
return { ok: true }
} catch (err) {
const message =
err instanceof Error ? err.message : typeof err === 'string' ? err : 'Unknown error'
return { ok: false, error: classifyGhError(message).message }
} finally {
release()
}
}
+1 -1
View File
@@ -839,7 +839,7 @@ export async function getWorkItemDetailsBySlug(
// Why: PR files/checks/review-thread tabs depend on a local repo path and
// are out of Project-mode slug scope for v1. Omit them here; the dialog
// branches on their absence and hides those tabs.
...(args.type === 'issue' ? { assignees } : {})
assignees
}
return { ok: true, details }
}