diff --git a/ci-operator/step-registry/hypershift/jira-agent/README.md b/ci-operator/step-registry/hypershift/jira-agent/README.md
deleted file mode 100644
index 0630d7c03d8f3..0000000000000
--- a/ci-operator/step-registry/hypershift/jira-agent/README.md
+++ /dev/null
@@ -1,286 +0,0 @@
-# HyperShift Jira Agent Workflow
-
-Automated periodic job that processes Jira issues labeled with `issue-for-agent` and creates pull requests using Claude Code.
-
-## Overview
-
-This workflow implements a fully automated system for processing HyperShift Jira issues:
-
-1. **Query**: Searches Jira for unresolved issues in OCPBUGS and CNTRLPLANE projects with label `issue-for-agent` (excluding those with `agent-processed`)
-2. **Process**: For each issue, runs the `/jira-solve` command from the HyperShift repository non-interactively
-3. **Track**: Adds `agent-processed` label to successfully processed issues to prevent reprocessing
-
-## Data Flow Diagram
-
-```mermaid
-flowchart TD
- %% Trigger
- Start([Cron Trigger
Daily 9:00 AM UTC]):::trigger --> PrePhase
-
- %% PRE-PHASE: Setup
- subgraph PrePhase[PRE-PHASE: Setup]
- direction TB
- Verify[Verify Claude Code CLI
claude --version]:::setup
- end
-
- %% TEST-PHASE: Process
- PrePhase --> TestPhase
-
- subgraph TestPhase[TEST-PHASE: Process Issues]
- direction TB
-
- CloneRepos[Clone Repositories
ai-helpers + hypershift-community/hypershift]:::setup
- CopyCommand[Copy jira-solve command
to .claude/commands/]:::setup
- GitConfig[Configure Git
user: OpenShift CI Bot]:::setup
- GenTokens[Generate GitHub App Tokens
JWT auth for fork + upstream]:::setup
-
- QueryJira[Query Jira API
JQL: status in New, To Do
AND labels = issue-for-agent
AND labels != agent-processed]:::process
-
- CheckIssues{Issues
Found?}:::decision
- CheckMax{Processed <
MAX_ISSUES
Default: 1}:::decision
- CheckSuccess{Processing
Successful?}:::decision
-
- ProcessIssue[Run Claude Code CLI
--system-prompt jira-solve.md
--max-turns 100]:::ai
-
- AddLabel[Add label
agent-processed
to Jira issue]:::success
- LogFailure[Log failure
Will retry next run]:::failure
- NoIssues[Exit: No issues to process]:::skip
-
- RateLimit[Wait 60 seconds
Rate limiting]:::process
- Summary[Print Summary
Processed/Failed counts]:::process
-
- CloneRepos --> CopyCommand --> GitConfig --> GenTokens --> QueryJira
- QueryJira --> CheckIssues
- CheckIssues -->|No| NoIssues
- CheckIssues -->|Yes| CheckMax
- CheckMax -->|No| Summary
- CheckMax -->|Yes| ProcessIssue
- ProcessIssue --> CheckSuccess
- CheckSuccess -->|Yes| AddLabel
- CheckSuccess -->|No| LogFailure
- AddLabel --> RateLimit
- LogFailure --> RateLimit
- RateLimit --> CheckMax
- end
-
- %% Secrets
- Secret1[(Secret:
hypershift-team-claude-prow
app-id, private-key,
installation-ids)]:::secret -.->|GitHub App auth| GenTokens
- Secret1 -.->|Vertex AI auth| ProcessIssue
-
- %% External Systems
- JiraAPI[(Jira API
redhat.atlassian.net)]:::external -.->|Return issues| QueryJira
- JiraAPI -.->|Add label| AddLabel
- ClaudeAPI[(Claude API
via Vertex AI)]:::external -.->|Generate solution| ProcessIssue
- GitHubAPI[(GitHub API)]:::external -.->|Push to fork| ProcessIssue
- GitHubAPI -.->|Create PR to upstream| ProcessIssue
-
- TestPhase --> End([Workflow Complete]):::trigger
- NoIssues --> End
- Summary --> End
-
- %% Style Definitions
- classDef trigger fill:#e1f5ff,stroke:#01579b,stroke-width:3px,color:#000
- classDef setup fill:#f3e5f5,stroke:#4a148c,stroke-width:2px,color:#000
- classDef process fill:#e8f5e9,stroke:#1b5e20,stroke-width:2px,color:#000
- classDef decision fill:#fff3e0,stroke:#e65100,stroke-width:2px,color:#000
- classDef ai fill:#fce4ec,stroke:#880e4f,stroke-width:3px,color:#000
- classDef success fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px,color:#000
- classDef failure fill:#ffcdd2,stroke:#c62828,stroke-width:2px,color:#000
- classDef skip fill:#f5f5f5,stroke:#757575,stroke-width:1px,color:#000
- classDef external fill:#fff9c4,stroke:#f57f17,stroke-width:2px,color:#000
- classDef secret fill:#ffebee,stroke:#b71c1c,stroke-width:2px,color:#000
-```
-
-## Components
-
-### Workflow
-- **File**: `hypershift-jira-agent-workflow.yaml`
-- **Description**: Defines the two-phase workflow (pre/test)
-
-### Steps
-
-#### 1. Setup (`hypershift-jira-agent-setup`)
-- Verifies Claude Code CLI is available
-
-#### 2. Process (`hypershift-jira-agent-process`)
-- Clones ai-helpers and hypershift-community/hypershift repositories
-- Copies jira-solve command to `.claude/commands/`
-- Configures git and generates GitHub App tokens (JWT auth)
-- Queries Jira API for labeled issues (excluding those with `agent-processed`)
-- Runs jira-solve for each issue using Claude Code CLI with `--system-prompt`
-- Pushes branches to fork, creates PRs to upstream openshift/hypershift
-- Implements rate limiting (60s between issues)
-- Adds `agent-processed` label to successfully processed issues
-
-## Configuration
-
-### Secrets Required
-
-The workflow requires a single secret in the `test-credentials` namespace:
-
-**`hypershift-team-claude-prow`**
-- Mount path: `/var/run/claude-code-service-account`
-- Required keys:
- - `claude-prow`: GCP service account JSON key for Vertex AI authentication
- - `app-id`: GitHub App ID
- - `private-key`: GitHub App private key for JWT signing
- - `installation-id`: GitHub App installation ID for hypershift-community fork
- - `o-h-installation-id`: GitHub App installation ID for openshift/hypershift upstream
-
-The workflow uses GitHub App authentication (JWT-based) rather than personal access tokens. This provides better security and allows fine-grained permissions.
-
-**Optional:**
-- `hypershift-jira-token`: Jira API token for adding `agent-processed` labels
-- `slack-webhook-url`: Slack incoming webhook URL for posting PR notifications to team-ocp-hypershift
-- `gh-to-slack-ids`: JSON mapping of GitHub usernames to Slack member IDs, plus a `backup-user` key for fallback (e.g., `{"gh-username": "UXXXXXXXXXX", "backup-user": "UXXXXXXXXXX"}`)
-
-These should be configured in Vault with secretsync metadata and synced automatically.
-
-### Periodic Job
-
-Configured in `ci-operator/config/openshift/hypershift/openshift-hypershift-main.yaml`:
-
-```yaml
-- as: periodic-jira-agent
- cron: 0 9 * * * # Daily at 9:00 AM UTC
- steps:
- env:
- JIRA_AGENT_MAX_ISSUES: "1" # Start with 1 for testing, increase later
- workflow: hypershift-jira-agent
-```
-
-### Environment Variables
-
-- **`JIRA_AGENT_MAX_ISSUES`** (default: `1`)
- - Maximum number of issues to process per run
- - Set to `1` initially for safe testing
- - Can be increased to `5`, `10`, or higher once validated
- - Counts both successful and failed processing attempts
-
-### State Management
-
-State is tracked using Jira labels:
-- **Label**: `agent-processed`
-- When an issue is successfully processed, the `agent-processed` label is added
-- The JQL query excludes issues with this label, preventing reprocessing
-- Failed issues are NOT labeled, allowing automatic retry on subsequent runs
-
-To reprocess an issue:
-1. Remove the `agent-processed` label from the Jira issue
-2. The issue will be picked up on the next run
-
-## How It Works
-
-### Non-Interactive Execution
-
-The workflow uses Claude Code CLI's non-interactive mode with a system prompt:
-
-```bash
-claude -p "$ISSUE_KEY origin --ci" \
- --system-prompt "$SKILL_CONTENT" \
- --allowedTools "Bash Read Write Edit Grep Glob WebFetch" \
- --max-turns 100 \
- --verbose \
- --output-format stream-json
-```
-
-The jira-solve command is loaded from `ai-helpers/plugins/jira/commands/solve.md` and passed as a system prompt. This allows Claude to analyze the Jira issue and create a PR automatically.
-
-### Jira Query
-
-Issues are queried using JQL:
-```
-project in (OCPBUGS, CNTRLPLANE) AND resolution = Unresolved AND status in (New, "To Do") AND labels = issue-for-agent AND labels != agent-processed
-```
-
-Maximum issues queried and processed is controlled by `JIRA_AGENT_MAX_ISSUES` (default: 1).
-
-### Rate Limiting
-
-- 60 seconds between processing each issue
-- Maximum 100 agentic turns per issue
-- Maximum issues per run: configurable via `JIRA_AGENT_MAX_ISSUES`
-- Runs once daily at 9:00 AM UTC
-
-## Container Image
-
-Uses the `claude-ai-helpers` image from OpenShift CI containing:
-- Claude Code CLI
-- GitHub CLI (gh)
-- jq, git, curl
-- Required dependencies
-
-## Local Testing
-
-Use the test script:
-
-```bash
-export ANTHROPIC_API_KEY=your-key
-export GITHUB_TOKEN=your-token
-./tools/hypershift-jira-agent/test-locally.sh
-```
-
-## Monitoring
-
-### Success Indicators
-- Issues processed successfully with PRs created
-- `agent-processed` label added to processed issues
-- No authentication errors
-
-### Failure Indicators
-- Failed to authenticate with Claude API
-- Failed to create PRs (GitHub auth issues)
-- Individual issue processing failures
-
-### Logs
-Check Prow job logs for:
-- Jira query results
-- Processing output for each issue
-- PR URLs created
-- Error messages
-
-## Maintenance
-
-### Adding/Removing Issues
-Add or remove the `issue-for-agent` label in Jira to control which issues are processed.
-
-### Reprocessing an Issue
-To reprocess an issue, remove the `agent-processed` label from the Jira issue:
-1. Open the issue in Jira
-2. Remove the `agent-processed` label
-3. The issue will be picked up on the next scheduled run
-
-### Adjusting Frequency
-Modify the `cron` schedule in the CI config file. Currently runs daily at 9:00 AM UTC.
-
-### Adjusting Issue Limit
-Modify the `JIRA_AGENT_MAX_ISSUES` environment variable in the CI config file:
-```yaml
-env:
- JIRA_AGENT_MAX_ISSUES: "5" # Increase from 1 to 5
-```
-Then run `make update` to regenerate job configs.
-
-## Troubleshooting
-
-### Issue: No issues being processed
-- Check Jira query returns results
-- Verify `issue-for-agent` label exists on issues
-- Verify `agent-processed` label is NOT on issues (or remove it to reprocess)
-
-### Issue: Authentication failures
-- Verify secrets are mounted correctly
-- Check API keys are valid and not expired
-- Ensure GitHub token has required permissions
-
-### Issue: PR creation fails
-- Check GitHub token permissions
-- Verify HyperShift repository access
-- Review `/jira-solve` command output in logs
-
-## Future Enhancements
-
-- Metrics push to Prometheus
-- Automatic retries for transient failures
-- Priority-based processing
-- Issue assignment tracking
diff --git a/ci-operator/step-registry/hypershift/jira-agent/hypershift-jira-agent-workflow.yaml b/ci-operator/step-registry/hypershift/jira-agent/hypershift-jira-agent-workflow.yaml
index 42857ce8af563..cad7046952060 100644
--- a/ci-operator/step-registry/hypershift/jira-agent/hypershift-jira-agent-workflow.yaml
+++ b/ci-operator/step-registry/hypershift/jira-agent/hypershift-jira-agent-workflow.yaml
@@ -2,22 +2,35 @@ workflow:
as: hypershift-jira-agent
steps:
pre:
- - ref: hypershift-jira-agent-setup
+ - ref: jira-agent-setup
test:
- - ref: hypershift-jira-agent-process
+ - ref: jira-agent-process
post:
- - ref: hypershift-jira-agent-report
+ - ref: jira-agent-report
+ env:
+ JIRA_AGENT_FORK_REPO: "hypershift-community/hypershift"
+ JIRA_AGENT_UPSTREAM_REPO: "openshift/hypershift"
+ JIRA_AGENT_JQL: 'project in (OCPBUGS, CNTRLPLANE) AND resolution = Unresolved AND status in (New, "To Do") AND labels = issue-for-agent AND labels != agent-processed'
+ JIRA_AGENT_TARGET_STATUS: '{"OCPBUGS":"ASSIGNED","CNTRLPLANE":"Code Review"}'
+ JIRA_AGENT_ASSIGNEE: "hypershift-automation"
+ JIRA_AGENT_UPSTREAM_INSTALLATION_ID_KEY: "o-h-installation-id"
+ JIRA_AGENT_FORK_INSTALLATION_ID_KEY: "installation-id"
+ JIRA_AGENT_EXTRA_PLUGIN_COMMANDS: |
+ claude plugin install utils@ai-helpers
+ claude plugin install golang@ai-helpers
+ claude plugin marketplace add enxebre/ai-scripts
+ claude plugin install git@enxebre
+ JIRA_AGENT_TOOL_SETUP_SCRIPT: "GOFLAGS='' go install golang.org/x/tools/gopls@v0.21.0 && python3.9 -m ensurepip --user 2>/dev/null || true && python3.9 -m pip install --user pre-commit 2>&1 | tail -1"
+ JIRA_AGENT_REVIEW_LANGUAGE: "go"
+ JIRA_AGENT_REVIEW_PROFILE: "hypershift"
+ JIRA_AGENT_SLACK_EMOJI: ":hypershift-bot:"
documentation: |-
- HyperShift Jira Agent workflow for automated issue processing.
+ HyperShift-specific wrapper for the generic Jira Agent workflow.
- This workflow:
- 1. Setup: Verifies Claude Code CLI is available
- 2. Process: For each Jira issue, runs a four-phase pipeline:
- a. Phase 1 - Solve: Runs /jira-solve to implement, commit, and push changes
- b. Phase 2 - Review: Runs /code-review:pre-commit-review to review code quality (read-only)
- c. Phase 3 - Fix: Addresses review findings by editing code and pushing fixes
- d. Phase 4 - PR: Creates a draft PR after review is complete
- 3. Report: Generates HTML report with per-phase token usage, cost estimates, and posts link on PRs
+ This workflow delegates to the generic jira-agent steps with HyperShift-specific
+ configuration (fork repo, JQL query, status transitions, plugins, etc.).
- The workflow uses /jira-solve and /code-review:pre-commit-review in non-interactive mode.
- Issues are queried from Jira with: project in (OCPBUGS, CNTRLPLANE) AND status in (New, "To Do") AND labels = issue-for-agent
+ Credentials: Uses hypershift-team-claude-prow (configured in generic step refs).
+ When another team onboards, they will need to either:
+ 1. Create their own ref YAMLs pointing to the generic commands with their credential
+ 2. Request the generic credential name be updated to a shared secret
diff --git a/ci-operator/step-registry/hypershift/jira-agent/process/OWNERS b/ci-operator/step-registry/hypershift/jira-agent/process/OWNERS
deleted file mode 100644
index e39269bf55090..0000000000000
--- a/ci-operator/step-registry/hypershift/jira-agent/process/OWNERS
+++ /dev/null
@@ -1,12 +0,0 @@
-approvers:
-- bryan-cox
-- csrwng
-- celebdor
-- enxebre
-- sjenning
-reviewers:
-- bryan-cox
-- csrwng
-- celebdor
-- enxebre
-- sjenning
diff --git a/ci-operator/step-registry/hypershift/jira-agent/process/hypershift-jira-agent-process-ref.yaml b/ci-operator/step-registry/hypershift/jira-agent/process/hypershift-jira-agent-process-ref.yaml
deleted file mode 100644
index 5cfad84f6418b..0000000000000
--- a/ci-operator/step-registry/hypershift/jira-agent/process/hypershift-jira-agent-process-ref.yaml
+++ /dev/null
@@ -1,68 +0,0 @@
-
-ref:
- as: hypershift-jira-agent-process
- from: claude-ai-helpers
- commands: hypershift-jira-agent-process-commands.sh
- timeout: 14400s
- env:
- - name: CLAUDE_CODE_USE_VERTEX
- default: "1"
- documentation: |-
- Enable Vertex AI for Claude Code.
- - name: CLOUD_ML_REGION
- default: "global"
- documentation: |-
- Google Cloud region for Vertex AI.
- - name: ANTHROPIC_VERTEX_PROJECT_ID
- default: "itpc-gcp-hybrid-pe-eng-claude"
- documentation: |-
- Google Cloud project ID for Vertex AI authentication.
- - name: GOOGLE_APPLICATION_CREDENTIALS
- default: "/var/run/claude-code-service-account/claude-prow"
- documentation: |-
- Path to the Google Cloud service account JSON key file for Vertex AI authentication.
- - name: JIRA_AGENT_ISSUE_KEY
- default: ""
- documentation: |-
- Optional override to process a specific Jira issue instead of querying.
- When set (e.g., "CNTRLPLANE-2784"), skips the JQL query and processes
- only this issue. Leave empty for normal JQL-based discovery.
- - name: MULTISTAGE_PARAM_OVERRIDE_JIRA_AGENT_ISSUE_KEY
- default: ""
- documentation: |-
- Gangway API override for JIRA_AGENT_ISSUE_KEY. When triggering
- this job via the Gangway API, pass it as:
- "pod_spec_options": {
- "envs": {
- "MULTISTAGE_PARAM_OVERRIDE_JIRA_AGENT_ISSUE_KEY": "CNTRLPLANE-2784"
- }
- }
- - name: JIRA_AGENT_MAX_ISSUES
- default: "1"
- documentation: |-
- Maximum number of Jira issues to process per run. Defaults to 1 for conservative processing.
- - name: CLAUDE_MODEL
- default: "claude-opus-4-6"
- documentation: |-
- Claude model to use for processing Jira issues.
- resources:
- requests:
- cpu: 500m
- memory: 1Gi
- credentials:
- - namespace: test-credentials
- name: hypershift-team-claude-prow
- mount_path: /var/run/claude-code-service-account
- documentation: |-
- Process step for the HyperShift Jira agent periodic job.
- This step runs a four-phase pipeline for each issue:
- Phase 1 - Solve: Runs /jira-solve to implement changes, commit, and push the branch (no PR created)
- Phase 2 - Review: Runs /code-review:pre-commit-review to review code quality (read-only)
- Phase 3 - Fix: Addresses review findings by editing code, committing, and pushing fixes
- Phase 4 - PR Creation: Creates a draft PR via gh CLI after review is complete
- Token usage (input/output) is extracted per phase and saved for reporting.
- Post-processing: Adds 'agent-processed' label, transitions issue status, sets assignee
- - Queries Jira for issues with label 'issue-for-agent' (excluding 'agent-processed')
- - Failed issues are retried on subsequent runs
- - If the review skill is unavailable or fails, the PR is still created
- - Uses Vertex AI for Claude authentication via GCP service account
diff --git a/ci-operator/step-registry/hypershift/jira-agent/report/OWNERS b/ci-operator/step-registry/hypershift/jira-agent/report/OWNERS
deleted file mode 100644
index e39269bf55090..0000000000000
--- a/ci-operator/step-registry/hypershift/jira-agent/report/OWNERS
+++ /dev/null
@@ -1,12 +0,0 @@
-approvers:
-- bryan-cox
-- csrwng
-- celebdor
-- enxebre
-- sjenning
-reviewers:
-- bryan-cox
-- csrwng
-- celebdor
-- enxebre
-- sjenning
diff --git a/ci-operator/step-registry/hypershift/jira-agent/report/hypershift-jira-agent-report-ref.yaml b/ci-operator/step-registry/hypershift/jira-agent/report/hypershift-jira-agent-report-ref.yaml
deleted file mode 100644
index 0bf59445bd783..0000000000000
--- a/ci-operator/step-registry/hypershift/jira-agent/report/hypershift-jira-agent-report-ref.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-ref:
- as: hypershift-jira-agent-report
- from: claude-ai-helpers
- commands: hypershift-jira-agent-report-commands.sh
- resources:
- requests:
- cpu: 100m
- memory: 256Mi
- documentation: |-
- Generates an HTML report from the jira-agent processing output.
- Parses stream-json output from all three phases (solve, review, PR)
- and produces a readable report in ${ARTIFACT_DIR}.
diff --git a/ci-operator/step-registry/hypershift/jira-agent/setup/OWNERS b/ci-operator/step-registry/hypershift/jira-agent/setup/OWNERS
deleted file mode 100644
index e39269bf55090..0000000000000
--- a/ci-operator/step-registry/hypershift/jira-agent/setup/OWNERS
+++ /dev/null
@@ -1,12 +0,0 @@
-approvers:
-- bryan-cox
-- csrwng
-- celebdor
-- enxebre
-- sjenning
-reviewers:
-- bryan-cox
-- csrwng
-- celebdor
-- enxebre
-- sjenning
diff --git a/ci-operator/step-registry/jira-agent/OWNERS b/ci-operator/step-registry/jira-agent/OWNERS
new file mode 100644
index 0000000000000..ff943340794d2
--- /dev/null
+++ b/ci-operator/step-registry/jira-agent/OWNERS
@@ -0,0 +1,12 @@
+approvers:
+ - bryan-cox
+ - csrwng
+ - celebdor
+ - enxebre
+ - sjenning
+reviewers:
+ - bryan-cox
+ - csrwng
+ - celebdor
+ - enxebre
+ - sjenning
diff --git a/ci-operator/step-registry/jira-agent/README.md b/ci-operator/step-registry/jira-agent/README.md
new file mode 100644
index 0000000000000..7e8b90b1a21ea
--- /dev/null
+++ b/ci-operator/step-registry/jira-agent/README.md
@@ -0,0 +1,80 @@
+# jira-agent Step Registry
+
+Generic, reusable Jira Agent workflow for automated issue processing using Claude Code.
+
+## Overview
+
+This step registry provides a parameterized workflow that:
+1. **Setup** — Verifies Claude Code CLI is available with Vertex AI authentication
+2. **Process** — Runs a four-phase pipeline for each Jira issue:
+ - Phase 1: Solve (implement changes, commit, push)
+ - Phase 2: Review (pre-commit code review, read-only)
+ - Phase 3: Fix (address review findings, commit, push)
+ - Phase 4: PR Creation (create draft PR via `gh`)
+3. **Report** — Generates an HTML report with token usage and cost breakdown
+
+## Quick Start
+
+Create a wrapper workflow in your team's directory that references the generic steps and sets your team-specific env vars:
+
+```yaml
+workflow:
+ as: my-team-jira-agent
+ steps:
+ pre:
+ - ref: jira-agent-setup
+ test:
+ - ref: jira-agent-process
+ post:
+ - ref: jira-agent-report
+ env:
+ JIRA_AGENT_FORK_REPO: "my-org/my-repo"
+ JIRA_AGENT_UPSTREAM_REPO: "openshift/my-repo"
+ JIRA_AGENT_JQL: 'project = MYPROJ AND resolution = Unresolved AND labels = issue-for-agent'
+```
+
+**Credentials:** The generic ref YAMLs use the `hypershift-team-claude-prow` Vault secret.
+Teams using a different secret must create their own ref YAMLs (setup + process) that
+reference the same command scripts but with their credential name. Workflow-level `env`
+cannot override a ref's `credentials` block.
+
+## Required Environment Variables
+
+| Variable | Description |
+|----------|-------------|
+| `JIRA_AGENT_FORK_REPO` | Fork repo in `org/repo` format (e.g., `my-org/my-repo`) |
+| `JIRA_AGENT_UPSTREAM_REPO` | Upstream repo in `org/repo` format (e.g., `openshift/my-repo`) |
+| `JIRA_AGENT_JQL` | JQL query to find issues to process |
+
+## Optional Environment Variables
+
+| Variable | Default | Description |
+|----------|---------|-------------|
+| `JIRA_AGENT_TARGET_STATUS` | `""` | JSON map of project prefix → target status |
+| `JIRA_AGENT_ASSIGNEE` | `""` | Display name for assignee lookup |
+| `JIRA_AGENT_UPSTREAM_INSTALLATION_ID_KEY` | `o-h-installation-id` | Vault key for upstream GH App installation ID |
+| `JIRA_AGENT_FORK_INSTALLATION_ID_KEY` | `installation-id` | Vault key for fork GH App installation ID |
+| `JIRA_AGENT_EXTRA_PLUGIN_COMMANDS` | `""` | Newline-separated Claude plugin install commands |
+| `JIRA_AGENT_TOOL_SETUP_SCRIPT` | `""` | Inline shell for project-specific tool installs |
+| `JIRA_AGENT_REVIEW_LANGUAGE` | `go` | Language for code-review plugin |
+| `JIRA_AGENT_REVIEW_PROFILE` | `""` | Profile for code-review plugin |
+| `JIRA_AGENT_SLACK_EMOJI` | `:robot:` | Slack emoji for notifications |
+| `JIRA_AGENT_MAX_ISSUES` | `1` | Max issues per run |
+| `JIRA_AGENT_ISSUE_KEY` | `""` | Override to process a specific issue |
+| `CLAUDE_MODEL` | `claude-opus-4-6` | Claude model to use |
+| `JIRA_BASE_URL` | `https://redhat.atlassian.net` | Jira instance base URL |
+
+## Credentials
+
+Each team needs a Vault secret containing:
+- `claude-prow` — GCP service account JSON for Vertex AI
+- `jira-pat` — Jira API token (Basic auth)
+- `jira-email` — Jira account email
+- `app-id` — GitHub App ID
+- `private-key` — GitHub App private key (PEM)
+- `installation-id` — Fork GitHub App installation ID (key name configurable via `JIRA_AGENT_FORK_INSTALLATION_ID_KEY`)
+- Upstream installation ID (key name configurable via `JIRA_AGENT_UPSTREAM_INSTALLATION_ID_KEY`, default: `o-h-installation-id`)
+- `slack-webhook-url` — Slack incoming webhook URL
+- `gh-to-slack-ids` — JSON mapping of GitHub usernames to Slack user IDs (optional)
+
+See the onboarding guide in `openshift/hypershift` docs for full setup instructions.
diff --git a/ci-operator/step-registry/hypershift/jira-agent/setup/hypershift-jira-agent-setup-ref.metadata.json b/ci-operator/step-registry/jira-agent/jira-agent-workflow.metadata.json
similarity index 72%
rename from ci-operator/step-registry/hypershift/jira-agent/setup/hypershift-jira-agent-setup-ref.metadata.json
rename to ci-operator/step-registry/jira-agent/jira-agent-workflow.metadata.json
index 59e74a6fdf8fb..9c077f1c492e8 100644
--- a/ci-operator/step-registry/hypershift/jira-agent/setup/hypershift-jira-agent-setup-ref.metadata.json
+++ b/ci-operator/step-registry/jira-agent/jira-agent-workflow.metadata.json
@@ -1,5 +1,5 @@
{
- "path": "hypershift/jira-agent/setup/hypershift-jira-agent-setup-ref.yaml",
+ "path": "jira-agent/jira-agent-workflow.yaml",
"owners": {
"approvers": [
"bryan-cox",
diff --git a/ci-operator/step-registry/jira-agent/jira-agent-workflow.yaml b/ci-operator/step-registry/jira-agent/jira-agent-workflow.yaml
new file mode 100644
index 0000000000000..2f58105a30105
--- /dev/null
+++ b/ci-operator/step-registry/jira-agent/jira-agent-workflow.yaml
@@ -0,0 +1,23 @@
+workflow:
+ as: jira-agent
+ steps:
+ pre:
+ - ref: jira-agent-setup
+ test:
+ - ref: jira-agent-process
+ post:
+ - ref: jira-agent-report
+ documentation: |-
+ Generic Jira Agent workflow for automated issue processing.
+
+ This workflow runs a four-phase pipeline for each matching Jira issue:
+ 1. Setup: Verifies Claude Code CLI is available
+ 2. Process: Solve, review, fix, and create PR for each issue
+ 3. Report: Generates HTML report with token usage and cost breakdown
+
+ Required env vars (set in your team's wrapper workflow):
+ - JIRA_AGENT_FORK_REPO: Fork repo (org/repo) for pushing branches
+ - JIRA_AGENT_UPSTREAM_REPO: Upstream repo (org/repo) for creating PRs
+ - JIRA_AGENT_JQL: JQL query for finding issues to process
+
+ See the onboarding guide in openshift-eng/ai-helpers for full configuration reference.
diff --git a/ci-operator/step-registry/jira-agent/process/OWNERS b/ci-operator/step-registry/jira-agent/process/OWNERS
new file mode 100644
index 0000000000000..ff943340794d2
--- /dev/null
+++ b/ci-operator/step-registry/jira-agent/process/OWNERS
@@ -0,0 +1,12 @@
+approvers:
+ - bryan-cox
+ - csrwng
+ - celebdor
+ - enxebre
+ - sjenning
+reviewers:
+ - bryan-cox
+ - csrwng
+ - celebdor
+ - enxebre
+ - sjenning
diff --git a/ci-operator/step-registry/hypershift/jira-agent/process/hypershift-jira-agent-process-commands.sh b/ci-operator/step-registry/jira-agent/process/jira-agent-process-commands.sh
old mode 100755
new mode 100644
similarity index 74%
rename from ci-operator/step-registry/hypershift/jira-agent/process/hypershift-jira-agent-process-commands.sh
rename to ci-operator/step-registry/jira-agent/process/jira-agent-process-commands.sh
index fee7b43f3bec0..2def314a3a2dc
--- a/ci-operator/step-registry/hypershift/jira-agent/process/hypershift-jira-agent-process-commands.sh
+++ b/ci-operator/step-registry/jira-agent/process/jira-agent-process-commands.sh
@@ -1,7 +1,7 @@
#!/bin/bash
set -euo pipefail
-echo "=== HyperShift Jira Agent Process ==="
+echo "=== Jira Agent Process ==="
# Apply Gangway API overrides (MULTISTAGE_PARAM_OVERRIDE_* prefix)
if [[ -n "${MULTISTAGE_PARAM_OVERRIDE_JIRA_AGENT_ISSUE_KEY:-}" ]]; then
@@ -9,6 +9,29 @@ if [[ -n "${MULTISTAGE_PARAM_OVERRIDE_JIRA_AGENT_ISSUE_KEY:-}" ]]; then
export JIRA_AGENT_ISSUE_KEY="${MULTISTAGE_PARAM_OVERRIDE_JIRA_AGENT_ISSUE_KEY}"
fi
+# Validate required env vars
+for required_var in JIRA_AGENT_FORK_REPO JIRA_AGENT_UPSTREAM_REPO; do
+ if [ -z "${!required_var:-}" ]; then
+ echo "ERROR: Required env var $required_var is not set"
+ exit 1
+ fi
+done
+if [ -z "${JIRA_AGENT_ISSUE_KEY:-}" ] && [ -z "${JIRA_AGENT_JQL:-}" ]; then
+ echo "ERROR: JIRA_AGENT_JQL must be set when JIRA_AGENT_ISSUE_KEY is not provided"
+ exit 1
+fi
+
+# Derive org name from fork repo slug
+FORK_ORG="${JIRA_AGENT_FORK_REPO%%/*}"
+
+# Configurable defaults
+UPSTREAM_INSTALL_ID_KEY="${JIRA_AGENT_UPSTREAM_INSTALLATION_ID_KEY:-o-h-installation-id}"
+FORK_INSTALL_ID_KEY="${JIRA_AGENT_FORK_INSTALLATION_ID_KEY:-installation-id}"
+REVIEW_LANGUAGE="${JIRA_AGENT_REVIEW_LANGUAGE:-go}"
+REVIEW_PROFILE="${JIRA_AGENT_REVIEW_PROFILE:-}"
+SLACK_EMOJI="${JIRA_AGENT_SLACK_EMOJI:-:robot:}"
+JIRA_BASE_URL="${JIRA_BASE_URL:-https://redhat.atlassian.net}"
+
# State file for sharing results with report step
STATE_FILE="${SHARED_DIR}/processed-issues.txt"
@@ -16,14 +39,14 @@ STATE_FILE="${SHARED_DIR}/processed-issues.txt"
echo "Cloning ai-helpers repository..."
git clone https://github.com/openshift-eng/ai-helpers /tmp/ai-helpers
-# Clone HyperShift fork (we push here and create PRs to upstream)
-echo "Cloning HyperShift repository..."
-git clone https://github.com/hypershift-community/hypershift /tmp/hypershift
+# Clone the project fork (we push here and create PRs to upstream)
+echo "Cloning ${JIRA_AGENT_FORK_REPO}..."
+git clone "https://github.com/${JIRA_AGENT_FORK_REPO}" /tmp/project-repo
-# Copy jira-solve command from ai-helpers to hypershift
+# Copy jira-solve command from ai-helpers to project repo
echo "Setting up Claude commands..."
-mkdir -p /tmp/hypershift/.claude/commands
-cp /tmp/ai-helpers/plugins/jira/commands/solve.md /tmp/hypershift/.claude/commands/jira-solve.md
+mkdir -p /tmp/project-repo/.claude/commands
+cp /tmp/ai-helpers/plugins/jira/commands/solve.md /tmp/project-repo/.claude/commands/jira-solve.md
# Check if code-review plugin is available for Phase 2
REVIEW_PLUGIN_DIR="/tmp/ai-helpers/plugins/code-review"
@@ -33,30 +56,40 @@ if [ ! -d "${REVIEW_PLUGIN_DIR}/.claude-plugin" ]; then
fi
echo "Code-review plugin found"
-# Install tool dependencies
-echo "Installing tool dependencies..."
-GOFLAGS="" go install golang.org/x/tools/gopls@v0.21.0
-python3.9 -m ensurepip --user 2>/dev/null || true
-python3.9 -m pip install --user pre-commit 2>&1 | tail -1
+# Install tool dependencies (project-specific)
+# Trust boundary: these env vars come from the workflow YAML authored by team members
+if [ -n "${JIRA_AGENT_TOOL_SETUP_SCRIPT:-}" ]; then
+ echo "Running project-specific tool setup..."
+ eval "$JIRA_AGENT_TOOL_SETUP_SCRIPT"
+fi
export PATH="${GOPATH:-$HOME/go}/bin:$HOME/.local/bin:$PATH"
+# Force HTTPS for plugin installs (claude CLI defaults to SSH which lacks host keys in CI)
+git config --global url."https://github.com/".insteadOf "git@github.com:"
+
# Install plugins
echo "Installing Claude Code plugins..."
claude plugin marketplace add openshift-eng/ai-helpers
-claude plugin install utils@ai-helpers
-claude plugin install golang@ai-helpers
-claude plugin marketplace add enxebre/ai-scripts
-claude plugin install git@enxebre
-cd /tmp/hypershift
+# Run any extra plugin setup commands
+if [ -n "${JIRA_AGENT_EXTRA_PLUGIN_COMMANDS:-}" ]; then
+ echo "Running extra plugin commands..."
+ while IFS= read -r cmd; do
+ if [ -n "$cmd" ]; then
+ eval "$cmd"
+ fi
+ done <<< "$JIRA_AGENT_EXTRA_PLUGIN_COMMANDS"
+fi
+
+cd /tmp/project-repo
# Configure git
git config user.name "OpenShift CI Bot"
git config user.email "ci-bot@redhat.com"
# Sync fork with upstream before doing any work
-echo "Syncing fork with upstream openshift/hypershift..."
-git remote add upstream https://github.com/openshift/hypershift.git
+echo "Syncing fork with upstream ${JIRA_AGENT_UPSTREAM_REPO}..."
+git remote add upstream "https://github.com/${JIRA_AGENT_UPSTREAM_REPO}.git"
git fetch upstream main
git checkout main
git rebase upstream/main
@@ -67,11 +100,11 @@ echo "Generating GitHub App token..."
GITHUB_APP_CREDS_DIR="/var/run/claude-code-service-account"
APP_ID_FILE="${GITHUB_APP_CREDS_DIR}/app-id"
-INSTALLATION_ID_FILE="${GITHUB_APP_CREDS_DIR}/installation-id"
+INSTALLATION_ID_FILE="${GITHUB_APP_CREDS_DIR}/${FORK_INSTALL_ID_KEY}"
PRIVATE_KEY_FILE="${GITHUB_APP_CREDS_DIR}/private-key"
# Check if all required credentials exist
-INSTALLATION_ID_UPSTREAM_FILE="${GITHUB_APP_CREDS_DIR}/o-h-installation-id"
+INSTALLATION_ID_UPSTREAM_FILE="${GITHUB_APP_CREDS_DIR}/${UPSTREAM_INSTALL_ID_KEY}"
if [ ! -f "$APP_ID_FILE" ] || [ ! -f "$INSTALLATION_ID_FILE" ] || [ ! -f "$PRIVATE_KEY_FILE" ] || [ ! -f "$INSTALLATION_ID_UPSTREAM_FILE" ]; then
echo "GitHub App credentials not yet available in ${GITHUB_APP_CREDS_DIR}"
@@ -80,14 +113,19 @@ if [ ! -f "$APP_ID_FILE" ] || [ ! -f "$INSTALLATION_ID_FILE" ] || [ ! -f "$PRIVA
echo ""
echo "Waiting for Vault secretsync to complete. The following keys are required:"
echo " - app-id"
- echo " - installation-id (for hypershift-community fork)"
- echo " - o-h-installation-id (for openshift/hypershift upstream)"
+ echo " - ${FORK_INSTALL_ID_KEY} (for ${FORK_ORG} fork)"
+ echo " - ${UPSTREAM_INSTALL_ID_KEY} (for ${JIRA_AGENT_UPSTREAM_REPO} upstream)"
echo " - private-key"
echo ""
- echo "Exiting gracefully. Re-run once secrets are synced."
- exit 0
+ echo "ERROR: Required credentials are missing. Re-run once secrets are synced."
+ echo "no_credentials" > "${SHARED_DIR}/processed-issues.txt"
+ exit 1
fi
+# Disable tracing for credential handling
+[[ $- == *x* ]] && _TOKEN_WAS_TRACING=true || _TOKEN_WAS_TRACING=false
+set +x
+
APP_ID=$(cat "$APP_ID_FILE")
INSTALLATION_ID_FORK=$(cat "$INSTALLATION_ID_FILE")
INSTALLATION_ID_UPSTREAM=$(cat "$INSTALLATION_ID_UPSTREAM_FILE")
@@ -115,32 +153,35 @@ generate_github_token() {
| jq -r '.token'
}
-# Generate token for fork (hypershift-community/hypershift) - for pushing branches
+# Generate token for fork - for pushing branches
echo "Generating GitHub App token for fork..."
GITHUB_TOKEN_FORK=$(generate_github_token "$INSTALLATION_ID_FORK")
if [ -z "$GITHUB_TOKEN_FORK" ] || [ "$GITHUB_TOKEN_FORK" = "null" ]; then
echo "ERROR: Failed to generate GitHub App token for fork"
+ $_TOKEN_WAS_TRACING && set -x
exit 1
fi
echo "Fork token generated successfully"
-# Generate token for upstream (openshift/hypershift) - for creating PRs
+# Generate token for upstream - for creating PRs
echo "Generating GitHub App token for upstream..."
GITHUB_TOKEN_UPSTREAM=$(generate_github_token "$INSTALLATION_ID_UPSTREAM")
if [ -z "$GITHUB_TOKEN_UPSTREAM" ] || [ "$GITHUB_TOKEN_UPSTREAM" = "null" ]; then
echo "ERROR: Failed to generate GitHub App token for upstream"
+ $_TOKEN_WAS_TRACING && set -x
exit 1
fi
echo "Upstream token generated successfully"
# Configure git to use the fork token for push operations via credential helper
-# Using credential helper instead of URL rewriting prevents token leaking in git remote output
git config --global credential.helper "!f() { echo username=x-access-token; echo password=${GITHUB_TOKEN_FORK}; }; f"
# Export upstream token as GITHUB_TOKEN for gh CLI (used for PR creation)
export GITHUB_TOKEN="$GITHUB_TOKEN_UPSTREAM"
echo "GitHub App tokens configured successfully"
+$_TOKEN_WAS_TRACING && set -x
+
# Configuration: maximum issues to process per run (default: 1)
MAX_ISSUES=${JIRA_AGENT_MAX_ISSUES:-1}
echo "Configuration: MAX_ISSUES=$MAX_ISSUES"
@@ -151,6 +192,8 @@ SUBAGENT_PROMPT="SUBAGENTS: Launch ALL subagents in parallel (single message wit
# Load Jira API credentials for Atlassian Cloud (Basic Auth: email:api-token)
JIRA_TOKEN_FILE="/var/run/claude-code-service-account/jira-pat"
JIRA_EMAIL_FILE="/var/run/claude-code-service-account/jira-email"
+[[ $- == *x* ]] && _JIRA_WAS_TRACING=true || _JIRA_WAS_TRACING=false
+set +x
if [ -f "$JIRA_TOKEN_FILE" ] && [ -f "$JIRA_EMAIL_FILE" ]; then
JIRA_TOKEN=$(cat "$JIRA_TOKEN_FILE")
JIRA_EMAIL=$(cat "$JIRA_EMAIL_FILE")
@@ -162,6 +205,7 @@ else
JIRA_TOKEN=""
JIRA_AUTH=""
fi
+$_JIRA_WAS_TRACING && set -x
# Load Slack webhook URL for notifications (tracing disabled to protect credential)
SLACK_WEBHOOK_FILE="/var/run/claude-code-service-account/slack-webhook-url"
@@ -208,7 +252,7 @@ transition_issue() {
# Get available transitions
TRANSITIONS=$(curl -s \
- "https://redhat.atlassian.net/rest/api/3/issue/$ISSUE_KEY/transitions" \
+ "${JIRA_BASE_URL}/rest/api/3/issue/$ISSUE_KEY/transitions" \
-H "Authorization: Basic $JIRA_AUTH" \
-H "Content-Type: application/json")
@@ -217,12 +261,16 @@ transition_issue() {
'.transitions[] | select(.name == $status) | .id' | head -1)
if [ -n "$TRANSITION_ID" ] && [ "$TRANSITION_ID" != "null" ]; then
- curl -s -X POST \
- "https://redhat.atlassian.net/rest/api/3/issue/$ISSUE_KEY/transitions" \
+ TRANSITION_HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" -X POST \
+ "${JIRA_BASE_URL}/rest/api/3/issue/$ISSUE_KEY/transitions" \
-H "Authorization: Basic $JIRA_AUTH" \
-H "Content-Type: application/json" \
- -d "{\"transition\":{\"id\":\"$TRANSITION_ID\"}}"
- return 0
+ -d "{\"transition\":{\"id\":\"$TRANSITION_ID\"}}")
+ if [ "$TRANSITION_HTTP_CODE" = "204" ] || [ "$TRANSITION_HTTP_CODE" = "200" ]; then
+ return 0
+ fi
+ echo " Warning: Jira transition API returned HTTP $TRANSITION_HTTP_CODE"
+ return 1
else
echo " Warning: Transition to '$TARGET_STATUS' not available"
return 1
@@ -235,7 +283,7 @@ set_assignee() {
local ACCOUNT_ID=$2
curl -s -w "\n%{http_code}" -X PUT \
- "https://redhat.atlassian.net/rest/api/3/issue/$ISSUE_KEY/assignee" \
+ "${JIRA_BASE_URL}/rest/api/3/issue/$ISSUE_KEY/assignee" \
-H "Authorization: Basic $JIRA_AUTH" \
-H "Content-Type: application/json" \
-d "{\"accountId\":\"$ACCOUNT_ID\"}"
@@ -259,7 +307,7 @@ send_slack_notification() {
while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do
local PR_DATA
- PR_DATA=$(gh pr view "$PR_NUM" --repo openshift/hypershift --json reviewRequests,title 2>/dev/null || echo "{}")
+ PR_DATA=$(gh pr view "$PR_NUM" --repo "${JIRA_AGENT_UPSTREAM_REPO}" --json reviewRequests,title 2>/dev/null || echo "{}")
PR_TITLE=$(echo "$PR_DATA" | jq -r '.title // empty' 2>/dev/null)
REVIEWERS=$(echo "$PR_DATA" | jq -r '.reviewRequests[]?.login // empty' 2>/dev/null)
if [ -n "$REVIEWERS" ]; then
@@ -302,8 +350,8 @@ send_slack_notification() {
# Send Slack message (tracing disabled to protect webhook URL)
local SLACK_PAYLOAD
- SLACK_PAYLOAD=$(jq -n --arg title "$PR_TITLE" --arg url "$PR_URL" --arg reviewers "$REVIEWER_MENTIONS" \
- '{text: ":hypershift-bot: *Jira Agent PR ready for review*\n:review: <\($url)|\($title)>\n:eyes: Reviewers: \($reviewers)"}')
+ SLACK_PAYLOAD=$(jq -n --arg title "$PR_TITLE" --arg url "$PR_URL" --arg reviewers "$REVIEWER_MENTIONS" --arg emoji "$SLACK_EMOJI" \
+ '{text: "\($emoji) *Jira Agent PR ready for review*\n:review: <\($url)|\($title)>\n:eyes: Reviewers: \($reviewers)"}')
[[ $- == *x* ]] && local _was_tracing=true || local _was_tracing=false
set +x
@@ -340,11 +388,11 @@ if [ -n "${JIRA_AGENT_ISSUE_KEY:-}" ]; then
echo "Using override: JIRA_AGENT_ISSUE_KEY=$JIRA_AGENT_ISSUE_KEY"
JQL="key = ${JIRA_AGENT_ISSUE_KEY}"
else
- JQL='project in (OCPBUGS, CNTRLPLANE) AND resolution = Unresolved AND status in (New, "To Do") AND labels = issue-for-agent AND labels != agent-processed'
+ JQL="$JIRA_AGENT_JQL"
fi
SEARCH_PAYLOAD=$(jq -n --arg jql "$JQL" --argjson max "$MAX_ISSUES" \
'{jql: $jql, fields: ["key", "summary"], maxResults: $max}')
-SEARCH_RESPONSE=$(curl -s -w "\n%{http_code}" "https://redhat.atlassian.net/rest/api/3/search/jql" \
+SEARCH_RESPONSE=$(curl -s -w "\n%{http_code}" "${JIRA_BASE_URL}/rest/api/3/search/jql" \
-X POST \
-H "Authorization: Basic $JIRA_AUTH" \
-H "Content-Type: application/json" \
@@ -396,7 +444,6 @@ while IFS= read -r line; do
echo "=========================================="
# Run jira-solve command non-interactively using --system-prompt
- # (Claude's -p mode doesn't support slash commands directly)
TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
echo "Running: jira-solve $ISSUE_KEY origin --ci"
@@ -404,11 +451,10 @@ while IFS= read -r line; do
PHASE1_START=$(date +%s)
# Load the skill content as system prompt
- SKILL_CONTENT=$(cat /tmp/hypershift/.claude/commands/jira-solve.md)
+ SKILL_CONTENT=$(cat /tmp/project-repo/.claude/commands/jira-solve.md)
# Additional context for fork-based workflow
- # Git push uses fork token (configured via credential helper), gh CLI uses upstream token (GITHUB_TOKEN env var)
- FORK_CONTEXT="IMPORTANT: You are working in a fork (hypershift-community/hypershift). Git push is pre-configured to work with the fork. After creating commits on your feature branch, push the branch to origin. Do NOT create a Pull Request - the PR will be created in a subsequent automated step after code review. SECURITY: Do NOT run commands that reveal git credentials like 'git remote -v' or 'git remote get-url origin'. ${SUBAGENT_PROMPT}"
+ FORK_CONTEXT="IMPORTANT: You are working in a fork (${JIRA_AGENT_FORK_REPO}). Git push is pre-configured to work with the fork. After creating commits on your feature branch, push the branch to origin. Do NOT create a Pull Request - the PR will be created in a subsequent automated step after code review. SECURITY: Do NOT run commands that reveal git credentials like 'git remote -v', 'git remote get-url origin', 'git config --list', 'git config --global credential.helper', or 'cat ~/.gitconfig'. ${SUBAGENT_PROMPT}"
set +e # Don't exit on error for individual issues
echo "Starting Claude processing with streaming output..."
@@ -450,7 +496,7 @@ while IFS= read -r line; do
echo "$PHASE1_DURATION" > "${SHARED_DIR}/claude-${ISSUE_KEY}-solve-duration.txt"
if [ $EXIT_CODE -eq 0 ]; then
- echo "✅ Phase 1 (jira-solve) completed for $ISSUE_KEY"
+ echo "Phase 1 (jira-solve) completed for $ISSUE_KEY"
# Check if code changes were made (branch changed from main)
BRANCH_NAME=$(git branch --show-current)
@@ -475,12 +521,15 @@ while IFS= read -r line; do
PHASE2_START=$(date +%s)
- REVIEW_PROMPT="/code-review:pre-commit-review --language go --profile hypershift"
+ REVIEW_PROMPT="/code-review:pre-commit-review --language ${REVIEW_LANGUAGE}"
+ if [ -n "$REVIEW_PROFILE" ]; then
+ REVIEW_PROMPT="${REVIEW_PROMPT} --profile ${REVIEW_PROFILE}"
+ fi
set +e
claude -p "$REVIEW_PROMPT" \
--plugin-dir "${REVIEW_PLUGIN_DIR}" \
- --append-system-prompt "SECURITY: Do NOT run commands that reveal git credentials like 'git remote -v' or 'git remote get-url origin'. ${SUBAGENT_PROMPT}" \
+ --append-system-prompt "SECURITY: Do NOT run commands that reveal git credentials like 'git remote -v', 'git remote get-url origin', 'git config --list', 'git config --global credential.helper', or 'cat ~/.gitconfig'. ${SUBAGENT_PROMPT}" \
--allowedTools "Bash Read Grep Glob Task" \
--max-turns 225 \
--effort max \
@@ -518,9 +567,9 @@ while IFS= read -r line; do
echo "$PHASE2_DURATION" > "${SHARED_DIR}/claude-${ISSUE_KEY}-review-duration.txt"
if [ $REVIEW_EXIT_CODE -eq 0 ]; then
- echo "✅ Phase 2 (pre-commit review) completed for $ISSUE_KEY"
+ echo "Phase 2 (pre-commit review) completed for $ISSUE_KEY"
else
- echo "⚠️ Phase 2 (pre-commit review) failed for $ISSUE_KEY (exit code: $REVIEW_EXIT_CODE)"
+ echo "Phase 2 (pre-commit review) failed for $ISSUE_KEY (exit code: $REVIEW_EXIT_CODE)"
echo "Continuing with PR creation despite review failure..."
fi
@@ -538,15 +587,18 @@ while IFS= read -r line; do
fi
# Refresh tokens before Phase 3 since it pushes code.
- # Phases 1-2 can exceed the 1-hour GitHub App token lifetime.
echo "Refreshing GitHub App tokens before Phase 3..."
- GITHUB_TOKEN_FORK=$(generate_github_token "$INSTALLATION_ID_FORK")
- if [ -z "$GITHUB_TOKEN_FORK" ] || [ "$GITHUB_TOKEN_FORK" = "null" ]; then
- echo "ERROR: Failed to refresh GitHub App token for fork"
- else
+ [[ $- == *x* ]] && _REFRESH3_TRACING=true || _REFRESH3_TRACING=false
+ set +x
+ if _NEW_TOKEN=$(generate_github_token "$INSTALLATION_ID_FORK") \
+ && [ -n "$_NEW_TOKEN" ] && [ "$_NEW_TOKEN" != "null" ]; then
+ GITHUB_TOKEN_FORK="$_NEW_TOKEN"
git config --global credential.helper "!f() { echo username=x-access-token; echo password=${GITHUB_TOKEN_FORK}; }; f"
echo "Fork token refreshed"
+ else
+ echo "ERROR: Failed to refresh GitHub App token for fork — continuing with previous token"
fi
+ $_REFRESH3_TRACING && set -x
PHASE3_START=$(date +%s)
@@ -561,7 +613,7 @@ IMPORTANT:
- Run 'make test' and 'make verify' after fixes to verify nothing is broken.
- If 'make verify' generates new files, commit those too and run 'make verify' again to confirm it passes.
- Commit all fixes and push to origin.
-- SECURITY: Do NOT run commands that reveal git credentials like 'git remote -v' or 'git remote get-url origin'.
+- SECURITY: Do NOT run commands that reveal git credentials like 'git remote -v', 'git remote get-url origin', 'git config --list', 'git config --global credential.helper', or 'cat ~/.gitconfig'.
- ${SUBAGENT_PROMPT}"
set +e
@@ -599,9 +651,9 @@ IMPORTANT:
echo "Phase 3 tokens: $(cat "${SHARED_DIR}/claude-${ISSUE_KEY}-fix-tokens.json")"
if [ $FIX_EXIT_CODE -eq 0 ]; then
- echo "✅ Phase 3 (address review) completed for $ISSUE_KEY"
+ echo "Phase 3 (address review) completed for $ISSUE_KEY"
else
- echo "⚠️ Phase 3 (address review) failed (exit code: $FIX_EXIT_CODE)"
+ echo "Phase 3 (address review) failed (exit code: $FIX_EXIT_CODE)"
echo "Continuing with PR creation..."
fi
else
@@ -614,24 +666,27 @@ IMPORTANT:
echo "$PHASE3_DURATION" > "${SHARED_DIR}/claude-${ISSUE_KEY}-fix-duration.txt"
# Regenerate GitHub App tokens before Phase 4.
- # Phase 3 may also have taken significant time, so refresh again
- # to ensure PR creation uses a valid token.
echo "Refreshing GitHub App tokens before Phase 4..."
- GITHUB_TOKEN_FORK=$(generate_github_token "$INSTALLATION_ID_FORK")
- if [ -z "$GITHUB_TOKEN_FORK" ] || [ "$GITHUB_TOKEN_FORK" = "null" ]; then
- echo "ERROR: Failed to refresh GitHub App token for fork"
- else
+ [[ $- == *x* ]] && _REFRESH4_TRACING=true || _REFRESH4_TRACING=false
+ set +x
+ if _NEW_TOKEN=$(generate_github_token "$INSTALLATION_ID_FORK") \
+ && [ -n "$_NEW_TOKEN" ] && [ "$_NEW_TOKEN" != "null" ]; then
+ GITHUB_TOKEN_FORK="$_NEW_TOKEN"
git config --global credential.helper "!f() { echo username=x-access-token; echo password=${GITHUB_TOKEN_FORK}; }; f"
echo "Fork token refreshed"
+ else
+ echo "ERROR: Failed to refresh GitHub App token for fork — continuing with previous token"
fi
- GITHUB_TOKEN_UPSTREAM=$(generate_github_token "$INSTALLATION_ID_UPSTREAM")
- if [ -z "$GITHUB_TOKEN_UPSTREAM" ] || [ "$GITHUB_TOKEN_UPSTREAM" = "null" ]; then
- echo "ERROR: Failed to refresh GitHub App token for upstream"
- else
+ if _NEW_TOKEN=$(generate_github_token "$INSTALLATION_ID_UPSTREAM") \
+ && [ -n "$_NEW_TOKEN" ] && [ "$_NEW_TOKEN" != "null" ]; then
+ GITHUB_TOKEN_UPSTREAM="$_NEW_TOKEN"
export GITHUB_TOKEN="$GITHUB_TOKEN_UPSTREAM"
echo "Upstream token refreshed"
+ else
+ echo "ERROR: Failed to refresh GitHub App token for upstream — continuing with previous token"
fi
+ $_REFRESH4_TRACING && set -x
# === Phase 4: Create Pull Request ===
echo ""
@@ -644,15 +699,15 @@ IMPORTANT:
PR_PROMPT="Create a pull request for the changes on branch '${BRANCH_NAME}'. Details:
- Jira issue: ${ISSUE_KEY}
- Jira summary: ${ISSUE_SUMMARY}
-- Jira URL: https://redhat.atlassian.net/browse/${ISSUE_KEY}
+- Jira URL: ${JIRA_BASE_URL}/browse/${ISSUE_KEY}
- Read the PR template at .github/PULL_REQUEST_TEMPLATE.md and use it to structure the PR body.
- Use 'git log main..HEAD' to understand what changed and write a meaningful description.
- PR title must start with '${ISSUE_KEY}: '.
- The PR body MUST end with the following two lines:
Always review AI generated responses prior to use.
Generated with [Claude Code](https://claude.com/claude-code) via \`/jira:solve ${ISSUE_KEY}\`
-- Create the PR by running: gh pr create --repo openshift/hypershift --head hypershift-community:${BRANCH_NAME} --no-maintainer-edit --title '