From 9367c99c513a94a4ec65a1f40925e016a91515a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaspur=20H=C3=B8jgaard?= Date: Wed, 11 Feb 2026 12:20:29 +0000 Subject: [PATCH 1/3] feat: add opencode-provider input for OpenCode integration and update related scripts --- .github/workflows/comment-revalidation.yml | 8 +++++++- action.yml | 12 +++++++++--- scripts/setup-mcp.sh | 7 ++++--- scripts/validate.sh | 6 ++++-- 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/.github/workflows/comment-revalidation.yml b/.github/workflows/comment-revalidation.yml index fe65b1d..db10a00 100644 --- a/.github/workflows/comment-revalidation.yml +++ b/.github/workflows/comment-revalidation.yml @@ -35,8 +35,13 @@ on: required: false type: string default: 'gemini-2.5-flash' + opencode-provider: + description: 'AI provider for OpenCode (e.g., openrouter, anthropic, openai)' + required: false + type: string + default: 'openrouter' openrouter-model: - description: 'OpenRouter model ID (when provider is opencode)' + description: 'Model ID (when provider is opencode)' required: false type: string default: 'moonshotai/kimi-k2.5' @@ -288,6 +293,7 @@ jobs: base-ref: ${{ steps.base-ref.outputs.base-ref }} provider: ${{ inputs.provider || 'opencode' }} gemini-model: ${{ inputs.gemini-model || 'gemini-2.5-flash' }} + opencode-provider: ${{ inputs.opencode-provider || 'openrouter' }} openrouter-model: ${{ inputs.openrouter-model || 'moonshotai/kimi-k2.5' }} comment-title: ${{ inputs.comment-title || '🔄 Revalidation (Comment Triggered)' }} comment-mode: 'create' diff --git a/action.yml b/action.yml index bd5a555..a8274bb 100644 --- a/action.yml +++ b/action.yml @@ -86,12 +86,16 @@ inputs: description: 'AI provider to use (opencode or gemini)' required: false default: 'opencode' + opencode-provider: + description: 'AI provider for OpenCode (e.g., openrouter, anthropic, openai)' + required: false + default: 'openrouter' openrouter-model: - description: 'OpenRouter model ID when provider is opencode (e.g., moonshotai/kimi-k2.5)' + description: 'Model ID when provider is opencode (e.g., moonshotai/kimi-k2.5 for openrouter, claude-sonnet-4-5 for anthropic)' required: false default: 'moonshotai/kimi-k2.5' openrouter-api-key-secret: - description: 'Name of secret containing OpenRouter API key (when provider is opencode)' + description: 'Name of secret containing the API key for the configured opencode-provider' required: false default: 'OPENROUTER_API_KEY' @@ -319,6 +323,7 @@ runs: MCP_SECRET_NAME: ${{ inputs.mcp-token-secret }} WORKSPACE_ID: ${{ inputs.workspace-id }} PROVIDER: ${{ inputs.provider }} + OPENCODE_PROVIDER: ${{ inputs.opencode-provider }} OPENROUTER_MODEL: ${{ inputs.openrouter-model }} run: | ${{ github.action_path }}/scripts/setup-mcp.sh @@ -344,6 +349,7 @@ runs: USE_DYNAMIC_PROMPTS: ${{ inputs.use-dynamic-prompts }} PROVIDER: ${{ inputs.provider }} GEMINI_MODEL: ${{ inputs.gemini-model }} + OPENCODE_PROVIDER: ${{ inputs.opencode-provider }} OPENROUTER_MODEL: ${{ inputs.openrouter-model }} MAX_RETRIES: ${{ inputs.max-retries }} PR_NUMBER: ${{ github.event.pull_request.number }} @@ -384,7 +390,7 @@ runs: const provider = '${{ inputs.provider }}'; const modelInfo = provider === 'opencode' - ? 'OpenRouter ${{ inputs.openrouter-model }}' + ? '${{ inputs.opencode-provider }}/${{ inputs.openrouter-model }}' : 'Google Gemini ${{ inputs.gemini-model }}'; const commentBody = marker + '\n## 🤖 ' + commentTitle + '\n\n' + diff --git a/scripts/setup-mcp.sh b/scripts/setup-mcp.sh index a2ec4f4..3fa2991 100755 --- a/scripts/setup-mcp.sh +++ b/scripts/setup-mcp.sh @@ -7,6 +7,7 @@ echo "::group::Setting up MCP Server Integration" MCP_SECRET_NAME="${MCP_SECRET_NAME:-USABLE_API_TOKEN}" MCP_URL="${MCP_SERVER_URL:-https://usable.dev/api/mcp}" PROVIDER="${PROVIDER:-opencode}" +OPENCODE_PROVIDER="${OPENCODE_PROVIDER:-openrouter}" OPENROUTER_MODEL="${OPENROUTER_MODEL:-moonshotai/kimi-k2.5}" # Get the MCP token from environment using the secret name @@ -31,9 +32,9 @@ if [ "$PROVIDER" = "opencode" ]; then { "\$schema": "https://opencode.ai/config.json", "provider": { - "openrouter": {} + "${OPENCODE_PROVIDER}": {} }, - "model": "openrouter/${OPENROUTER_MODEL}", + "model": "${OPENCODE_PROVIDER}/${OPENROUTER_MODEL}", "autoupdate": false, "mcp": { "usable": { @@ -58,7 +59,7 @@ EOF echo "✅ OpenCode MCP server configured" echo " URL: $MCP_URL" - echo " Model: openrouter/${OPENROUTER_MODEL}" + echo " Model: ${OPENCODE_PROVIDER}/${OPENROUTER_MODEL}" echo " Settings file: ./opencode.json" # Debug: Show settings file content (mask token) diff --git a/scripts/validate.sh b/scripts/validate.sh index a279391..9488529 100755 --- a/scripts/validate.sh +++ b/scripts/validate.sh @@ -356,7 +356,9 @@ run_opencode() { local prompt_file="$1" local retry_count=0 local max_retries="${MAX_RETRIES:-2}" + local opencode_provider="${OPENCODE_PROVIDER:-openrouter}" local model="${OPENROUTER_MODEL:-moonshotai/kimi-k2.5}" + local full_model="${opencode_provider}/${model}" while [ $retry_count -le "$max_retries" ]; do echo "Attempt $((retry_count + 1))/$((max_retries + 1)): Running OpenCode validation..." @@ -374,7 +376,7 @@ run_opencode() { # Show detailed execution info echo "🤖 Running OpenCode CLI" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "Model: openrouter/$model" + echo "Model: $full_model" echo "Prompt size: $(wc -c < "$prompt_file") bytes" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" @@ -382,7 +384,7 @@ run_opencode() { # Run OpenCode CLI and capture output set +e # Temporarily disable exit on error to capture exit code - opencode -q -m "openrouter/$model" -p "$(cat "$prompt_file")" 2>&1 | tee /tmp/validation-full-output.md + opencode run -m "$full_model" "$(cat "$prompt_file")" 2>&1 | tee /tmp/validation-full-output.md local exit_code=$? set -e # Re-enable exit on error From c852b9132e3d50a3926a4d69660b0573c70ec326 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaspur=20H=C3=B8jgaard?= Date: Wed, 11 Feb 2026 12:28:45 +0000 Subject: [PATCH 2/3] feat: update OpenCode references to use consistent naming and improve documentation --- .github/workflows/comment-revalidation.yml | 10 ++--- .github/workflows/test.yml | 6 +-- README.md | 44 +++++++++++----------- action.yml | 17 +++++---- scripts/setup-mcp.sh | 6 +-- scripts/setup-opencode.sh | 20 ++++++---- scripts/validate.sh | 2 +- 7 files changed, 56 insertions(+), 49 deletions(-) diff --git a/.github/workflows/comment-revalidation.yml b/.github/workflows/comment-revalidation.yml index db10a00..16c6317 100644 --- a/.github/workflows/comment-revalidation.yml +++ b/.github/workflows/comment-revalidation.yml @@ -40,7 +40,7 @@ on: required: false type: string default: 'openrouter' - openrouter-model: + opencode-model: description: 'Model ID (when provider is opencode)' required: false type: string @@ -59,8 +59,8 @@ on: GEMINI_SERVICE_ACCOUNT_KEY: description: 'Base64-encoded Gemini service account key (required when provider is gemini)' required: false - OPENROUTER_API_KEY: - description: 'OpenRouter API key (required when provider is opencode)' + OPENCODE_API_KEY: + description: 'OpenCode provider API key (required when provider is opencode)' required: false USABLE_API_TOKEN: description: 'Usable API token' @@ -294,14 +294,14 @@ jobs: provider: ${{ inputs.provider || 'opencode' }} gemini-model: ${{ inputs.gemini-model || 'gemini-2.5-flash' }} opencode-provider: ${{ inputs.opencode-provider || 'openrouter' }} - openrouter-model: ${{ inputs.openrouter-model || 'moonshotai/kimi-k2.5' }} + opencode-model: ${{ inputs.opencode-model || 'moonshotai/kimi-k2.5' }} comment-title: ${{ inputs.comment-title || '🔄 Revalidation (Comment Triggered)' }} comment-mode: 'create' fail-on-critical: ${{ inputs.fail-on-critical || false }} override-comment: ${{ needs.check-trigger.outputs.comment-body }} env: GEMINI_SERVICE_ACCOUNT_KEY: ${{ secrets.GEMINI_SERVICE_ACCOUNT_KEY }} - OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} USABLE_API_TOKEN: ${{ secrets.USABLE_API_TOKEN }} COMMENT_AUTHOR: ${{ github.event.comment.user.login }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 449d5d1..81d516f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -319,7 +319,7 @@ jobs: fail-on-critical: false provider: 'opencode' env: - OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} GEMINI_SERVICE_ACCOUNT_KEY: ${{ secrets.GEMINI_SERVICE_ACCOUNT_KEY }} USABLE_API_TOKEN: ${{ secrets.USABLE_API_TOKEN }} @@ -438,7 +438,7 @@ jobs: fail-on-critical: false provider: 'opencode' env: - OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} GEMINI_SERVICE_ACCOUNT_KEY: ${{ secrets.GEMINI_SERVICE_ACCOUNT_KEY }} USABLE_API_TOKEN: ${{ secrets.USABLE_API_TOKEN }} @@ -579,7 +579,7 @@ jobs: fail-on-critical: false provider: 'opencode' env: - OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} GEMINI_SERVICE_ACCOUNT_KEY: ${{ secrets.GEMINI_SERVICE_ACCOUNT_KEY }} USABLE_API_TOKEN: ${{ secrets.USABLE_API_TOKEN }} COMMENT_AUTHOR: 'test-user' diff --git a/README.md b/README.md index ff9e9b1..1812f0e 100644 --- a/README.md +++ b/README.md @@ -64,8 +64,8 @@ git diff origin/{{BASE_BRANCH}}...{{HEAD_BRANCH}} Go to your repository Settings → Secrets → Actions and add: -**For OpenRouter (default):** -- `OPENROUTER_API_KEY`: Your OpenRouter API key (get from [openrouter.ai](https://openrouter.ai/settings/keys)) +**For OpenCode (default):** +- `OPENCODE_API_KEY`: Your AI provider API key (e.g., [OpenRouter](https://openrouter.ai/settings/keys), [Anthropic](https://console.anthropic.com/), or [OpenAI](https://platform.openai.com/)) - `USABLE_API_TOKEN`: Your Usable API token (get from [usable.dev](https://usable.dev) → Settings → API Tokens) **For Gemini (alternative):** @@ -105,7 +105,7 @@ jobs: prompt-file: '.github/prompts/pr-validation.md' workspace-id: 'your-workspace-uuid' env: - OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} USABLE_API_TOKEN: ${{ secrets.USABLE_API_TOKEN }} ``` @@ -123,8 +123,9 @@ That's it! Your PRs will now be validated automatically. 🎉 | `workspace-id` | Usable workspace UUID (required - used to fetch MCP system prompt) | ✓ | | | `merge-custom-prompt` | Merge fetched Usable prompt with custom `prompt-file` (only when both are provided) | | `true` | | `provider` | AI provider to use (`opencode` or `gemini`) | | `opencode` | -| `openrouter-model` | OpenRouter model ID (when provider is `opencode`) | | `moonshotai/kimi-k2.5` | -| `openrouter-api-key-secret` | Secret name for OpenRouter API key | | `OPENROUTER_API_KEY` | +| `opencode-provider` | AI provider for OpenCode (e.g., `openrouter`, `anthropic`, `openai`) | | `openrouter` | +| `opencode-model` | Model ID for OpenCode | | `moonshotai/kimi-k2.5` | +| `opencode-api-key-secret` | Secret name for the configured opencode-provider API key | | `OPENCODE_API_KEY` | | `gemini-model` | Gemini model to use (when provider is `gemini`) | | `gemini-2.5-flash` | | `service-account-key-secret` | Secret name for Gemini service account key | | `GEMINI_SERVICE_ACCOUNT_KEY` | | `mcp-server-url` | Usable MCP server URL | | `https://usable.dev/api/mcp` | @@ -139,7 +140,7 @@ That's it! Your PRs will now be validated automatically. 🎉 | `head-ref` | Head reference for diff comparison | | PR head branch | | `allow-web-fetch` | Allow AI to use web_fetch tool for external resources (security consideration) | | `false` | -> **Note**: You must set the `USABLE_API_TOKEN` secret and either `OPENROUTER_API_KEY` (default provider) or `GEMINI_SERVICE_ACCOUNT_KEY` (gemini provider). Usable MCP integration is required for this action. +> **Note**: You must set the `USABLE_API_TOKEN` secret and either `OPENCODE_API_KEY` (default provider) or `GEMINI_SERVICE_ACCOUNT_KEY` (gemini provider). Usable MCP integration is required for this action. ### 🧠 System Prompts (Automatic) @@ -246,7 +247,7 @@ Instead of maintaining static prompt files, you can now fetch prompts dynamicall prompt-fragment-id: 'user-prompt-uuid' workspace-id: 'your-workspace-uuid' env: - OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} USABLE_API_TOKEN: ${{ secrets.USABLE_API_TOKEN }} # Static user prompt file (most common) @@ -255,7 +256,7 @@ Instead of maintaining static prompt files, you can now fetch prompts dynamicall prompt-file: '.github/prompts/pr-validation.md' workspace-id: 'your-workspace-uuid' env: - OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} USABLE_API_TOKEN: ${{ secrets.USABLE_API_TOKEN }} ``` @@ -278,7 +279,7 @@ Instead of maintaining static prompt files, you can now fetch prompts dynamicall prompt-file: '.github/prompts/validate.md' workspace-id: 'your-workspace-uuid' env: - OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} USABLE_API_TOKEN: ${{ secrets.USABLE_API_TOKEN }} ``` @@ -305,7 +306,7 @@ Instead of maintaining static prompt files, you can now fetch prompts dynamicall mcp-server-url: 'https://your-custom-mcp.com/api/mcp' mcp-token-secret: 'YOUR_CUSTOM_TOKEN' env: - OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} YOUR_CUSTOM_TOKEN: ${{ secrets.YOUR_MCP_TOKEN }} ``` @@ -317,7 +318,8 @@ Instead of maintaining static prompt files, you can now fetch prompts dynamicall prompt-file: '.github/validation/standards.md' workspace-id: 'your-workspace-uuid' provider: 'opencode' - openrouter-model: 'anthropic/claude-sonnet-4-5' + opencode-provider: 'anthropic' + opencode-model: 'claude-sonnet-4-5' mcp-server-url: 'https://confluence.company.com/api/mcp' mcp-token-secret: 'CONFLUENCE_TOKEN' fail-on-critical: true @@ -325,7 +327,7 @@ Instead of maintaining static prompt files, you can now fetch prompts dynamicall artifact-retention-days: 90 max-retries: 3 env: - OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} CONFLUENCE_TOKEN: ${{ secrets.CONF_API_TOKEN }} ``` @@ -348,7 +350,7 @@ jobs: workspace-id: 'your-workspace-uuid' comment-title: 'Backend Validation' # Creates unique comment env: - OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} USABLE_API_TOKEN: ${{ secrets.USABLE_API_TOKEN }} validate-frontend: @@ -364,7 +366,7 @@ jobs: workspace-id: 'your-workspace-uuid' comment-title: 'Frontend Validation' # Creates unique comment env: - OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} USABLE_API_TOKEN: ${{ secrets.USABLE_API_TOKEN }} ``` @@ -410,7 +412,7 @@ jobs: workspace-id: 'your-workspace-uuid' base-ref: ${{ steps.base-ref.outputs.ref }} # Custom base reference env: - OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} USABLE_API_TOKEN: ${{ secrets.USABLE_API_TOKEN }} ``` @@ -491,7 +493,7 @@ Usable is a team knowledge base and memory system that stores your: ```bash # In your repo: Settings → Secrets → Actions USABLE_API_TOKEN=your_usable_token_here - OPENROUTER_API_KEY=your_openrouter_key_here + OPENCODE_API_KEY=your_api_key_here ``` 3. **Configure Workflow** @@ -502,7 +504,7 @@ Usable is a team knowledge base and memory system that stores your: prompt-file: '.github/prompts/pr-validation.md' workspace-id: 'your-workspace-uuid' env: - OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} USABLE_API_TOKEN: ${{ secrets.USABLE_API_TOKEN }} ``` @@ -573,11 +575,11 @@ jobs: # use-dynamic-prompts: true # prompt-fragment-id: 'fragment-uuid' # provider: 'opencode' - # openrouter-model: 'moonshotai/kimi-k2.5' + # opencode-model: 'moonshotai/kimi-k2.5' # comment-title: '🔄 Custom Title' # fail-on-critical: false secrets: - OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} USABLE_API_TOKEN: ${{ secrets.USABLE_API_TOKEN }} permissions: contents: read @@ -700,7 +702,7 @@ The `allow-web-fetch` input controls whether the AI can download external resour prompt-file: '.github/prompts/validate-api-docs.md' allow-web-fetch: true # Only enable when needed env: - OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} USABLE_API_TOKEN: ${{ secrets.USABLE_API_TOKEN }} ``` @@ -985,7 +987,7 @@ git clone https://github.com/flowcore/usable-pr-validator.git cd usable-pr-validator # Test locally (requires act) -act pull_request -s OPENROUTER_API_KEY="your-key-here" -s USABLE_API_TOKEN="your-token" +act pull_request -s OPENCODE_API_KEY="your-key-here" -s USABLE_API_TOKEN="your-token" ``` ## 📜 License diff --git a/action.yml b/action.yml index a8274bb..65bc418 100644 --- a/action.yml +++ b/action.yml @@ -90,14 +90,14 @@ inputs: description: 'AI provider for OpenCode (e.g., openrouter, anthropic, openai)' required: false default: 'openrouter' - openrouter-model: - description: 'Model ID when provider is opencode (e.g., moonshotai/kimi-k2.5 for openrouter, claude-sonnet-4-5 for anthropic)' + opencode-model: + description: 'Model ID for OpenCode (e.g., moonshotai/kimi-k2.5 for openrouter, claude-sonnet-4-5 for anthropic)' required: false default: 'moonshotai/kimi-k2.5' - openrouter-api-key-secret: + opencode-api-key-secret: description: 'Name of secret containing the API key for the configured opencode-provider' required: false - default: 'OPENROUTER_API_KEY' + default: 'OPENCODE_API_KEY' outputs: validation-status: @@ -200,7 +200,8 @@ runs: if: inputs.provider == 'opencode' shell: bash env: - OPENROUTER_SECRET_NAME: ${{ inputs.openrouter-api-key-secret }} + OPENCODE_PROVIDER: ${{ inputs.opencode-provider }} + OPENCODE_SECRET_NAME: ${{ inputs.opencode-api-key-secret }} run: | ${{ github.action_path }}/scripts/setup-opencode.sh @@ -324,7 +325,7 @@ runs: WORKSPACE_ID: ${{ inputs.workspace-id }} PROVIDER: ${{ inputs.provider }} OPENCODE_PROVIDER: ${{ inputs.opencode-provider }} - OPENROUTER_MODEL: ${{ inputs.openrouter-model }} + OPENCODE_MODEL: ${{ inputs.opencode-model }} run: | ${{ github.action_path }}/scripts/setup-mcp.sh @@ -350,7 +351,7 @@ runs: PROVIDER: ${{ inputs.provider }} GEMINI_MODEL: ${{ inputs.gemini-model }} OPENCODE_PROVIDER: ${{ inputs.opencode-provider }} - OPENROUTER_MODEL: ${{ inputs.openrouter-model }} + OPENCODE_MODEL: ${{ inputs.opencode-model }} MAX_RETRIES: ${{ inputs.max-retries }} PR_NUMBER: ${{ github.event.pull_request.number }} PR_TITLE: ${{ github.event.pull_request.title }} @@ -390,7 +391,7 @@ runs: const provider = '${{ inputs.provider }}'; const modelInfo = provider === 'opencode' - ? '${{ inputs.opencode-provider }}/${{ inputs.openrouter-model }}' + ? '${{ inputs.opencode-provider }}/${{ inputs.opencode-model }}' : 'Google Gemini ${{ inputs.gemini-model }}'; const commentBody = marker + '\n## 🤖 ' + commentTitle + '\n\n' + diff --git a/scripts/setup-mcp.sh b/scripts/setup-mcp.sh index 3fa2991..69eb3b7 100755 --- a/scripts/setup-mcp.sh +++ b/scripts/setup-mcp.sh @@ -8,7 +8,7 @@ MCP_SECRET_NAME="${MCP_SECRET_NAME:-USABLE_API_TOKEN}" MCP_URL="${MCP_SERVER_URL:-https://usable.dev/api/mcp}" PROVIDER="${PROVIDER:-opencode}" OPENCODE_PROVIDER="${OPENCODE_PROVIDER:-openrouter}" -OPENROUTER_MODEL="${OPENROUTER_MODEL:-moonshotai/kimi-k2.5}" +OPENCODE_MODEL="${OPENCODE_MODEL:-moonshotai/kimi-k2.5}" # Get the MCP token from environment using the secret name MCP_TOKEN="${!MCP_SECRET_NAME:-}" @@ -34,7 +34,7 @@ if [ "$PROVIDER" = "opencode" ]; then "provider": { "${OPENCODE_PROVIDER}": {} }, - "model": "${OPENCODE_PROVIDER}/${OPENROUTER_MODEL}", + "model": "${OPENCODE_PROVIDER}/${OPENCODE_MODEL}", "autoupdate": false, "mcp": { "usable": { @@ -59,7 +59,7 @@ EOF echo "✅ OpenCode MCP server configured" echo " URL: $MCP_URL" - echo " Model: ${OPENCODE_PROVIDER}/${OPENROUTER_MODEL}" + echo " Model: ${OPENCODE_PROVIDER}/${OPENCODE_MODEL}" echo " Settings file: ./opencode.json" # Debug: Show settings file content (mask token) diff --git a/scripts/setup-opencode.sh b/scripts/setup-opencode.sh index 988b210..44de57b 100755 --- a/scripts/setup-opencode.sh +++ b/scripts/setup-opencode.sh @@ -1,16 +1,17 @@ #!/usr/bin/env bash set -euo pipefail -echo "::group::Setting up OpenCode CLI + OpenRouter" +OPENCODE_PROVIDER="${OPENCODE_PROVIDER:-openrouter}" + +echo "::group::Setting up OpenCode CLI + ${OPENCODE_PROVIDER}" # Get the secret value from the environment using the secret name -SECRET_NAME="${OPENROUTER_SECRET_NAME:-OPENROUTER_API_KEY}" +SECRET_NAME="${OPENCODE_SECRET_NAME:-OPENCODE_API_KEY}" SECRET_VALUE="${!SECRET_NAME:-}" if [ -z "$SECRET_VALUE" ]; then - echo "::error::OpenRouter API key not found in environment variable: $SECRET_NAME" + echo "::error::API key not found in environment variable: $SECRET_NAME" echo "Please ensure the secret is set in your workflow: env.$SECRET_NAME" - echo "For local testing: export OPENROUTER_API_KEY=''" exit 1 fi @@ -43,13 +44,16 @@ fi echo "✅ OpenCode CLI installed: $(opencode --version 2>/dev/null || echo 'version unknown')" -# Export OpenRouter API key for OpenCode to use -export OPENROUTER_API_KEY="$SECRET_VALUE" +# Determine the correct env var name for the provider +# OpenCode expects provider-specific env vars (e.g., OPENROUTER_API_KEY, ANTHROPIC_API_KEY) +PROVIDER_ENV_VAR="$(echo "${OPENCODE_PROVIDER}" | tr '[:lower:]' '[:upper:]')_API_KEY" + +export "$PROVIDER_ENV_VAR"="$SECRET_VALUE" # Write to GITHUB_ENV for subsequent steps (if in GitHub Actions) if [ -n "${GITHUB_ENV:-}" ]; then - echo "OPENROUTER_API_KEY=$SECRET_VALUE" >> "$GITHUB_ENV" + echo "$PROVIDER_ENV_VAR=$SECRET_VALUE" >> "$GITHUB_ENV" fi -echo "✅ OpenRouter authentication configured" +echo "✅ ${OPENCODE_PROVIDER} authentication configured (${PROVIDER_ENV_VAR})" echo "::endgroup::" diff --git a/scripts/validate.sh b/scripts/validate.sh index 9488529..b6a94f5 100755 --- a/scripts/validate.sh +++ b/scripts/validate.sh @@ -357,7 +357,7 @@ run_opencode() { local retry_count=0 local max_retries="${MAX_RETRIES:-2}" local opencode_provider="${OPENCODE_PROVIDER:-openrouter}" - local model="${OPENROUTER_MODEL:-moonshotai/kimi-k2.5}" + local model="${OPENCODE_MODEL:-moonshotai/kimi-k2.5}" local full_model="${opencode_provider}/${model}" while [ $retry_count -le "$max_retries" ]; do From 013f3ca511c09b4e1cdacec5596f592ac6b4310d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaspur=20H=C3=B8jgaard?= Date: Wed, 11 Feb 2026 12:37:57 +0000 Subject: [PATCH 3/3] feat: enhance documentation for multi-provider support in Usable PR Validator --- README.md | 38 ++++++++++++++++++++++++++++++++------ action.yml | 2 +- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 1812f0e..1936b4c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # 🤖 Usable PR Validator -> Validate Pull Requests against your Usable knowledge base standards using AI (OpenCode/OpenRouter or Google Gemini) +> Validate Pull Requests against your Usable knowledge base standards using AI (OpenCode with OpenRouter, Anthropic, OpenAI, or Google Gemini) [![GitHub Marketplace](https://img.shields.io/badge/Marketplace-Usable%20PR%20Validator-blue?logo=github)](https://github.com/marketplace/actions/usable-pr-validator) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) @@ -8,8 +8,8 @@ ## ✨ Features -- 🧠 **AI-Powered Validation**: Uses OpenCode/OpenRouter (default) or Google Gemini to understand context and architectural patterns -- 🔀 **Multi-Provider Support**: Choose between OpenRouter (75+ models) or Google Gemini +- 🧠 **AI-Powered Validation**: Uses OpenCode with OpenRouter (default), Anthropic, OpenAI, or Google Gemini to understand context and architectural patterns +- 🔀 **Multi-Provider Support**: Choose between OpenRouter (75+ models), Anthropic, OpenAI, or Google Gemini - 📚 **Usable Integration**: Validate PRs against your team's knowledge base stored in Usable - 🔌 **MCP Protocol**: Connects directly to Usable's MCP server for real-time standards - 🎯 **System Prompts**: Organization-wide validation standards fetched from Usable and auto-merged @@ -26,8 +26,8 @@ ### Prerequisites -**For OpenRouter (default provider):** -1. An OpenRouter API key ([get one at openrouter.ai](https://openrouter.ai/settings/keys)) +**For OpenCode (default, supports OpenRouter, Anthropic, OpenAI):** +1. An API key for your chosen provider (e.g., [OpenRouter](https://openrouter.ai/settings/keys), [Anthropic](https://console.anthropic.com/), or [OpenAI](https://platform.openai.com/)) 2. A Usable account with API token ([get one at usable.dev](https://usable.dev)) 3. GitHub repository with pull requests @@ -37,6 +37,24 @@ 3. A Usable account with API token ([get one at usable.dev](https://usable.dev)) 4. GitHub repository with pull requests +### Choosing a Provider + +Provider selection works in two levels: + +1. **`provider`** chooses the CLI tool: `opencode` (default) or `gemini` +2. **`opencode-provider`** chooses which AI service OpenCode connects to (only applies when `provider` is `opencode`) + +``` +provider +├── opencode (default) +│ └── opencode-provider +│ ├── openrouter (default) ─ 75+ models via OpenRouter +│ ├── anthropic ─ Claude models direct +│ └── openai ─ GPT models direct +└── gemini + └── Uses Google Gemini directly (configured via gemini-model) +``` + ### Step 1: Create Validation Prompt Create `.github/prompts/pr-validation.md` in your repository: @@ -68,6 +86,14 @@ Go to your repository Settings → Secrets → Actions and add: - `OPENCODE_API_KEY`: Your AI provider API key (e.g., [OpenRouter](https://openrouter.ai/settings/keys), [Anthropic](https://console.anthropic.com/), or [OpenAI](https://platform.openai.com/)) - `USABLE_API_TOKEN`: Your Usable API token (get from [usable.dev](https://usable.dev) → Settings → API Tokens) +> If using a non-default provider, also set `opencode-provider` and `opencode-model` in your workflow: +> +> | Provider | `opencode-provider` | `opencode-model` example | +> |----------|--------------------|-----------------------| +> | OpenRouter (default) | `openrouter` | `moonshotai/kimi-k2.5` | +> | Anthropic | `anthropic` | `claude-sonnet-4-5` | +> | OpenAI | `openai` | `gpt-4o` | + **For Gemini (alternative):** - `GEMINI_SERVICE_ACCOUNT_KEY`: Base64-encoded service account JSON key @@ -271,7 +297,7 @@ Instead of maintaining static prompt files, you can now fetch prompts dynamicall ## 🎯 Usage Examples -### Minimal Setup (OpenRouter - default) +### Minimal Setup (OpenCode - default) ```yaml - uses: flowcore/usable-pr-validator@latest diff --git a/action.yml b/action.yml index 65bc418..b134aa2 100644 --- a/action.yml +++ b/action.yml @@ -1,5 +1,5 @@ name: 'Usable PR Validator' -description: 'Validate Pull Requests against your Usable knowledge base standards using AI (OpenCode/OpenRouter or Google Gemini) and Usable MCP integration' +description: 'Validate Pull Requests against your Usable knowledge base standards using AI (OpenCode with OpenRouter, Anthropic, OpenAI, or Google Gemini) and Usable MCP integration' author: 'Flowcore' branding: