Files
automaker/apps/server/src/routes/github/routes/list-prs.ts
DhanushSantosh 253ab94646 feat(github): add Codex/OpenCode model support for issue validation
- Support Codex and OpenCode models in issue validation
- Add reasoningEffort parameter for Codex model configuration
- Update validation logic to use structured output for Claude/Codex
- Update UI hooks and types for multi-provider model selection
2026-01-14 00:50:02 +05:30

124 lines
3.2 KiB
TypeScript

/**
* POST /list-prs endpoint - List GitHub pull requests for a project
*/
import type { Request, Response } from 'express';
import { execAsync, execEnv, getErrorMessage, logError } from './common.js';
import { checkGitHubRemote } from './check-github-remote.js';
const OPEN_PRS_LIMIT = 100;
const MERGED_PRS_LIMIT = 50;
const PR_LIST_FIELDS =
'number,title,state,author,createdAt,labels,url,isDraft,headRefName,reviewDecision,mergeable,body';
const PR_STATE_OPEN = 'open';
const PR_STATE_MERGED = 'merged';
const GH_PR_LIST_COMMAND = 'gh pr list';
const GH_STATE_FLAG = '--state';
const GH_JSON_FLAG = '--json';
const GH_LIMIT_FLAG = '--limit';
export interface GitHubLabel {
name: string;
color: string;
}
export interface GitHubAuthor {
login: string;
}
export interface GitHubPR {
number: number;
title: string;
state: string;
author: GitHubAuthor;
createdAt: string;
labels: GitHubLabel[];
url: string;
isDraft: boolean;
headRefName: string;
reviewDecision: string | null;
mergeable: string;
body: string;
}
export interface ListPRsResult {
success: boolean;
openPRs?: GitHubPR[];
mergedPRs?: GitHubPR[];
error?: string;
}
export function createListPRsHandler() {
return async (req: Request, res: Response): Promise<void> => {
try {
const { projectPath } = req.body;
if (!projectPath) {
res.status(400).json({ success: false, error: 'projectPath is required' });
return;
}
// First check if this is a GitHub repo
const remoteStatus = await checkGitHubRemote(projectPath);
if (!remoteStatus.hasGitHubRemote) {
res.status(400).json({
success: false,
error: 'Project does not have a GitHub remote',
});
return;
}
const repoQualifier =
remoteStatus.owner && remoteStatus.repo ? `${remoteStatus.owner}/${remoteStatus.repo}` : '';
const repoFlag = repoQualifier ? `-R ${repoQualifier}` : '';
const [openResult, mergedResult] = await Promise.all([
execAsync(
[
GH_PR_LIST_COMMAND,
repoFlag,
`${GH_STATE_FLAG} ${PR_STATE_OPEN}`,
`${GH_JSON_FLAG} ${PR_LIST_FIELDS}`,
`${GH_LIMIT_FLAG} ${OPEN_PRS_LIMIT}`,
]
.filter(Boolean)
.join(' '),
{
cwd: projectPath,
env: execEnv,
}
),
execAsync(
[
GH_PR_LIST_COMMAND,
repoFlag,
`${GH_STATE_FLAG} ${PR_STATE_MERGED}`,
`${GH_JSON_FLAG} ${PR_LIST_FIELDS}`,
`${GH_LIMIT_FLAG} ${MERGED_PRS_LIMIT}`,
]
.filter(Boolean)
.join(' '),
{
cwd: projectPath,
env: execEnv,
}
),
]);
const { stdout: openStdout } = openResult;
const { stdout: mergedStdout } = mergedResult;
const openPRs: GitHubPR[] = JSON.parse(openStdout || '[]');
const mergedPRs: GitHubPR[] = JSON.parse(mergedStdout || '[]');
res.json({
success: true,
openPRs,
mergedPRs,
});
} catch (error) {
logError(error, 'List GitHub PRs failed');
res.status(500).json({ success: false, error: getErrorMessage(error) });
}
};
}