This is a first cut at a GitHub Action that lets you define prompt
templates in `.md` files under `.github/codex/labels` that will run
Codex with the associated prompt when the label is added to a GitHub
pull request.
For example, this PR includes these files:
```
.github/codex/labels/codex-attempt.md
.github/codex/labels/codex-code-review.md
.github/codex/labels/codex-investigate-issue.md
```
And the new `.github/workflows/codex.yml` workflow declares the
following triggers:
```yaml
on:
issues:
types: [opened, labeled]
pull_request:
branches: [main]
types: [labeled]
```
as well as the following expression to gate the action:
```
jobs:
codex:
if: |
(github.event_name == 'issues' && (
(github.event.action == 'labeled' && (github.event.label.name == 'codex-attempt' || github.event.label.name == 'codex-investigate-issue'))
)) ||
(github.event_name == 'pull_request' && github.event.action == 'labeled' && github.event.label.name == 'codex-code-review')
```
Note the "actor" who added the label must have write access to the repo
for the action to take effect.
After adding a label, the action will "ack" the request by replacing the
original label (e.g., `codex-review`) with an `-in-progress` suffix
(e.g., `codex-review-in-progress`). When it is finished, it will swap
the `-in-progress` label with a `-completed` one (e.g.,
`codex-review-completed`).
Users of the action are responsible for providing an `OPENAI_API_KEY`
and making it available as a secret to the action.
54 lines
1.8 KiB
TypeScript
54 lines
1.8 KiB
TypeScript
import type { EnvContext } from "./env-context";
|
|
import { runCodex } from "./run-codex";
|
|
import { postComment } from "./post-comment";
|
|
import { addEyesReaction } from "./add-reaction";
|
|
|
|
/**
|
|
* Handle `issue_comment` and `pull_request_review_comment` events once we know
|
|
* the action is supported.
|
|
*/
|
|
export async function onComment(ctx: EnvContext): Promise<void> {
|
|
const triggerPhrase = ctx.tryGet("INPUT_TRIGGER_PHRASE");
|
|
if (!triggerPhrase) {
|
|
console.warn("Empty trigger phrase: skipping.");
|
|
return;
|
|
}
|
|
|
|
// Attempt to get the body of the comment from the environment. Depending on
|
|
// the event type either `GITHUB_EVENT_COMMENT_BODY` (issue & PR comments) or
|
|
// `GITHUB_EVENT_REVIEW_BODY` (PR reviews) is set.
|
|
const commentBody =
|
|
ctx.tryGetNonEmpty("GITHUB_EVENT_COMMENT_BODY") ??
|
|
ctx.tryGetNonEmpty("GITHUB_EVENT_REVIEW_BODY") ??
|
|
ctx.tryGetNonEmpty("GITHUB_EVENT_ISSUE_BODY");
|
|
|
|
if (!commentBody) {
|
|
console.warn("Comment body not found in environment: skipping.");
|
|
return;
|
|
}
|
|
|
|
// Check if the trigger phrase is present.
|
|
if (!commentBody.includes(triggerPhrase)) {
|
|
console.log(
|
|
`Trigger phrase '${triggerPhrase}' not found: nothing to do for this comment.`,
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Derive the prompt by removing the trigger phrase. Remove only the first
|
|
// occurrence to keep any additional occurrences that might be meaningful.
|
|
const prompt = commentBody.replace(triggerPhrase, "").trim();
|
|
|
|
if (prompt.length === 0) {
|
|
console.warn("Prompt is empty after removing trigger phrase: skipping");
|
|
return;
|
|
}
|
|
|
|
// Provide immediate feedback that we are working on the request.
|
|
await addEyesReaction(ctx);
|
|
|
|
// Run Codex and post the response as a new comment.
|
|
const lastMessage = await runCodex(prompt, ctx);
|
|
await postComment(lastMessage, ctx);
|
|
}
|