Skip to content
This repository was archived by the owner on May 29, 2026. It is now read-only.

Commit af6b5f7

Browse files
authored
Add --issue flag to run command for GitHub issue creation (#1883)
1 parent fdd7a73 commit af6b5f7

4 files changed

Lines changed: 92 additions & 1 deletion

File tree

packages/api/src/run.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ import {
6161
genaiscriptDebug,
6262
generateId,
6363
getRunDir,
64+
githubCreateIssue,
6465
githubCreateIssueComment,
6566
githubCreatePullRequestReviews,
6667
githubParseEnv,
@@ -204,6 +205,7 @@ export async function runScriptInternal(
204205
const pullRequestComment = options.pullRequestComment;
205206
const pullRequestDescription = options.pullRequestDescription;
206207
const pullRequestReviews = options.pullRequestReviews;
208+
const issue = options.issue;
207209
const teamsMessage = options.teamsMessage;
208210
const outData = options.outData;
209211
const label = options.label;
@@ -740,6 +742,33 @@ export async function runScriptInternal(
740742
}
741743
}
742744

745+
if (issue && result.text) {
746+
dbg(`creating GitHub issue`);
747+
const ghInfo = await resolveGitHubInfo();
748+
if (
749+
ghInfo.repository &&
750+
(await confirmOrSkipInCI("Would you like to create a GitHub issue?", {
751+
preview: result.text,
752+
}))
753+
) {
754+
// Generate title (simple approach for now, can be enhanced with small model later)
755+
const firstLine = result.text.split('\n')[0]?.trim();
756+
const shortContent = firstLine ? firstLine.substring(0, 60) : '';
757+
let title = shortContent ? `${script.id}: ${shortContent}` : `Generated by ${script.id}`;
758+
759+
// TODO: Enhance with small model title generation
760+
// This could be implemented by creating a temporary script and running it with the small model,
761+
// but for now we use a simpler approach to avoid complexity.
762+
763+
await githubCreateIssue(script, ghInfo, title, result.text, {
764+
cancellationToken,
765+
stats,
766+
});
767+
} else {
768+
logError("GitHub issue creation: no repository information found");
769+
}
770+
}
771+
743772
if (result.status === "success") logInfo(`genaiscript: ${result.status}`);
744773
else if (result.status === "cancelled") logWarn(`genaiscript: ${result.status}`);
745774
else logError(`genaiscript: ${result.status}`);

packages/cli/src/cli.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ export async function cli(): Promise<void> {
214214
)
215215
.option("--out-changelog <string>", "output file for changelogs");
216216
addPullRequestOptions(run)
217+
.option("--issue", "create a GitHub issue with the generation output")
217218
.option("--teams-message", "Posts a message to the teams channel")
218219
.option("-j, --json", "emit full JSON response to output")
219220
.option(`--fail-on-errors`, `fails on detected annotation error`)

packages/core/src/githubclient.ts

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { resolveRuntimeHost } from "./host.js";
1818
import { prettifyMarkdown } from "./pretty.js";
1919
import { arrayify } from "./cleaners.js";
2020
import { assert } from "./assert.js";
21-
import { logError, logVerbose } from "./util.js";
21+
import { logError, logVerbose, logWarn } from "./util.js";
2222
import { shellRemoveAsciiColors } from "./shell.js";
2323
import { isGlobMatch } from "./glob.js";
2424
import { concurrentLimit } from "./concurrency.js";
@@ -507,6 +507,66 @@ export async function githubCreateIssueComment(
507507
return r;
508508
}
509509

510+
// Function to generate a title for a GitHub issue
511+
async function generateIssueTitle(
512+
script: PromptScript,
513+
body: string,
514+
): Promise<string> {
515+
// For now, create a simple title based on the script and first line of body
516+
const firstLine = body.split('\n')[0]?.trim();
517+
const shortContent = firstLine ? firstLine.substring(0, 60) : '';
518+
519+
if (shortContent) {
520+
return `${script.id}: ${shortContent}`;
521+
}
522+
523+
return `Generated by ${script.id}`;
524+
}
525+
526+
// https://docs.github.com/en/rest/issues/issues?apiVersion=2022-11-28#create-an-issue
527+
export async function githubCreateIssue(
528+
script: PromptScript,
529+
info: GithubConnectionInfo,
530+
title: string,
531+
body: string,
532+
options?: CancellationOptions & { stats?: GenerationStats },
533+
): Promise<{ created: boolean; statusText: string; html_url?: string; issue_number?: number }> {
534+
const { cancellationToken, stats } = options ?? {};
535+
const { repository, token } = info;
536+
537+
if (!repository) {
538+
dbg(`missing repository, cannot create issue`);
539+
return { created: false, statusText: "missing repository" };
540+
}
541+
if (!token) {
542+
dbg(`missing github token, cannot create issue`);
543+
return { created: false, statusText: "missing github token" };
544+
}
545+
546+
try {
547+
// Create the GitHub client and create the issue
548+
const client = new GitHubClient(info);
549+
const issue = await client.createIssue(title, prettifyMarkdown(dedent(body)));
550+
551+
const r = {
552+
created: true,
553+
statusText: "Created",
554+
html_url: issue.html_url,
555+
issue_number: issue.number,
556+
};
557+
558+
logVerbose(`GitHub issue created at ${r.html_url}`);
559+
return r;
560+
} catch (error) {
561+
const errorMsg = errorMessage(error);
562+
logError(`GitHub issue creation failed: ${errorMsg}`);
563+
return {
564+
created: false,
565+
statusText: errorMsg,
566+
};
567+
}
568+
}
569+
510570
async function githubCreatePullRequestReview(
511571
script: PromptScript,
512572
info: Pick<

packages/core/src/server/messages.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ export interface PromptScriptRunOptions {
165165
pullRequestComment: string | boolean;
166166
pullRequestDescription: string | boolean;
167167
pullRequestReviews: boolean;
168+
issue: boolean;
168169
teamsMessage: boolean;
169170
outData: string;
170171
label: string;

0 commit comments

Comments
 (0)