diff --git a/github/check/action.yml b/github/check/action.yml
new file mode 100644
index 000000000..732d67b3a
--- /dev/null
+++ b/github/check/action.yml
@@ -0,0 +1,48 @@
+name: "opencode GitHub Run"
+description: "Run opencode in GitHub Actions workflows"
+branding:
+ icon: "code"
+ color: "orange"
+
+inputs:
+ model:
+ description: "The model to use with opencode. Takes the format of `provider/model`."
+ required: true
+
+ prompt:
+ description: "The prompt to use with opencode."
+ required: true
+
+ share:
+ description: "Whether to share the opencode session. Defaults to true for public repositories."
+ required: false
+
+ token:
+ description: "Optional GitHub access token for performing operations such as creating comments, committing changes, and opening pull requests. Defaults to the installation access token from the opencode GitHub App."
+ required: false
+
+runs:
+ using: "composite"
+ steps:
+ - name: Install opencode
+ shell: bash
+ run: curl -fsSL https://opencode.ai/install | bash
+
+ - name: Install bun
+ shell: bash
+ run: npm install -g bun
+
+ - name: Install dependencies
+ shell: bash
+ run: |
+ cd ${GITHUB_ACTION_PATH}
+ bun install
+
+ - name: Run opencode
+ shell: bash
+ run: bun ${GITHUB_ACTION_PATH}/index.ts
+ env:
+ MODEL: ${{ inputs.model }}
+ PROMPT: ${{ inputs.prompt }}
+ SHARE: ${{ inputs.share }}
+ TOKEN: ${{ inputs.token }}
diff --git a/github/check/index.ts b/github/check/index.ts
new file mode 100644
index 000000000..587603ba7
--- /dev/null
+++ b/github/check/index.ts
@@ -0,0 +1,61 @@
+import { $ } from "bun"
+import * as core from "@actions/core"
+import { Auth } from "../src/auth"
+import { Git } from "../src/git"
+import { Opencode } from "../src/opencode"
+import { Context } from "../src/context"
+
+try {
+ Context.assertEventName("pull_request_opened", "pull_request_synchronize")
+ await check()
+ process.exit(0)
+} catch (e: any) {
+ console.error(e)
+ let msg = e
+ if (e instanceof $.ShellError) msg = e.stderr.toString()
+ else if (e instanceof Error) msg = e.message
+ core.setFailed(msg)
+ // Also output the clean error message for the action to capture
+ //core.setOutput("prepare_error", e.message);
+ process.exit(1)
+}
+
+export async function check() {
+ try {
+ await Git.configure()
+ await Opencode.start()
+
+ const filename = "check-failed-reason.json"
+ const pr = Context.payloadPullRequest()
+ await Opencode.chat(`
+A pull request has been created or updated: '${pr.title}'
+
+
+${pr.number}
+
+
+
+${pr.body}
+
+
+Please check:
+
+${process.env.PROMPT}
+
+
+If the check failed, write the reason to ${filename}. Keep the reason short and concise, only one sentence.
+
+If the check passed, do not create any file.
+ `)
+
+ // check file exists
+ try {
+ const reason = await Bun.file(filename).text()
+ if (reason) throw new Error(reason)
+ } catch (e) {}
+ } finally {
+ Opencode.closeServer()
+ await Auth.revoke()
+ await Git.restore()
+ }
+}