@catladder/cli 0.0.0-main-a99d8000 → 0.0.0-main-bbb9c8e8

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.
@@ -5892,20 +5892,46 @@ __exportStar(__nccwpck_require__(2990), exports);
5892
5892
  "use strict";
5893
5893
 
5894
5894
  Object.defineProperty(exports, "__esModule", ({ value: true }));
5895
- exports.createAgentContext = void 0;
5896
- const createAgentContext = async (ctx) => {
5897
- var _a;
5898
- const agentConfig = (_a = ctx.config.agents) === null || _a === void 0 ? void 0 : _a[ctx.agentName];
5899
- if (!agentConfig) {
5900
- throw new Error(`Agent ${ctx.agentName} not found in config`);
5895
+ exports.createAgentContext = exports.AgentContext = void 0;
5896
+ class AgentContext {
5897
+ constructor(agentName, config) {
5898
+ this.agentName = agentName;
5899
+ this.config = config;
5900
+ this.type = "agent";
5901
5901
  }
5902
- return {
5903
- type: "agent",
5904
- name: ctx.agentName,
5905
- //env: ctx.env,
5906
- fullConfig: ctx.config,
5907
- agentConfig: agentConfig,
5908
- };
5902
+ get name() {
5903
+ return this.agentName;
5904
+ }
5905
+ get agentConfig() {
5906
+ var _a;
5907
+ const agentConfig = (_a = this.config.agents) === null || _a === void 0 ? void 0 : _a[this.agentName];
5908
+ if (!agentConfig) {
5909
+ throw new Error(`Agent ${this.agentName} not found in config`);
5910
+ }
5911
+ return agentConfig;
5912
+ }
5913
+ get agentUser() {
5914
+ var _a, _b, _c, _d;
5915
+ return {
5916
+ username: (_b = (_a = this.agentConfig.agentUser) === null || _a === void 0 ? void 0 : _a.username) !== null && _b !== void 0 ? _b : "agent.claude",
5917
+ userId: (_d = (_c = this.agentConfig.agentUser) === null || _c === void 0 ? void 0 : _c.userId) !== null && _d !== void 0 ? _d : "$DEFAULT_AGENT_USER_ID",
5918
+ };
5919
+ }
5920
+ get reviews() {
5921
+ var _a, _b;
5922
+ return {
5923
+ byUser: (_b = (_a = this.agentConfig.reviews) === null || _a === void 0 ? void 0 : _a.byUser) !== null && _b !== void 0 ? _b : {
5924
+ [this.agentUser.username]: { automatic: true },
5925
+ },
5926
+ };
5927
+ }
5928
+ get fullConfig() {
5929
+ return this.config;
5930
+ }
5931
+ }
5932
+ exports.AgentContext = AgentContext;
5933
+ const createAgentContext = async (ctx) => {
5934
+ return new AgentContext(ctx.agentName, ctx.config);
5909
5935
  };
5910
5936
  exports.createAgentContext = createAgentContext;
5911
5937
  //# sourceMappingURL=createAgentContext.js.map
@@ -5970,13 +5996,28 @@ const utils_1 = __nccwpck_require__(9133);
5970
5996
  const createAgentReviewJob = (context) => {
5971
5997
  const baseJob = (0, shared_1.createBaseAgentJob)(context);
5972
5998
  const agentUserName = (0, utils_1.getAgentUserName)(context);
5999
+ const rules = context.reviews.byUser === "all-automatic"
6000
+ ? [
6001
+ {
6002
+ ...rules_1.RULE_IS_MERGE_REQUEST,
6003
+ when: "always",
6004
+ },
6005
+ ]
6006
+ : Object.entries(context.reviews.byUser)
6007
+ .filter(([_, { automatic }]) => automatic)
6008
+ .map(([username]) => ({
6009
+ // GITLAB_USER_LOGIN is the username of the user who created the pipeline
6010
+ if: `${rules_1.RULE_IS_MERGE_REQUEST.if} && $GITLAB_USER_LOGIN == "${username}"`,
6011
+ when: "always",
6012
+ }));
5973
6013
  return {
5974
6014
  ...baseJob,
5975
6015
  name: context.name + "-agent-review",
5976
6016
  rules: [
6017
+ ...rules,
5977
6018
  {
5978
6019
  ...rules_1.RULE_IS_MERGE_REQUEST,
5979
- when: "always",
6020
+ when: "manual",
5980
6021
  },
5981
6022
  {
5982
6023
  when: "never",
@@ -6019,8 +6060,12 @@ exports.createJobsForAgentContext = createJobsForAgentContext;
6019
6060
 
6020
6061
  "use strict";
6021
6062
 
6063
+ // prompts.ts — MCP-only, DRY, review-first-then-push, CI logic, self-mention guard,
6064
+ // event prompt supports review-on-demand via manual "agent-review" job or fallback MR review.
6065
+ // Prevents double-runs: event-triggered work cancels any running "agent-review" job on the same MR.
6022
6066
  Object.defineProperty(exports, "__esModule", ({ value: true }));
6023
6067
  exports.getMergeRequestPrompt = exports.getEventPrompt = void 0;
6068
+ /* ---------- Shared blocks ---------- */
6024
6069
  const header = () => `
6025
6070
  Project ID: $CI_PROJECT_ID
6026
6071
  GitLab Host: $CI_SERVER_URL
@@ -6031,84 +6076,84 @@ const identity = ({ agentUserName }) => `
6031
6076
  `;
6032
6077
  const goldenRules = ({ agentUserName }) => `
6033
6078
  ## Golden Rules
6034
- - Use the \`gitlab-mcp\` tool for ALL GitLab actions. If a needed action is missing, use GitLab REST/GraphQL API directly as a fallback.
6035
- - NEVER mention yourself ("@${agentUserName}").
6079
+ - Use the \`gitlab-mcp\` tool for ALL GitLab actions. Do not call any other APIs.
6080
+ - If a needed \`gitlab-mcp\` capability is unavailable, post a short comment explaining the limitation and stop.
6081
+ - NEVER mention yourself ("@${agentUserName}") anywhere (comments, descriptions, titles, commit messages).
6036
6082
  - NEVER push to main/default or any protected branch. Always create a new branch and open a Merge Request (MR).
6083
+ - Always assign yourself as the assignee of any MR you create.
6037
6084
  - Do not create an MR for a **closed** issue.
6038
6085
  - Keep actions minimal and idempotent. Avoid duplicate comments or duplicate MRs.
6039
6086
  - Use ONE stable \`source_branch\` per run; do not regenerate its name later.
6040
6087
  `;
6088
+ const selfMentionGuard = ({ agentUserName }) => `
6089
+ ## Self-mention Guard (mandatory preflight for ALL writes)
6090
+ Before ANY call that writes text (comment/create/update MR/issue/commit message), sanitize the text:
6091
+
6092
+ - Remove all occurrences of your own handle:
6093
+ - Match case-insensitively: \`/@?${agentUserName}\\b/gi\`
6094
+ - Also strip variants inside parentheses or brackets if present.
6095
+ - Do NOT replace with another token; simply remove the self @-mention.
6096
+ - If after sanitization the body becomes empty/meaningless, skip the write.
6097
+
6098
+ Additionally:
6099
+ - If the last actor/author of the target item is you ("${agentUserName}"), **do not** post an acknowledgement comment (avoid loops on your own events).
6100
+ - To assign yourself, use the MCP assignee field(s). Do **not** mention yourself in the body to indicate assignment.
6101
+ `;
6041
6102
  const commentGuidelines = () => `
6042
6103
  ## Comment Guidelines (flexible, not verbatim)
6043
- - Keep tone professional, friendly, and concise.
6104
+ - Professional, friendly, concise.
6044
6105
  - Always @-mention the human author when replying; never mention yourself.
6045
6106
  - Acknowledgements: confirm you saw the request and you’ll handle it.
6046
6107
  - MR updates: acknowledge feedback and say you’ll apply/have applied the change.
6047
6108
  - Q&A: answer directly first; add context/links only if useful.
6048
6109
  - Avoid repeating identical boilerplate across comments.
6049
6110
  `;
6050
- const mcpAndApi = () => `
6051
- ## Tools & API (MCP-first, REST/GraphQL fallback)
6052
- Use these \`gitlab-mcp\` capabilities when available (names illustrative—match the actual tool schema):
6053
-
6054
- - **Comments**
6055
- - \`gitlab-mcp.comment.create({ project_id: $CI_PROJECT_ID, target: "issue"|"mr", iid, body })\`
6056
-
6057
- - **Branch**
6058
- - \`gitlab-mcp.branch.create({ project_id: $CI_PROJECT_ID, from: "<default_branch>", name: "<source_branch>" })\`
6059
-
6060
- - **Commits & push**
6061
- - \`gitlab-mcp.commit.push({ project_id: $CI_PROJECT_ID, branch: "<source_branch>", message, files: [{ path, content | patch }] })\`
6111
+ /* Exact tool names from @zereight/mcp-gitlab (lean, indicative signatures) */
6112
+ const mcpOnly = () => `
6113
+ ## gitlab-mcp Operations (exact tool names; indicative params)
6114
+
6115
+ - **Comments / Notes**
6116
+ - create_note({ projectId, targetType: "issue"|"merge_request", iid, body })
6117
+ - create_issue_note({ projectId, issueIid, body })
6118
+ - create_merge_request_note({ projectId, mergeRequestIid, body })
6119
+ - update_issue_note({ projectId, issueIid, noteId, body })
6120
+ - update_merge_request_note({ projectId, mergeRequestIid, noteId, body })
6121
+ - mr_discussions({ projectId, mergeRequestIid })
6122
+
6123
+ - **Issues**
6124
+ - create_issue({ projectId, title, description, assigneeUsernames?: string[] })
6125
+ - list_issues({ projectId, state?: "opened"|"closed", scope?: "all"|... })
6126
+
6127
+ - **Branch & Files**
6128
+ - create_branch({ projectId, branchName, ref }) // ref = default branch or SHA
6129
+ - push_files({ projectId, branch, commitMessage, files: [{ filePath, content }] })
6130
+ - create_or_update_file({ projectId, branch, filePath, content, commitMessage })
6131
+ - get_file_contents({ projectId, ref, path })
6132
+ - get_branch_diffs({ projectId, from, to }) // compare refs
6062
6133
 
6063
6134
  - **Merge Requests**
6064
- - \`gitlab-mcp.merge_request.create({ project_id: $CI_PROJECT_ID, source_branch, target_branch: "<default_branch>", title, description, assign_to_self: true })\`
6065
- - \`gitlab-mcp.merge_request.update({ project_id: $CI_PROJECT_ID, mr_iid, ... })\`
6066
- - \`gitlab-mcp.merge_request.rebase({ project_id: $CI_PROJECT_ID, mr_iid, onto: "<default_branch>" })\`
6067
-
6068
- - **Read/verify**
6069
- - \`gitlab-mcp.project.get({ project_id: $CI_PROJECT_ID })\` → default branch
6070
- - \`gitlab-mcp.repo.compare({ project_id: $CI_PROJECT_ID, from: "<default_branch>", to: "<source_branch>" })\`
6071
- - \`gitlab-mcp.repo.branch.get({ project_id: $CI_PROJECT_ID, name: "<source_branch>" })\`
6072
- - \`gitlab-mcp.repo.commits.list({ project_id: $CI_PROJECT_ID, ref_name: "<source_branch>", per_page: 1 })\`
6073
-
6074
- ### Fallback: Direct GitLab API
6075
- If MCP lacks an operation, call GitLab’s REST/GraphQL API directly.
6076
-
6077
- - **Authentication**
6078
- Use the environment variable \`GITLAB_PERSONAL_ACCESS_TOKEN\`.
6079
- Send it in the HTTP header:
6080
- \`\`\`
6081
- Private-Token: $GITLAB_PERSONAL_ACCESS_TOKEN
6082
- \`\`\`
6083
-
6084
- - **Host & project variables**
6085
- - API base URL: \`$CI_SERVER_URL/api/v4\`
6086
- - Project ID: \`$CI_PROJECT_ID\`
6087
-
6088
- - **Examples**
6089
- - Get project (default branch):
6090
- \`GET $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID\`
6091
- - Get/create branch:
6092
- \`GET|POST $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/repository/branches\`
6093
- - Compare refs:
6094
- \`GET $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/repository/compare?from=<default>&to=<source>\`
6095
- - List commits:
6096
- \`GET $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/repository/commits?ref_name=<branch>&per_page=1\`
6097
- - Create comment on issue/MR:
6098
- \`POST $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/issues/:iid/notes\`
6099
- \`POST $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/merge_requests/:iid/notes\`
6100
- - Create MR:
6101
- \`POST $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/merge_requests\`
6102
- - Get MR changes:
6103
- \`GET $CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/merge_requests/:iid/changes\`
6135
+ - create_merge_request({ projectId, sourceBranch, targetBranch, title, description, assigneeUsernames?: string[] })
6136
+ - get_merge_request({ projectId, mergeRequestIid? , branchName? })
6137
+ - get_merge_request_diffs({ projectId, mergeRequestIid? , branchName? })
6138
+ - list_merge_request_diffs({ projectId, mergeRequestIid? , branchName?, page?, perPage? })
6139
+ - update_merge_request({ projectId, mergeRequestIid? , branchName?, title?, description?, draft?, assigneeUsernames? })
6140
+ - merge_merge_request(...) // **Do NOT use** (never merge)
6141
+
6142
+ - **Pipelines / Jobs** (requires env USE_PIPELINE=true)
6143
+ - list_pipeline_jobs({ projectId, pipelineId })
6144
+ - get_pipeline_job_output({ projectId, pipelineId, jobId })
6145
+ - retry_pipeline({ projectId, pipelineId })
6146
+ - retry_pipeline_job({ projectId, jobId })
6147
+ - play_pipeline_job({ projectId, jobId })
6148
+ - cancel_pipeline_job({ projectId, jobId })
6104
6149
  `;
6105
6150
  const outputDiscipline = ({ agentUserName }) => `
6106
6151
  ## Output Discipline
6107
- - Prefer \`gitlab-mcp\` tool calls. If unavailable, output direct API requests (endpoint, method, headers, JSON body).
6152
+ - Output only \`gitlab-mcp\` tool calls and plain-text summaries where requested.
6108
6153
  - Keep comments concise and professional.
6109
6154
  - Never include "@${agentUserName}" in any body.
6110
6155
  `;
6111
- // --- Event-specific sections ---
6156
+ /* ---------- Event (webhook) specific ---------- */
6112
6157
  const eventSelfParse = () => `
6113
6158
  ## Self-Parse the Raw Payload (no preprocessing available)
6114
6159
  From \`event_json\`, extract:
@@ -6118,37 +6163,84 @@ From \`event_json\`, extract:
6118
6163
  - \`/-/merge_requests/<n>\` → target="mr", iid=<n>
6119
6164
  - note_id if present (\`#note_<id>\`)
6120
6165
  - description/body text, state, author \`user_username\`, timestamps
6121
- - project id/path; detect default branch via tool/API when needed
6166
+ - project id/path; detect default branch via \`get_merge_request\`/context as needed
6122
6167
 
6123
6168
  If any key is missing, choose the safest minimal action or briefly explain via a comment.
6124
6169
  `;
6125
- const eventWorkflow = () => `
6170
+ // NEW: Single-runner guard (event-triggered → existing MR)
6171
+ const singleRunnerGuard = () => `
6172
+ ## Single-Runner Guard (event-triggered work on an existing MR)
6173
+ Before entering MR Review Mode from an event:
6174
+
6175
+ - **Goal:** Avoid two agents working the same MR. If a **running or pending** CI job whose name **ends with "agent-review"** is active for this MR, **cancel** it first.
6176
+
6177
+ **Best-effort procedure (MCP-only):**
6178
+ 1) If \`$CI_PIPELINE_ID\` is available (this event is executing inside a CI context for the same MR):
6179
+ - Call \`list_pipeline_jobs({ projectId: $CI_PROJECT_ID, pipelineId: $CI_PIPELINE_ID })\`.
6180
+ - Identify any job where \`status\` is \`"running"\` or \`"pending"\` **and** \`name\` **endsWith** \`"agent-review"\`.
6181
+ - For each match, call \`cancel_pipeline_job({ projectId: $CI_PROJECT_ID, jobId })\`.
6182
+ - Proceed with review immediately after issuing cancellations (do not wait).
6183
+
6184
+ 2) If \`$CI_PIPELINE_ID\` is **not** available, or jobs for this MR cannot be listed with available MCP calls:
6185
+ - Post a short MR note stating you are proceeding but **cannot verify/cancel** a running \`agent-review\` job due to missing capabilities.
6186
+ - Proceed with review.
6187
+
6188
+ **Notes:**
6189
+ - Keep this guard **idempotent** (safe to run multiple times).
6190
+ - This guard only applies to **event-triggered** flows that decide to act on an **existing MR**.
6191
+ `;
6192
+ const reviewOnDemandFromEvents = () => `
6193
+ ## Review-on-Demand (from events)
6194
+ If the issue/note text **asks for a review** (case-insensitive tokens like: "review", "please review", "PTAL", "needs review", "can you look at", "LGTM?"), then:
6195
+
6196
+ 1) **Check for pipeline review job**
6197
+ - List jobs for the current pipeline \`$CI_PIPELINE_ID\` via \`list_pipeline_jobs\`.
6198
+ - If any job has \`status = "manual"\` **and** its \`name\` ends with "agent-review":
6199
+ - Trigger it via \`play_pipeline_job({ projectId: $CI_PROJECT_ID, jobId })\`.
6200
+ - Post a short comment confirming you triggered the review job (sanitize).
6201
+ - **Stop** further review actions.
6202
+
6203
+ 2) **If no such job exists, resolve which MR to review**:
6204
+ - If the event target is an MR → use its \`iid\`.
6205
+ - Else, parse the text for MR references in order:
6206
+ - \`!<iid>\` (e.g., \`!123\`)
6207
+ - \`/-/merge_requests/<iid>\` in a path or URL
6208
+ - full GitLab MR URL
6209
+ - If no MR can be resolved, reply with a brief comment asking the user to reference an MR (sanitize) and **stop**.
6210
+
6211
+ 3) **Single-Runner Guard (cancel any running "agent-review" job)** // NEW: Single-runner guard
6212
+ - Execute the **Single-Runner Guard** steps above **before** MR Review Mode.
6213
+
6214
+ 4) **Enter MR Review Mode**: execute the **MR Review Bundle** below with the resolved \`mr_iid\`.
6215
+ `;
6216
+ /** Regular event workflow for non-review work */
6217
+ const eventWorkflow = ({ agentUserName }) => `
6126
6218
  ## High-Reliability Workflow (sequence + postconditions)
6127
6219
  Follow this order for any change work:
6128
6220
 
6129
- 1) **Acknowledge** with a short comment on the issue/MR thread.
6130
- 2) **Discover default branch** (e.g., "main") via MCP or API.
6131
- 3) **Create a working branch** from default (stable name, e.g., \`fix/issue-<iid>-<slug>\` or \`feat/issue-<iid>-<slug>\`).
6132
- 4) **Write changes → commit → push to remote branch.**
6221
+ 1) **Acknowledge** with a short comment on the issue/MR thread (\`create_note\`), **unless the last actor is you**.
6222
+ 2) **Discover default branch** (e.g., "main") — infer from repo/MR context if needed.
6223
+ 3) **Create a working branch** from default (stable name, e.g., \`fix/issue-<iid>-<slug>\` or \`feat/issue-<iid>-<slug>\`) via \`create_branch\`.
6224
+ 4) **Write changes → commit → push to remote branch** via \`push_files\` (or \`create_or_update_file\`).
6133
6225
  5) **Verify push landed**:
6134
- - Fetch latest commit on \`source_branch\`; record its short SHA.
6135
- - Compare default vs \`source_branch\` and ensure \`diffs.length > 0\`.
6136
- 6) **Create or update MR** ONLY if there is a non-empty diff.
6226
+ - Fetch latest state (optional: \`get_file_contents\`/log) and capture a short SHA from the branch head if exposed by the host.
6227
+ - Compare default vs \`source_branch\` via \`get_branch_diffs({ from: "<default>", to: "<source>" })\` and ensure there are diffs.
6228
+ 6) **Create or update MR** ONLY if there is a non-empty diff via \`create_merge_request\`.
6137
6229
  - Include \`Closes #<issue_iid>\` in MR description when applicable.
6138
- - Assign yourself to the MR.
6139
- 7) **Follow-up comment** with branch name, commit short SHA, files changed count, and MR link.
6230
+ - **Assign the MR to yourself**: \`assigneeUsernames: ["${agentUserName}"]\`.
6231
+ 7) **Follow-up comment** with branch name, any commit short SHA you can obtain, files changed count (approx by diffs), and MR link via \`create_note\`, **unless the last actor is you**.
6140
6232
  8) **If verification fails**:
6141
6233
  - Do NOT create the MR.
6142
6234
  - Comment the exact failure and retry once with a fresh branch name. If still failing, comment and stop.
6143
6235
 
6144
- For Q&A-only (no code changes), just post a concise, helpful answer on the same issue/MR.
6236
+ For Q&A-only (no code changes), just post a concise, helpful answer on the same issue/MR (sanitize first).
6145
6237
  `;
6146
- // --- MR-specific sections ---
6238
+ /* ---------- MR-review specific (shared with both prompts) ---------- */
6147
6239
  const mrScope = ({ agentUserName }) => `
6148
6240
  ## Identity & Scope
6149
6241
  - Your GitLab username is "${agentUserName}".
6150
- - This prompt runs in the context of ONE MR (no webhook).
6151
- - You may review, comment, rebase, and push updates **to the MR's source branch**.
6242
+ - This prompt runs in the context of ONE MR.
6243
+ - You may review, comment, and push updates **to the MR's source branch**.
6152
6244
  - You must **never merge** the MR yourself.
6153
6245
  `;
6154
6246
  const mrWorkflow = () => `
@@ -6156,44 +6248,66 @@ const mrWorkflow = () => `
6156
6248
  Follow this sequence with verification at each step:
6157
6249
 
6158
6250
  1) **Collect context**
6159
- - Get MR metadata (source_branch, target_branch, state, draft/WIP).
6160
- - Fetch the full changeset/diffs and open discussions (notes, threads, unresolved discussions).
6161
- - Read existing reviews/comments to avoid duplication.
6162
- - (Optional) Fetch recent CI pipeline(s) for this MR SHA/branch).
6251
+ - Get MR metadata via \`get_merge_request({ projectId: $CI_PROJECT_ID, mergeRequestIid })\`.
6252
+ - Fetch the full changeset/diffs via \`get_merge_request_diffs\` (or \`list_merge_request_diffs\`) and open discussions via \`mr_discussions\`.
6253
+ - Read existing notes to avoid duplication.
6163
6254
 
6164
6255
  2) **Code review**
6165
6256
  - Identify required changes (bugs, tests, style, security, perf, docs).
6166
- - If no meaningful changes are needed:
6167
- - Post a concise review comment summarizing findings.
6168
- - Ask for review by a **recent active human contributor** (not you).
6169
-
6170
- 3) **If changes are needed**
6171
- - Post a short acknowledgment comment on the MR.
6172
- - **Rebase** the MR onto the target/default branch (resolve trivial conflicts).
6173
- - Implement minimal, safe changes; keep commits small and clear.
6174
- - **Push** to the MR's **source_branch**.
6175
- - **Verify push landed** (latest commit short SHA; compare target vs source shows diffs > 0).
6176
- - Comment summarizing what changed and why.
6177
-
6178
- 4) **CI pipeline**
6179
- - Check pipeline status for the new commit on the MR branch.
6180
- - Retry/re-run if allowed on flaky failures; fix minimal issues; push again if needed.
6181
- - If still failing, comment with failure summary and next steps.
6257
+ - Always **post your review comments first** using \`create_merge_request_note\` (ack + concrete notes). Sanitize before sending.
6258
+ - Set an internal intent flag:
6259
+ - \`will_push_changes = true\` if you will modify code/config.
6260
+ - \`will_push_changes = false\` if it’s commentary-only.
6261
+
6262
+ 3) **Implement changes after review is posted (only if \`will_push_changes = true\`)**
6263
+ - If needed, create the working branch from the target/default (or use existing MR source branch).
6264
+ - Apply minimal, safe changes; keep commits small and clear.
6265
+ - **Push** to the MR's **source branch** via \`push_files\` (or \`create_or_update_file\`).
6266
+ - **Verify push landed** using \`get_branch_diffs({ from: "<target_branch>", to: "<source_branch>" })\` and ensure there are diffs.
6267
+ - Post a follow-up MR note summarizing what changed and why (sanitize).
6268
+ `;
6269
+ const ciInspection = () => `
6270
+ 4) **CI jobs (current pipeline focus: diagnose first, retry only when useful)**
6271
+ - Inspect jobs for the **current pipeline**: \`$CI_PIPELINE_ID\` via \`list_pipeline_jobs\`.
6272
+ - Consider **only** jobs with \`status = "failed"\` and \`allow_failure = false\`.
6273
+ - For each such job:
6274
+ 1. Retrieve details (id, name, stage, status, allow_failure, web_url).
6275
+ 2. Fetch job output via \`get_pipeline_job_output({ projectId: $CI_PROJECT_ID, pipelineId: $CI_PIPELINE_ID, jobId })\`.
6276
+ 3. **Classify the failure**:
6277
+ - **Code-related (do not retry):** compiler/type/lint/test/build script errors.
6278
+ - **Likely transient (may retry):** network/timeouts/infra/cache/artifacts/5xx/429/etc.
6279
+ 4. **Decision**:
6280
+ - If \`will_push_changes = true\`:
6281
+ - **Do not retry** current pipeline (upcoming push will trigger a new one).
6282
+ - Post an MR note: brief diagnosis per failed job; note a new pipeline will validate the fix (sanitize).
6283
+ - If \`will_push_changes = false\`:
6284
+ - If transient ⇒ \`retry_pipeline_job({ projectId: $CI_PROJECT_ID, jobId })\` (or \`retry_pipeline\` if job-level retry not available).
6285
+ Post a note stating you retried and why (sanitize).
6286
+ - If code-related ⇒ do not retry; post a note with diagnosis and suggested fix (sanitize).
6287
+ - Retry-once policy: at most **one** retry per job in this run.
6182
6288
 
6183
6289
  5) **Assign human reviewer if ready**
6184
- - If discussions are resolved and CI is passing (or running), request review from a recent active human contributor (not you).
6290
+ - If discussions are resolved and blocking CI issues are addressed or clearly triaged, request review from a recent active human contributor (not you), if supported by your environment.
6185
6291
 
6186
6292
  6) **Stdout summary**
6187
- - Print concise summary: commits pushed (short SHAs), files changed count, discussions resolved/left, CI status, and requested reviewers.
6293
+ - Print concise summary: branch used, files changed count (approx by diffs), discussions resolved/left, **blocking failed jobs (names + stages)** with classification (code vs transient), which jobs were retried (if any), and requested reviewers.
6188
6294
  `;
6189
- const fallbackApiAuth = () => `
6190
- ## Fallback API Auth (if MCP lacks a method)
6191
- - Base URL: \`$CI_SERVER_URL/api/v4\`
6192
- - Project: \`$CI_PROJECT_ID\`
6193
- - Header: \`Private-Token: $GITLAB_PERSONAL_ACCESS_TOKEN\`
6295
+ const outputDisciplineMR = ({ agentUserName }) => `
6296
+ ## Output Discipline (MR)
6297
+ - Output only \`gitlab-mcp\` tool calls and the final plain-text summary.
6298
+ - Do **not** merge the MR yourself under any circumstance.
6299
+ - Never include "@${agentUserName}" in any body.
6194
6300
  `;
6195
- // ---------- Public builders ----------
6196
- const getEventPrompt = ({ agentUserName }) => `
6301
+ /* ---------- Shared bundle for MR review ---------- */
6302
+ const mrReviewBundle = (ctx) => `
6303
+ ## MR Review Mode (execute ONLY when review intent is detected and an MR IID is resolved)
6304
+ Resolved MR IID: <set this to the resolved \`mr_iid\` before executing>
6305
+ ${mrScope(ctx)}
6306
+ ${mrWorkflow()}
6307
+ ${ciInspection()}
6308
+ `;
6309
+ /* ---------- Public builders ---------- */
6310
+ const getEventPrompt = (ctx) => `
6197
6311
  You are a GitLab assistant bot. You receive ONE raw GitLab webhook JSON payload.
6198
6312
 
6199
6313
  ${header()}
@@ -6202,16 +6316,20 @@ event_json:
6202
6316
  $(cat $TRIGGER_PAYLOAD)
6203
6317
  ---
6204
6318
 
6205
- ${identity({ agentUserName })}
6206
- ${goldenRules({ agentUserName })}
6319
+ ${identity(ctx)}
6320
+ ${goldenRules(ctx)}
6321
+ ${selfMentionGuard(ctx)}
6207
6322
  ${eventSelfParse()}
6208
- ${eventWorkflow()}
6323
+ ${singleRunnerGuard()} <!-- NEW: included so the agent can run it when acting on an existing MR -->
6324
+ ${reviewOnDemandFromEvents()}
6325
+ ${mrReviewBundle(ctx)} <!-- Included so the agent can execute it when review intent is true -->
6326
+ ${eventWorkflow(ctx)}
6209
6327
  ${commentGuidelines()}
6210
- ${mcpAndApi()}
6211
- ${outputDiscipline({ agentUserName })}
6328
+ ${mcpOnly()}
6329
+ ${outputDiscipline(ctx)}
6212
6330
  `;
6213
6331
  exports.getEventPrompt = getEventPrompt;
6214
- const getMergeRequestPrompt = ({ agentUserName }) => `
6332
+ const getMergeRequestPrompt = (ctx) => `
6215
6333
  You are a GitLab assistant bot reviewing and updating a single Merge Request (MR).
6216
6334
 
6217
6335
  ${header()}
@@ -6221,21 +6339,14 @@ title: $CI_MERGE_REQUEST_TITLE
6221
6339
  description: $CI_MERGE_REQUEST_DESCRIPTION
6222
6340
  ---
6223
6341
 
6224
- ${mrScope({ agentUserName })}
6225
- ${goldenRules({ agentUserName })}
6342
+ ${mrScope(ctx)}
6343
+ ${goldenRules(ctx)}
6344
+ ${selfMentionGuard(ctx)}
6226
6345
  ${mrWorkflow()}
6346
+ ${ciInspection()}
6227
6347
  ${commentGuidelines()}
6228
- ${mcpAndApi()}
6229
- ${fallbackApiAuth()}
6230
- ## Output Discipline (MR)
6231
- - Prefer \`gitlab-mcp\` tool calls; if unavailable, provide explicit REST calls (method, url, headers, body).
6232
- - At the end, print a **plain-text** summary to STDOUT including:
6233
- - \`source_branch\` and \`target_branch\`
6234
- - commits pushed (short SHAs)
6235
- - number of files changed
6236
- - CI status/result
6237
- - reviewers requested (if any)
6238
- - Do **not** merge the MR yourself under any circumstance.
6348
+ ${mcpOnly()}
6349
+ ${outputDisciplineMR(ctx)}
6239
6350
  `;
6240
6351
  exports.getMergeRequestPrompt = getMergeRequestPrompt;
6241
6352
  //# sourceMappingURL=prompts.js.map
@@ -6250,10 +6361,12 @@ exports.getMergeRequestPrompt = getMergeRequestPrompt;
6250
6361
  Object.defineProperty(exports, "__esModule", ({ value: true }));
6251
6362
  exports.callClaude = exports.baseSetupScript = exports.createBaseAgentJob = void 0;
6252
6363
  const bashEscape_1 = __nccwpck_require__(2463);
6364
+ const runner_1 = __nccwpck_require__(4058);
6253
6365
  const createBaseAgentJob = (context) => ({
6254
6366
  stage: "agents",
6255
6367
  envMode: "none",
6256
- image: "node:24-alpine3.21",
6368
+ // image: "node:24-alpine3.21",
6369
+ image: (0, runner_1.getRunnerImage)("agent-claude"),
6257
6370
  variables: {
6258
6371
  MAX_MCP_OUTPUT_TOKENS: "75000",
6259
6372
  GITLAB_PERSONAL_ACCESS_TOKEN: "$AGENT_GITLAB_PERSONAL_ACCESS_TOKEN", // TODO: we don't have global secret keys to configure yet
@@ -6262,16 +6375,17 @@ const createBaseAgentJob = (context) => ({
6262
6375
  });
6263
6376
  exports.createBaseAgentJob = createBaseAgentJob;
6264
6377
  exports.baseSetupScript = [
6265
- "apk update",
6266
- "apk add --no-cache git curl bash",
6267
- "npm install -g @anthropic-ai/claude-code",
6378
+ // these are done in the image already
6379
+ // "apk update",
6380
+ // "apk add --no-cache git curl bash",
6381
+ //"npm install -g @anthropic-ai/claude-code",
6268
6382
  "claude mcp add gitlab --env GITLAB_PERSONAL_ACCESS_TOKEN=$GITLAB_PERSONAL_ACCESS_TOKEN --env GITLAB_API_URL=$GITLAB_API_URL --env USE_PIPELINE='true' -- npx -y @zereight/mcp-gitlab",
6269
6383
  ];
6270
6384
  const callClaude = ({ prompt }) => {
6271
6385
  return [
6272
6386
  `export PROMPT="${(0, bashEscape_1.escapeNewlines)((0, bashEscape_1.escapeDoubleQuotes)((0, bashEscape_1.escapeBackTicks)(prompt)))}"`,
6273
6387
  //'echo "$PROMPT"',
6274
- `claude -p "$PROMPT" --permission-mode acceptEdits --allowedTools "Bash(*) Read(*) Edit(*) Write(*) mcp__gitlab" --verbose --debug`,
6388
+ `claude -p "$PROMPT" --permission-mode acceptEdits --allowedTools "Bash(*) Bash(git checkout:*) Read(*) Edit(*) Write(*) mcp__gitlab" --verbose --debug`,
6275
6389
  ];
6276
6390
  };
6277
6391
  exports.callClaude = callClaude;