@cxi-lmai/ci-agent-platform 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +219 -0
- package/bin/init.mjs +236 -0
- package/package.json +47 -0
- package/payload/INSTALL.md +113 -0
- package/payload/agents/agent-architect.md +101 -0
- package/payload/agents/code-reviewer.md +87 -0
- package/payload/agents/codebase-auditor.md +73 -0
- package/payload/agents/coder.md +56 -0
- package/payload/agents/decomposer.md +70 -0
- package/payload/agents/docs-sync.md +115 -0
- package/payload/agents/e2e-test-writer.md +47 -0
- package/payload/agents/migration-reviewer.md +100 -0
- package/payload/agents/orchestrator.md +50 -0
- package/payload/agents/performance-reviewer.md +82 -0
- package/payload/agents/postmortem.md +83 -0
- package/payload/agents/release-mr.md +274 -0
- package/payload/agents/security-reviewer.md +122 -0
- package/payload/agents/test-fix.md +33 -0
- package/payload/agents/test-writer.md +40 -0
- package/payload/ci-templates/claude-pipeline.gitlab-ci.yml +233 -0
- package/payload/ci-templates/github/README.md +76 -0
- package/payload/ci-templates/github/claude-issue-pipeline.yml +141 -0
- package/payload/ci-templates/github/claude-pipeline.yml +141 -0
- package/payload/ci-templates/github/claude-test-fix.yml +104 -0
- package/payload/ci-templates/scripts/code.sh +114 -0
- package/payload/ci-templates/scripts/lib/issue-loop.sh +430 -0
- package/payload/ci-templates/scripts/lib/pipeline-common.sh +280 -0
- package/payload/ci-templates/scripts/lib/platform.sh +177 -0
- package/payload/ci-templates/scripts/lib/usage-capture.sh +110 -0
- package/payload/ci-templates/scripts/orchestrate.sh +294 -0
- package/payload/ci-templates/scripts/postmortem.sh +45 -0
- package/payload/ci-templates/scripts/review-fix.sh +90 -0
- package/payload/ci-templates/scripts/review.sh +93 -0
- package/payload/ci-templates/scripts/test-fix.sh +58 -0
- package/payload/skills/fix-review-findings/SKILL.md +79 -0
- package/payload/skills/fix-tests/SKILL.md +70 -0
- package/payload/skills/implement-issue/SKILL.md +62 -0
- package/payload/skills/init-pipeline-config/SKILL.md +96 -0
- package/payload/skills/postmortem-mr/SKILL.md +50 -0
- package/payload/skills/review-mr/SKILL.md +82 -0
- package/payload/skills/triage-issue/SKILL.md +74 -0
- package/payload/templates/pipeline-config.template.md +98 -0
- package/payload/templates/review_suppressions.template.md +25 -0
- package/payload/templates/spec-issue.template.md +64 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# Claude ci-agent-platform: GitHub Actions workflow (tier 1, review + review-fix)
|
|
3
|
+
# =============================================================================
|
|
4
|
+
# Copy this to .github/workflows/claude-pipeline.yml in your project. It mirrors
|
|
5
|
+
# the GitLab review and review-fix jobs. The test-fix job lives in a separate
|
|
6
|
+
# workflow (claude-test-fix.yml) because it hangs off YOUR test workflow.
|
|
7
|
+
#
|
|
8
|
+
# Configuration:
|
|
9
|
+
# - Repository/org VARIABLES (Settings > Variables): PIPE_TARGET_BRANCH,
|
|
10
|
+
# PIPE_LABEL_WIP, PIPE_LABEL_STUCK, PIPE_BOT_USER, PIPE_VERIFY_CMD, etc.
|
|
11
|
+
# Any PIPE_ variable overrides the defaults baked into the scripts.
|
|
12
|
+
# - SECRETS (Settings > Secrets): ANTHROPIC_API_KEY, and PIPE_BOT_TOKEN
|
|
13
|
+
# (a bot Personal Access Token). The default GITHUB_TOKEN cannot re-trigger
|
|
14
|
+
# workflows on its own push, so the fix loop needs a PAT to re-run review.
|
|
15
|
+
# - Vendor the ci-agent-platform repository's ci-templates/scripts/ into your
|
|
16
|
+
# repo (see its ci-templates/github/README.md), path set by
|
|
17
|
+
# PIPE_SCRIPTS_DIR below.
|
|
18
|
+
#
|
|
19
|
+
# on_failure mapping: GitLab's on_failure is `needs` + `if: failure()` here.
|
|
20
|
+
# =============================================================================
|
|
21
|
+
|
|
22
|
+
name: claude-pipeline
|
|
23
|
+
|
|
24
|
+
on:
|
|
25
|
+
pull_request:
|
|
26
|
+
types: [opened, synchronize, reopened, ready_for_review, labeled]
|
|
27
|
+
|
|
28
|
+
permissions:
|
|
29
|
+
contents: write
|
|
30
|
+
pull-requests: write
|
|
31
|
+
|
|
32
|
+
env:
|
|
33
|
+
PIPE_SCRIPTS_DIR: ".claude-pipeline/scripts"
|
|
34
|
+
PIPE_PLATFORM: "github"
|
|
35
|
+
PIPE_CONFIG_PATH: ".claude/pipeline-config.md"
|
|
36
|
+
PIPE_CONTEXT_DIR: "build/pipeline"
|
|
37
|
+
PIPE_TARGET_BRANCH: ${{ vars.PIPE_TARGET_BRANCH || github.event.repository.default_branch || 'main' }}
|
|
38
|
+
PIPE_LABEL_WIP: ${{ vars.PIPE_LABEL_WIP || 'pipe-wip' }}
|
|
39
|
+
PIPE_LABEL_STUCK: ${{ vars.PIPE_LABEL_STUCK || 'pipe-stuck' }}
|
|
40
|
+
PIPE_BOT_USER: ${{ vars.PIPE_BOT_USER || '' }}
|
|
41
|
+
PIPE_VERIFY_CMD: ${{ vars.PIPE_VERIFY_CMD || '' }}
|
|
42
|
+
PIPE_COMPILE_CMD: ${{ vars.PIPE_COMPILE_CMD || '' }}
|
|
43
|
+
PIPE_FIX_LOOP_CAP: ${{ vars.PIPE_FIX_LOOP_CAP || '2' }}
|
|
44
|
+
PIPE_COMMIT_REVIEWFIX: ${{ vars.PIPE_COMMIT_REVIEWFIX || 'Fix review findings' }}
|
|
45
|
+
PIPE_GIT_NAME: ${{ vars.PIPE_GIT_NAME || 'Pipeline Bot' }}
|
|
46
|
+
PIPE_GIT_EMAIL: ${{ vars.PIPE_GIT_EMAIL || 'bot@pipeline.ci' }}
|
|
47
|
+
PIPE_MODEL_REVIEW: ${{ vars.PIPE_MODEL_REVIEW || 'sonnet' }}
|
|
48
|
+
PIPE_MODEL_CODE: ${{ vars.PIPE_MODEL_CODE || 'sonnet' }}
|
|
49
|
+
PIPE_AGENT_ENV_ALLOWLIST: ${{ vars.PIPE_AGENT_ENV_ALLOWLIST || '' }}
|
|
50
|
+
# Runtime mapping onto the PIPE_ names the scripts read.
|
|
51
|
+
PIPE_REPO: ${{ github.repository }}
|
|
52
|
+
PIPE_PROJECT_PATH: ${{ github.repository }}
|
|
53
|
+
PIPE_SERVER_HOST: "github.com"
|
|
54
|
+
PIPE_MR_IID: ${{ github.event.pull_request.number }}
|
|
55
|
+
PIPE_SOURCE_BRANCH: ${{ github.event.pull_request.head.ref }}
|
|
56
|
+
PIPE_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
|
57
|
+
PIPE_PREV_HEAD_SHA: ${{ github.event.before }}
|
|
58
|
+
|
|
59
|
+
jobs:
|
|
60
|
+
review:
|
|
61
|
+
# Only non-draft PRs targeting the integration branch.
|
|
62
|
+
if: >-
|
|
63
|
+
github.event.pull_request.draft == false &&
|
|
64
|
+
github.event.pull_request.base.ref == (vars.PIPE_TARGET_BRANCH || github.event.repository.default_branch || 'main')
|
|
65
|
+
# One review at a time per PR (finding P5): a coder-created PR emits an
|
|
66
|
+
# `opened` event immediately followed by a `labeled` event (GitHub cannot
|
|
67
|
+
# attach labels at PR creation), which used to run two identical reviews.
|
|
68
|
+
# The newer event cancels the stale run. Job-level on purpose: review-fix
|
|
69
|
+
# must never be cancelled mid-push. The coder also creates its PRs
|
|
70
|
+
# draft-first (see _ghi_pr_create), so this is a second line of defence.
|
|
71
|
+
concurrency:
|
|
72
|
+
group: claude-review-pr-${{ github.event.pull_request.number }}
|
|
73
|
+
cancel-in-progress: true
|
|
74
|
+
runs-on: ubuntu-latest
|
|
75
|
+
# Hard wall-clock cap on API spend if a claude session hangs or loops.
|
|
76
|
+
timeout-minutes: 15
|
|
77
|
+
outputs:
|
|
78
|
+
has_bugs: ${{ steps.gate.outputs.has_bugs }}
|
|
79
|
+
steps:
|
|
80
|
+
- uses: actions/checkout@v4
|
|
81
|
+
with:
|
|
82
|
+
ref: ${{ github.event.pull_request.head.sha }}
|
|
83
|
+
fetch-depth: 0
|
|
84
|
+
token: ${{ secrets.PIPE_BOT_TOKEN || secrets.GITHUB_TOKEN }}
|
|
85
|
+
- name: Install claude-code
|
|
86
|
+
run: command -v claude >/dev/null 2>&1 || npm install -g @anthropic-ai/claude-code
|
|
87
|
+
- name: Run review
|
|
88
|
+
env:
|
|
89
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
90
|
+
GH_TOKEN: ${{ secrets.PIPE_BOT_TOKEN || secrets.GITHUB_TOKEN }}
|
|
91
|
+
PIPE_NO_GATE: "1" # the gate step below owns the exit code
|
|
92
|
+
run: bash "$PIPE_SCRIPTS_DIR/review.sh"
|
|
93
|
+
- name: Gate on the verdict
|
|
94
|
+
id: gate
|
|
95
|
+
if: always()
|
|
96
|
+
run: |
|
|
97
|
+
HAS_BUGS=$(grep -E '^REVIEW_HAS_BUGS=' "$PIPE_CONTEXT_DIR/review.env" | cut -d= -f2 || echo false)
|
|
98
|
+
echo "has_bugs=${HAS_BUGS:-false}" >> "$GITHUB_OUTPUT"
|
|
99
|
+
if [ "${HAS_BUGS:-false}" = "true" ]; then
|
|
100
|
+
echo "Review found confirmed bugs, failing the check"
|
|
101
|
+
exit 1
|
|
102
|
+
fi
|
|
103
|
+
- name: Upload metrics
|
|
104
|
+
if: always()
|
|
105
|
+
uses: actions/upload-artifact@v4
|
|
106
|
+
with:
|
|
107
|
+
name: review-metrics
|
|
108
|
+
path: ${{ env.PIPE_CONTEXT_DIR }}/metrics/
|
|
109
|
+
if-no-files-found: ignore
|
|
110
|
+
|
|
111
|
+
review-fix:
|
|
112
|
+
needs: review
|
|
113
|
+
# Runs when review flagged bugs (its job failed / output true) on a wip PR.
|
|
114
|
+
if: >-
|
|
115
|
+
always() &&
|
|
116
|
+
needs.review.outputs.has_bugs == 'true' &&
|
|
117
|
+
contains(github.event.pull_request.labels.*.name, vars.PIPE_LABEL_WIP || 'pipe-wip')
|
|
118
|
+
runs-on: ubuntu-latest
|
|
119
|
+
# Hard wall-clock cap on API spend if a claude session hangs or loops.
|
|
120
|
+
timeout-minutes: 20
|
|
121
|
+
steps:
|
|
122
|
+
- uses: actions/checkout@v4
|
|
123
|
+
with:
|
|
124
|
+
ref: ${{ github.event.pull_request.head.ref }}
|
|
125
|
+
fetch-depth: 0
|
|
126
|
+
token: ${{ secrets.PIPE_BOT_TOKEN || secrets.GITHUB_TOKEN }}
|
|
127
|
+
- name: Install claude-code
|
|
128
|
+
run: command -v claude >/dev/null 2>&1 || npm install -g @anthropic-ai/claude-code
|
|
129
|
+
- name: Run review-fix
|
|
130
|
+
env:
|
|
131
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
132
|
+
GH_TOKEN: ${{ secrets.PIPE_BOT_TOKEN || secrets.GITHUB_TOKEN }}
|
|
133
|
+
REVIEW_HAS_BUGS: "true"
|
|
134
|
+
run: bash "$PIPE_SCRIPTS_DIR/review-fix.sh"
|
|
135
|
+
- name: Upload metrics
|
|
136
|
+
if: always()
|
|
137
|
+
uses: actions/upload-artifact@v4
|
|
138
|
+
with:
|
|
139
|
+
name: review-fix-metrics
|
|
140
|
+
path: ${{ env.PIPE_CONTEXT_DIR }}/metrics/
|
|
141
|
+
if-no-files-found: ignore
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# Claude ci-agent-platform: GitHub Actions test-fix (tier 1)
|
|
3
|
+
# =============================================================================
|
|
4
|
+
# Copy to .github/workflows/claude-test-fix.yml. It hangs off YOUR test workflow:
|
|
5
|
+
# when that workflow fails on a pull request, this one resolves the PR, downloads
|
|
6
|
+
# the test artifacts, and runs the /fix-tests skill.
|
|
7
|
+
#
|
|
8
|
+
# REQUIRED EDIT: set `workflows:` below to the exact `name:` of your test
|
|
9
|
+
# workflow, and make that workflow upload its test reports / coverage signal as
|
|
10
|
+
# an artifact named `test-reports` (or change PIPE_TEST_ARTIFACT here).
|
|
11
|
+
#
|
|
12
|
+
# Alternative (simpler) wiring: instead of this workflow, add a test-fix JOB to
|
|
13
|
+
# your own test workflow with `needs: [test]` and `if: ${{ failure() }}`, then
|
|
14
|
+
# run the same script. Use that if you prefer everything in one workflow. See the
|
|
15
|
+
# github/README.md note. This workflow_run version keeps the ci-agent-platform
|
|
16
|
+
# repository's CI out of your test workflow file entirely.
|
|
17
|
+
#
|
|
18
|
+
# Limitation: github.event.workflow_run.pull_requests is only populated for
|
|
19
|
+
# same-repo PRs. Fork PRs will be skipped (this is a GitHub security boundary).
|
|
20
|
+
# =============================================================================
|
|
21
|
+
|
|
22
|
+
name: claude-test-fix
|
|
23
|
+
|
|
24
|
+
on:
|
|
25
|
+
workflow_run:
|
|
26
|
+
workflows: ["CI"] # <-- set to your test workflow's name
|
|
27
|
+
types: [completed]
|
|
28
|
+
|
|
29
|
+
permissions:
|
|
30
|
+
contents: write
|
|
31
|
+
pull-requests: write
|
|
32
|
+
actions: read
|
|
33
|
+
|
|
34
|
+
env:
|
|
35
|
+
PIPE_SCRIPTS_DIR: ".claude-pipeline/scripts"
|
|
36
|
+
PIPE_PLATFORM: "github"
|
|
37
|
+
PIPE_CONFIG_PATH: ".claude/pipeline-config.md"
|
|
38
|
+
PIPE_CONTEXT_DIR: "build/pipeline"
|
|
39
|
+
PIPE_TARGET_BRANCH: ${{ vars.PIPE_TARGET_BRANCH || github.event.repository.default_branch || 'main' }}
|
|
40
|
+
PIPE_LABEL_WIP: ${{ vars.PIPE_LABEL_WIP || 'pipe-wip' }}
|
|
41
|
+
PIPE_LABEL_STUCK: ${{ vars.PIPE_LABEL_STUCK || 'pipe-stuck' }}
|
|
42
|
+
PIPE_BOT_USER: ${{ vars.PIPE_BOT_USER || '' }}
|
|
43
|
+
PIPE_VERIFY_CMD: ${{ vars.PIPE_VERIFY_CMD || '' }}
|
|
44
|
+
PIPE_COMPILE_CMD: ${{ vars.PIPE_COMPILE_CMD || '' }}
|
|
45
|
+
PIPE_TEST_REPORT_GLOB: ${{ vars.PIPE_TEST_REPORT_GLOB || '' }}
|
|
46
|
+
PIPE_COVERAGE_SIGNAL: ${{ vars.PIPE_COVERAGE_SIGNAL || '' }}
|
|
47
|
+
PIPE_FIX_LOOP_CAP: ${{ vars.PIPE_FIX_LOOP_CAP || '2' }}
|
|
48
|
+
PIPE_COMMIT_TESTFIX: ${{ vars.PIPE_COMMIT_TESTFIX || 'Fix test errors' }}
|
|
49
|
+
PIPE_COMMIT_COVERAGE: ${{ vars.PIPE_COMMIT_COVERAGE || 'Add coverage tests' }}
|
|
50
|
+
PIPE_GIT_NAME: ${{ vars.PIPE_GIT_NAME || 'Pipeline Bot' }}
|
|
51
|
+
PIPE_GIT_EMAIL: ${{ vars.PIPE_GIT_EMAIL || 'bot@pipeline.ci' }}
|
|
52
|
+
PIPE_MODEL_CODE: ${{ vars.PIPE_MODEL_CODE || 'sonnet' }}
|
|
53
|
+
PIPE_AGENT_ENV_ALLOWLIST: ${{ vars.PIPE_AGENT_ENV_ALLOWLIST || '' }}
|
|
54
|
+
PIPE_TEST_ARTIFACT: ${{ vars.PIPE_TEST_ARTIFACT || 'test-reports' }}
|
|
55
|
+
PIPE_REPO: ${{ github.repository }}
|
|
56
|
+
PIPE_PROJECT_PATH: ${{ github.repository }}
|
|
57
|
+
PIPE_SERVER_HOST: "github.com"
|
|
58
|
+
PIPE_SOURCE_BRANCH: ${{ github.event.workflow_run.head_branch }}
|
|
59
|
+
PIPE_HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
|
|
60
|
+
|
|
61
|
+
jobs:
|
|
62
|
+
test-fix:
|
|
63
|
+
if: >-
|
|
64
|
+
github.event.workflow_run.conclusion == 'failure' &&
|
|
65
|
+
github.event.workflow_run.event == 'pull_request' &&
|
|
66
|
+
github.event.workflow_run.pull_requests[0] != null
|
|
67
|
+
runs-on: ubuntu-latest
|
|
68
|
+
# Hard wall-clock cap on API spend if a claude session hangs or loops.
|
|
69
|
+
timeout-minutes: 20
|
|
70
|
+
steps:
|
|
71
|
+
- name: Resolve the pull request
|
|
72
|
+
id: pr
|
|
73
|
+
env:
|
|
74
|
+
GH_TOKEN: ${{ secrets.PIPE_BOT_TOKEN || secrets.GITHUB_TOKEN }}
|
|
75
|
+
run: |
|
|
76
|
+
echo "number=${{ github.event.workflow_run.pull_requests[0].number }}" >> "$GITHUB_OUTPUT"
|
|
77
|
+
- uses: actions/checkout@v4
|
|
78
|
+
with:
|
|
79
|
+
ref: ${{ github.event.workflow_run.head_branch }}
|
|
80
|
+
fetch-depth: 0
|
|
81
|
+
token: ${{ secrets.PIPE_BOT_TOKEN || secrets.GITHUB_TOKEN }}
|
|
82
|
+
- name: Download test artifacts from the failed run
|
|
83
|
+
uses: actions/download-artifact@v4
|
|
84
|
+
with:
|
|
85
|
+
run-id: ${{ github.event.workflow_run.id }}
|
|
86
|
+
github-token: ${{ secrets.PIPE_BOT_TOKEN || secrets.GITHUB_TOKEN }}
|
|
87
|
+
name: ${{ env.PIPE_TEST_ARTIFACT }}
|
|
88
|
+
path: .
|
|
89
|
+
continue-on-error: true
|
|
90
|
+
- name: Install claude-code
|
|
91
|
+
run: command -v claude >/dev/null 2>&1 || npm install -g @anthropic-ai/claude-code
|
|
92
|
+
- name: Run test-fix
|
|
93
|
+
env:
|
|
94
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
95
|
+
GH_TOKEN: ${{ secrets.PIPE_BOT_TOKEN || secrets.GITHUB_TOKEN }}
|
|
96
|
+
PIPE_MR_IID: ${{ steps.pr.outputs.number }}
|
|
97
|
+
run: bash "$PIPE_SCRIPTS_DIR/test-fix.sh"
|
|
98
|
+
- name: Upload metrics
|
|
99
|
+
if: always()
|
|
100
|
+
uses: actions/upload-artifact@v4
|
|
101
|
+
with:
|
|
102
|
+
name: test-fix-metrics
|
|
103
|
+
path: ${{ env.PIPE_CONTEXT_DIR }}/metrics/
|
|
104
|
+
if-no-files-found: ignore
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Runner for the `code` job (issue -> code loop, coder side). Triggered by the
|
|
3
|
+
# orchestrator with CODER_ISSUE + CODER_BRANCH. Checks out the feature branch,
|
|
4
|
+
# runs the /implement-issue skill (which delegates to the coder agent and drafts
|
|
5
|
+
# the MR/PR body), then pushes and opens the MR/PR, or handles the no-commits
|
|
6
|
+
# case. All reasoning is in the skill; this script does token and git work.
|
|
7
|
+
#
|
|
8
|
+
# Generalized from a single-project original: the delegation prompt and the
|
|
9
|
+
# summary generation moved into the skill, platform calls go through
|
|
10
|
+
# lib/issue-loop.sh, and the coder runs non-root via pipe_run_claude.
|
|
11
|
+
#
|
|
12
|
+
# SECURITY NOTE: the coder agent runs with --dangerously-skip-permissions (it
|
|
13
|
+
# must edit files and run the build unattended). This is a deliberate
|
|
14
|
+
# choice for trusted inputs. Run only on an isolated ephemeral runner and, where
|
|
15
|
+
# the image allows, as an unprivileged user (see pipe_run_claude). This does not
|
|
16
|
+
# make arbitrary public issue or contributor content safe; see the README trust
|
|
17
|
+
# boundary. Do not run this job on a shared or privileged machine.
|
|
18
|
+
set -e
|
|
19
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
20
|
+
# shellcheck source=lib/pipeline-common.sh
|
|
21
|
+
source "$SCRIPT_DIR/lib/pipeline-common.sh"
|
|
22
|
+
# shellcheck source=lib/issue-loop.sh
|
|
23
|
+
source "$SCRIPT_DIR/lib/issue-loop.sh"
|
|
24
|
+
pipe_defaults
|
|
25
|
+
|
|
26
|
+
# --- 1. Validate inputs -----------------------------------------------------
|
|
27
|
+
[[ "${CODER_ISSUE:-}" =~ ^[0-9]+$ ]] || { echo "ERROR: CODER_ISSUE is not a valid issue number"; exit 1; }
|
|
28
|
+
[[ "${CODER_BRANCH:-}" =~ ^[a-z0-9][a-z0-9-]*$ ]] || { echo "ERROR: CODER_BRANCH contains invalid characters"; exit 1; }
|
|
29
|
+
export PIPE_ISSUE_IID="$CODER_ISSUE"
|
|
30
|
+
|
|
31
|
+
# --- 2. Git identity and feature branch -------------------------------------
|
|
32
|
+
pipe_git_identity
|
|
33
|
+
git fetch origin "$PIPE_TARGET_BRANCH"
|
|
34
|
+
git checkout -B "$CODER_BRANCH" "origin/$PIPE_TARGET_BRANCH"
|
|
35
|
+
|
|
36
|
+
# --- 3. Fetch the issue and build the coder input ---------------------------
|
|
37
|
+
META=$(issue_meta "$CODER_ISSUE")
|
|
38
|
+
ISSUE_TITLE=$(echo "$META" | jq -r '.title // ""')
|
|
39
|
+
ISSUE_DESC=$(echo "$META" | jq -r '.description // "(no description)"')
|
|
40
|
+
ISSUE_LABELS=$(echo "$META" | jq -r '.labels // ""')
|
|
41
|
+
pipe_log "Implementing #$CODER_ISSUE: $ISSUE_TITLE"
|
|
42
|
+
|
|
43
|
+
{
|
|
44
|
+
echo "# Issue #$CODER_ISSUE"
|
|
45
|
+
echo
|
|
46
|
+
echo "Title: $ISSUE_TITLE"
|
|
47
|
+
echo
|
|
48
|
+
echo "## Description"
|
|
49
|
+
echo "$ISSUE_DESC"
|
|
50
|
+
} > "$PIPE_CONTEXT_DIR/coder-input.md"
|
|
51
|
+
|
|
52
|
+
# --- 4. Run the implement-issue skill (delegates to the coder agent) --------
|
|
53
|
+
rm -f "$PIPE_CONTEXT_DIR/implement.env" "$PIPE_CONTEXT_DIR/mr-summary.md"
|
|
54
|
+
pipe_run_claude coder "/implement-issue" "Agent,Read,Write,Edit,Glob,Grep,Bash" "$PIPE_MODEL_CODE" < /dev/null
|
|
55
|
+
|
|
56
|
+
# --- 5. Authoritative commit count (the runner owns the push decision) -------
|
|
57
|
+
COMMITS=$(git rev-list "origin/$PIPE_TARGET_BRANCH..HEAD" --count 2>/dev/null || echo 0)
|
|
58
|
+
|
|
59
|
+
# --- 6. No commits: comment on the issue and mark it stuck ------------------
|
|
60
|
+
# issue_implemented is emitted only when commits exist (E2E finding N-7:
|
|
61
|
+
# emitting it before this check produced the metric for stuck runs too).
|
|
62
|
+
if [ "${COMMITS:-0}" = "0" ]; then
|
|
63
|
+
pipe_log "Coder made no commits, marking #$CODER_ISSUE stuck"
|
|
64
|
+
pipe_metric_event coder issue_stuck \
|
|
65
|
+
"$(jq -n --argjson i "$CODER_ISSUE" '{issue_iid: $i, reason: "no_commits"}')"
|
|
66
|
+
# The agent's own account of why it stopped is the most useful thing in a
|
|
67
|
+
# stuck comment, and it was being read and then dropped.
|
|
68
|
+
EXPL=$(head -c 3000 "$PIPE_CONTEXT_DIR/implement.env" 2>/dev/null || true)
|
|
69
|
+
issue_comment "$CODER_ISSUE" "🤖 **Coder agent made no commits for issue #$CODER_ISSUE.**
|
|
70
|
+
|
|
71
|
+
The implementation agent ran but produced no commits. A human needs to take over.
|
|
72
|
+
${EXPL:+
|
|
73
|
+
What the agent reported before stopping:
|
|
74
|
+
|
|
75
|
+
\`\`\`
|
|
76
|
+
$EXPL
|
|
77
|
+
\`\`\`
|
|
78
|
+
}
|
|
79
|
+
${PIPE_JOB_URL:+CI job: $PIPE_JOB_URL}"
|
|
80
|
+
issue_set_labels "$CODER_ISSUE" "$PIPE_LABEL_STUCK" "$PIPE_LABEL_READY,$PIPE_LABEL_WIP"
|
|
81
|
+
exit 0
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
pipe_metric_event coder issue_implemented \
|
|
85
|
+
"$(jq -n --argjson i "$CODER_ISSUE" --argjson c "${COMMITS:-0}" '{issue_iid: $i, commits: $c}')"
|
|
86
|
+
|
|
87
|
+
# --- 7. Push the branch -----------------------------------------------------
|
|
88
|
+
git push -u origin "$CODER_BRANCH"
|
|
89
|
+
|
|
90
|
+
# --- 8. Open the MR/PR ------------------------------------------------------
|
|
91
|
+
SUMMARY=$(head -c 6000 "$PIPE_CONTEXT_DIR/mr-summary.md" 2>/dev/null || true)
|
|
92
|
+
[ -z "$SUMMARY" ] && SUMMARY="Implemented by the ci-agent-platform coder agent."
|
|
93
|
+
|
|
94
|
+
# Inherit the issue labels onto the MR/PR: drop the ready label, add wip.
|
|
95
|
+
MR_LABELS=$( { echo "$ISSUE_LABELS" | tr ',' '\n' | grep -vxF "$PIPE_LABEL_READY" | grep -v '^$'; echo "$PIPE_LABEL_WIP"; } \
|
|
96
|
+
| awk '!seen[$0]++' | paste -sd, - )
|
|
97
|
+
|
|
98
|
+
MR_TITLE="Resolve \"$ISSUE_TITLE\""
|
|
99
|
+
MR_DESC="Closes #$CODER_ISSUE
|
|
100
|
+
|
|
101
|
+
$SUMMARY
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
🤖 Implemented by the ci-agent-platform coder"
|
|
105
|
+
|
|
106
|
+
# Ensure the MR/PR labels exist (repo/project may not have them yet).
|
|
107
|
+
issue_ensure_label "$PIPE_LABEL_WIP" "#3498DB" "Pipeline is working on this change"
|
|
108
|
+
|
|
109
|
+
MR_URL=$(mr_create "$CODER_BRANCH" "$PIPE_TARGET_BRANCH" "$MR_TITLE" "$MR_DESC" "$MR_LABELS")
|
|
110
|
+
if [ -n "$MR_URL" ]; then
|
|
111
|
+
pipe_log "Opened MR/PR: $MR_URL"
|
|
112
|
+
else
|
|
113
|
+
pipe_log "WARNING: MR/PR creation returned no URL (it may already exist for this branch)"
|
|
114
|
+
fi
|