feat(cli): add pull request creation with AI-generated messages (#2)
Add a new `gai pr` subcommand that generates pull request titles and descriptions using AI, then creates the PR via GitHub CLI (`gh`) or Gitea CLI (`tea`). This extends the existing commit-generation system by reusing retry logic and prompt infrastructure, and introduces a `callAI` function that returns raw output (instead of pre-cleaned messages) to support structured PR responses. Reviewed-on: #2
This commit was merged in pull request #2.
This commit is contained in:
@@ -39,11 +39,10 @@ async function sleep(ms: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
export async function generateCommitMessage(
|
||||
export async function callAI(
|
||||
config: Config,
|
||||
systemPrompt: string,
|
||||
userPrompt: string,
|
||||
retries = MAX_RETRIES,
|
||||
): Promise<string> {
|
||||
const url = `${config.apiBase.replace(/\/$/, "")}/chat/completions`;
|
||||
|
||||
@@ -52,7 +51,7 @@ export async function generateCommitMessage(
|
||||
{ role: "user", content: userPrompt },
|
||||
];
|
||||
|
||||
for (let attempt = 1; attempt <= retries; attempt++) {
|
||||
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
@@ -70,7 +69,7 @@ export async function generateCommitMessage(
|
||||
|
||||
if (!response.ok) {
|
||||
const text = await response.text();
|
||||
if (response.status === 429 && attempt < retries) {
|
||||
if (response.status === 429 && attempt < MAX_RETRIES) {
|
||||
await sleep(RETRY_DELAY * attempt);
|
||||
continue;
|
||||
}
|
||||
@@ -89,7 +88,7 @@ export async function generateCommitMessage(
|
||||
const finishReason = data.choices?.[0]?.finish_reason;
|
||||
|
||||
if (raw && raw.trim()) {
|
||||
return cleanMessage(raw);
|
||||
return raw;
|
||||
}
|
||||
|
||||
if (finishReason === "length") {
|
||||
@@ -102,22 +101,53 @@ export async function generateCommitMessage(
|
||||
throw new Error("Response blocked by content filter.");
|
||||
}
|
||||
|
||||
if (attempt < retries) {
|
||||
if (attempt < MAX_RETRIES) {
|
||||
await sleep(RETRY_DELAY * attempt);
|
||||
continue;
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Empty response from AI after ${retries} attempts. finish_reason: ${finishReason ?? "unknown"}`,
|
||||
`Empty response from AI after ${MAX_RETRIES} attempts. finish_reason: ${finishReason ?? "unknown"}`,
|
||||
);
|
||||
} catch (err) {
|
||||
if (attempt >= retries) throw err;
|
||||
if (attempt >= MAX_RETRIES) throw err;
|
||||
if (err instanceof Error && err.message.startsWith("API error")) throw err;
|
||||
if (err instanceof Error && err.message.includes("max_tokens")) throw err;
|
||||
if (err instanceof Error && err.message.includes("content filter")) throw err;
|
||||
if (err instanceof Error && err.message.includes("content filter"))
|
||||
throw err;
|
||||
await sleep(RETRY_DELAY * attempt);
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error("Failed to generate commit message");
|
||||
throw new Error("Failed to generate response");
|
||||
}
|
||||
|
||||
export async function generateCommitMessage(
|
||||
config: Config,
|
||||
systemPrompt: string,
|
||||
userPrompt: string,
|
||||
): Promise<string> {
|
||||
const raw = await callAI(config, systemPrompt, userPrompt);
|
||||
return cleanMessage(raw);
|
||||
}
|
||||
|
||||
export async function generatePRMessage(
|
||||
config: Config,
|
||||
systemPrompt: string,
|
||||
userPrompt: string,
|
||||
): Promise<{ title: string; body: string }> {
|
||||
const raw = await callAI(config, systemPrompt, userPrompt);
|
||||
const cleaned = cleanMessage(raw);
|
||||
|
||||
const lines = cleaned.split("\n");
|
||||
const title = lines[0]?.trim() || "Update";
|
||||
let bodyStart = 1;
|
||||
|
||||
while (bodyStart < lines.length && lines[bodyStart]?.trim() === "") {
|
||||
bodyStart++;
|
||||
}
|
||||
|
||||
const body = lines.slice(bodyStart).join("\n").trim();
|
||||
|
||||
return { title, body };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user