diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..59ab3a8 --- /dev/null +++ b/LICENCE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Nomyo AI + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 0d4d412..0b546be 100644 --- a/README.md +++ b/README.md @@ -1,190 +1,202 @@ # opencode Forgejo Action -A Forgejo Action that integrates [opencode](https://opencode.ai) directly into your Forgejo Actions workflow. +A Forgejo Action that runs [opencode](https://opencode.ai) against a Nomyo (or any OpenAI-compatible) backend, triggered by comments on issues and merge requests. -Mention `/opencode` or `/oc` in your comment, and opencode will execute tasks within your Forgejo Actions runner. +Mention `/opencode` or `/oc` in a comment and opencode reads the thread, executes the requested task, and replies. For "fix" tasks it opens a merge request; for MR comments it commits to the same MR. ## Features ### Explain an issue -Leave the following comment on a Forgejo issue. `opencode` will read the entire thread, including all comments, and reply with a clear explanation. - ``` /opencode explain this issue ``` -### Fix an issue +opencode reads the full thread and replies with an explanation. -Leave the following comment on a Forgejo issue. opencode will create a new branch, implement the changes, and open a merge request with the changes. +### Fix an issue ``` /opencode fix this ``` -### Review MRs and make changes +opencode creates a branch, implements the change, and opens a merge request. -Leave the following comment on a Forgejo MR. opencode will implement the requested change and commit it to the same MR. +### Update an open MR ``` Delete the attachment from S3 when the note is removed /oc ``` +opencode commits to the same MR branch. + ### Review specific code lines -Leave a comment directly on code lines in the MR's "Files" tab. opencode will automatically detect the file, line numbers, and diff context to provide precise responses. +Comment directly on lines in the MR's "Files" tab — opencode receives the file path, line numbers, and diff hunk as context. ``` -[Comment on specific lines in Files tab] /oc add error handling here ``` -When commenting on specific lines, opencode receives: - -- The exact file being reviewed -- The specific lines of code -- The surrounding diff context -- Line number information - ## Installation -### Quick setup +### Prerequisites -Add the following workflow file to `.forgejo/workflows/opencode.yml` in your repo. +- A Forgejo instance with Actions enabled. +- A runner that can run Docker containers (the example below uses `docker-amd64` with `node:lts-bookworm`). +- A Forgejo PAT (see scopes below) and a Nomyo API key. -### Manual setup +### Forgejo PAT scopes -1. **Create a PAT** +Generate the token at *Settings → Applications → Manage Access Tokens* with these scopes: - Go to your Forgejo instance → Settings → Applications → Generate Token. Give it `repo` scope (contents, pull-requests, issues). +| Scope | Used for | +|---|---| +| `read:repository` | Clone, fetch repo info | +| `write:repository` | Push commits and branches | +| `read:issue` | Read issue/MR comments and metadata | +| `write:issue` | Create + update comments, open MRs | +| `read:user` | Resolve actor info (optional) | -2. **Add the PAT as a secret** +### Secrets - Go to your repository → Settings → Actions → Secrets → New Repository Secret. Add: +In *Repo Settings → Actions → Secrets* add: - | Secret name | Value | - |---|---| - | `FORGEJO_TOKEN` | Your PAT | - | `NOMYO_API_KEY` | Your Nomyo API key | +| Secret | Value | +|---|---| +| `FORGEJO_TOKEN` | The PAT from above | +| `NOMYO_API_KEY` | Your Nomyo API key | -3. **Add the workflow file** +### Workflow file - Create `.forgejo/workflows/opencode.yml`: +Add `.forgejo/workflows/opencode.yml` to the repo where you want opencode to respond. This example targets a Forgejo instance hosted under a subpath (`bitfreedom.net/code/`); adjust URLs and `runs-on` to match your setup. - ```yaml - name: opencode - on: - issue_comment: - types: [created] - pull_request_review_comment: - types: [created] +```yaml +name: opencode +on: + issue_comment: + types: [created] + pull_request_review_comment: + types: [created] - jobs: - opencode: - if: | - contains(github.event.comment.body, '/oc') || - contains(github.event.comment.body, '/opencode') - runs-on: ubuntu-latest - permissions: - id-token: write - contents: write - pull-requests: write - issues: write - steps: - - uses: actions/checkout@v6 - with: - fetch-depth: 1 - persist-credentials: false +jobs: + opencode: + if: | + contains(github.event.comment.body, '/oc') || + contains(github.event.comment.body, '/opencode') + runs-on: docker-amd64 + container: + image: node:lts-bookworm + permissions: + id-token: write + contents: write + pull-requests: write + issues: write + steps: + - name: Install git, curl and Docker + run: | + apt-get update -qq + apt-get install -y -qq git curl unzip docker.io - - name: Run opencode - uses: bitfreedom.net/code/nomyo-ai/actions@v1 - env: - NOMYO_API_KEY: ${{ secrets.NOMYO_API_KEY }} - with: - model: anthropic/claude-sonnet-4-20250514 - forgejo_api_url: bitfreedom.net/code/ - forgejo_token: ${{ secrets.FORGEJO_TOKEN }} - ``` + - name: Start Docker daemon + run: | + dockerd --host=unix:///var/run/docker.sock --iptables=false --dns=8.8.8.8 --dns=8.8.4.4 > /tmp/dockerd.log 2>&1 & + for i in $(seq 1 30); do + sleep 2 + docker info > /dev/null 2>&1 && echo "Docker daemon ready" && exit 0 + echo "Waiting for Docker daemon... ($i/30)" + done + cat /tmp/dockerd.log + exit 1 -4. **Set the model** + - name: Checkout repository + run: | + git clone --depth=1 --branch "${{ github.ref_name }}" \ + "https://oauth2:${{ github.token }}@bitfreedom.net/code/${{ github.repository }}.git" \ + . - Replace `anthropic/claude-sonnet-4-20250514` with your preferred model format: `provider/model`. + - name: Fetch action source + run: | + git clone --depth=1 --branch v1 \ + "https://oauth2:${{ github.token }}@bitfreedom.net/code/nomyo-ai/actions.git" \ + ./.opencode-action + + - name: Run opencode + uses: ./.opencode-action + with: + nomyo_api_key: ${{ secrets.NOMYO_API_KEY }} + model: nomyo/unsloth/Qwen3.6-35B-A3B-GGUF:UD-Q4_K_M + forgejo_api_url: https://bitfreedom.net/code/ + forgejo_token: ${{ secrets.FORGEJO_TOKEN }} +``` + +### Why the manual clone? + +`act_runner`'s `uses:` parser expects `https:////` — it does not handle a Forgejo instance hosted under a subpath (e.g. `bitfreedom.net/code/`). For subpath instances, clone the action source into `./.opencode-action` and reference it as a local action (`uses: ./.opencode-action`). If your Forgejo lives at a domain root, you can replace those two steps with `uses: /nomyo-ai/actions@v1` directly. ## Configuration -| Input | Required | Description | -|---|---|---| -| `model` | Yes | Model to use (format: `provider/model`) | -| `forgejo_api_url` | No | Forgejo instance API URL (e.g., `https://git.example.com`). Defaults to `https://git.bitfreedom.at` | -| `forgejo_token` | No | Forgejo PAT with repo scope | -| `agent` | No | Agent to use (must be a primary agent) | -| `share` | No | Share the opencode session (defaults to true for public repos) | -| `prompt` | No | Custom prompt to override default behavior | -| `mentions` | No | Comma-separated trigger phrases. Defaults to `/opencode,/oc` | -| `variant` | No | Model variant (e.g., `high`, `max`, `minimal`) | +| Input | Required | Default | Description | +|---|---|---|---| +| `model` | Yes | — | Model in the form `provider/model`. The provider name becomes a custom opencode provider; the action registers it as an `@ai-sdk/openai-compatible` provider pointing at `nomyo_api_url`. | +| `nomyo_api_key` | Yes | — | API key for the OpenAI-compatible backend. | +| `nomyo_api_url` | No | `https://chat.nomyo.ai/api` | Base URL of the OpenAI-compatible endpoint. The adapter calls `${baseURL}/chat/completions`. | +| `forgejo_api_url` | No | `https://bitfreedom.net/code/` | Forgejo instance base URL. | +| `forgejo_token` | No | — | Forgejo PAT (see scopes above). | +| `agent` | No | — | Primary agent name to use. | +| `share` | No | auto | Share the opencode session (`true`/`false`). Defaults to `true` for public repos. | +| `prompt` | No | — | Custom prompt override. | +| `mentions` | No | `/opencode,/oc` | Comma-separated trigger phrases. | +| `variant` | No | — | Provider-specific reasoning effort (`high`, `max`, `minimal`, …). | ## Supported Events -| Event Type | Triggered By | Details | +| Event | Triggered by | Notes | |---|---|---| -| `issue_comment` | Comment on an issue or MR | Mention `/opencode` or `/oc` in your comment | -| `pull_request_review_comment` | Comment on specific PR/MR code lines | Mention `/opencode` or `/oc` when reviewing code | +| `issue_comment` | Comment on an issue or MR | Body must contain a mention phrase | +| `pull_request_review_comment` | Comment on a specific line in an MR's Files tab | Receives file path, line number, and diff hunk | -## Examples +## Architecture -### Explain an issue +This action is a **composite action**. On each run it: -``` -/opencode explain this issue -``` +1. Installs Bun and the opencode CLI. +2. Runs [`bun install`](package.json) inside the action source. +3. Registers `${MODEL%/*}` as a custom opencode provider via `OPENCODE_CONFIG_CONTENT` and the API key via `OPENCODE_AUTH_CONTENT`. +4. Spawns `opencode serve` locally and talks to it over HTTP (`/session/{id}/message`). +5. Resolves the event, builds a prompt with thread/diff context, sends it to opencode, and posts the response as a comment (and, for "fix" intents, opens an MR). -### Fix an issue - -``` -/opencode fix this -``` - -### Review MRs - -``` -/opencode review this merge request -``` +There is no compiled `dist/` artifact — `index.ts` is executed directly by Bun. ## Development -To test locally: - -1. Set up environment: +Local test loop (requires Bun and `opencode` on PATH): ```bash -export FORGEJO_API_URL="https://git.your-instance.com" -export FORGEJO_TOKEN="your-pat-here" -export MODEL="anthropic/claude-sonnet-4-20250514" -export GITHUB_RUN_ID="test" +export NOMYO_API_KEY="..." +export NOMYO_API_URL="https://chat.nomyo.ai/api" +export FORGEJO_API_URL="https://bitfreedom.net/code/" +export FORGEJO_TOKEN="..." +export MODEL="nomyo/unsloth/Qwen3.6-35B-A3B-GGUF:UD-Q4_K_M" +export GITHUB_RUN_ID="test-run" +export GITHUB_RUN_NUMBER="1" +export GITHUB_SERVER_URL="https://bitfreedom.net/code" export GITHUB_REPOSITORY="owner/repo" +export GITHUB_REF_NAME="main" export GITHUB_ACTOR="testuser" export GITHUB_EVENT_NAME="issue_comment" export GITHUB_EVENT_PATH="/tmp/event.json" -``` -2. Create a mock event payload in `/tmp/event.json`: - -```json +cat > /tmp/event.json <<'EOF' { - "eventName": "issue_comment", - "repo": { "owner": "owner", "repo": "repo" }, - "actor": "testuser", - "payload": { - "issue": { "number": 1, "index": 1, "title": "Test issue", "body": "Test body" }, - "comment": { "id": 1, "body": "/opencode fix this" } - } + "repository": { "owner": { "login": "owner" }, "name": "repo", "full_name": "owner/repo" }, + "issue": { "number": 1, "index": 1, "title": "Test", "body": "Test", "user": { "login": "testuser" } }, + "comment": { "id": 1, "body": "/oc explain this", "user": { "login": "testuser" } } } -``` +EOF -3. Run: - -```bash -bun index.ts +bun install +bun run index.ts ``` ## License