pipeline-worker 0.1.24 → 0.1.26

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.
Files changed (50) hide show
  1. package/README.md +12 -2
  2. package/dist/cli.js +23 -8
  3. package/dist/cli.js.map +1 -1
  4. package/dist/config/loader.js +7 -0
  5. package/dist/config/loader.js.map +1 -1
  6. package/dist/forge/github.d.ts +8 -0
  7. package/dist/forge/github.js +36 -0
  8. package/dist/forge/github.js.map +1 -1
  9. package/dist/forge/gitlab.js +20 -0
  10. package/dist/forge/gitlab.js.map +1 -1
  11. package/dist/forge/shared.d.ts +22 -2
  12. package/dist/forge/shared.js +61 -13
  13. package/dist/forge/shared.js.map +1 -1
  14. package/dist/forge/types.d.ts +21 -1
  15. package/dist/git/commit.d.ts +18 -0
  16. package/dist/git/commit.js +23 -0
  17. package/dist/git/commit.js.map +1 -1
  18. package/dist/git/diff.d.ts +8 -0
  19. package/dist/git/diff.js +11 -0
  20. package/dist/git/diff.js.map +1 -1
  21. package/dist/git/remote.d.ts +13 -0
  22. package/dist/git/remote.js +37 -1
  23. package/dist/git/remote.js.map +1 -1
  24. package/dist/git/squash.d.ts +8 -0
  25. package/dist/git/squash.js +26 -0
  26. package/dist/git/squash.js.map +1 -0
  27. package/dist/state/runState.js +9 -1
  28. package/dist/state/runState.js.map +1 -1
  29. package/dist/types.d.ts +23 -1
  30. package/dist/ui/sessions.js +4 -3
  31. package/dist/ui/sessions.js.map +1 -1
  32. package/dist/ui/welcome.d.ts +1 -1
  33. package/dist/ui/welcome.js +23 -14
  34. package/dist/ui/welcome.js.map +1 -1
  35. package/dist/workflow/adoptBranch.d.ts +21 -0
  36. package/dist/workflow/adoptBranch.js +90 -0
  37. package/dist/workflow/adoptBranch.js.map +1 -0
  38. package/dist/workflow/captureIntent.d.ts +7 -1
  39. package/dist/workflow/captureIntent.js +8 -2
  40. package/dist/workflow/captureIntent.js.map +1 -1
  41. package/dist/workflow/openMergeRequest.d.ts +2 -2
  42. package/dist/workflow/openMergeRequest.js +35 -3
  43. package/dist/workflow/openMergeRequest.js.map +1 -1
  44. package/dist/workflow/orchestrate.d.ts +13 -1
  45. package/dist/workflow/orchestrate.js +43 -9
  46. package/dist/workflow/orchestrate.js.map +1 -1
  47. package/dist/workflow/watchPipeline.d.ts +48 -5
  48. package/dist/workflow/watchPipeline.js +149 -62
  49. package/dist/workflow/watchPipeline.js.map +1 -1
  50. package/package.json +1 -1
@@ -1,8 +1,16 @@
1
1
  /** Stages 10-11: push the branch and open (or reuse) an MR/PR describing the workflow that produced it. */
2
2
  import { push } from '../git/commit.js';
3
- import { runStep, skipStep, note } from '../ui/steps.js';
4
- /** ✅/❌ per check, mirroring the pass/fail glyphs cli.ts prints for the same stage (see ui/steps.ts). */
3
+ import { runStep, skipStep, note, noteRisk } from '../ui/steps.js';
4
+ /**
5
+ * ✅/❌ per check, mirroring the pass/fail glyphs cli.ts prints for the same
6
+ * stage (see ui/steps.ts). An empty list means the `resume` branch-adoption
7
+ * path is refreshing an existing MR/PR's description without re-running
8
+ * local checks (see adoptBranch.ts) — that's not a check that failed to run,
9
+ * so it gets its own placeholder rather than an empty section.
10
+ */
5
11
  function formatChecks(checks) {
12
+ if (checks.length === 0)
13
+ return '_Not run locally for this update — see the CI pipeline below._';
6
14
  return checks.map((c) => `- ${c.ok ? '✅' : '❌'} ${c.name} (${(c.durationMs / 1000).toFixed(1)}s)`).join('\n');
7
15
  }
8
16
  /** Renders the MR/PR description as: Intent, Summary, per-file changes, Risk, Checks, Test Scenarios. */
@@ -20,16 +28,40 @@ export function buildDescription(intent, agentName, checks) {
20
28
  `Generated by \`pipeline-worker\`: intent captured via **${agentName}**. ` +
21
29
  'If the pipeline fails, pipeline-worker will attempt automated fixes (up to the configured retry cap) before escalating here.');
22
30
  }
23
- export async function openMergeRequest(forge, worktreePath, branch, targetBranch, intent, agentName, checks) {
31
+ /**
32
+ * Best-effort: repo settings (GitHub's "Allow auto-merge" toggle unset, no
33
+ * required checks configured) or GitLab's async merge-status computation can
34
+ * both cause the forge to reject this — never let that fail an otherwise
35
+ * fully-successful run. The fallback is simply what happens today: the
36
+ * developer merges manually once CI is green.
37
+ */
38
+ async function maybeEnableAutoMerge(forge, mr, autoMergeOnGreen, mergeMethod) {
39
+ if (!autoMergeOnGreen) {
40
+ skipStep('11.1', '🔀', 'Enabling auto-merge on green', 'config.autoMergeOnGreen is disabled');
41
+ return;
42
+ }
43
+ try {
44
+ await runStep('11.1', '🔀', 'Enabling auto-merge on green', `merge method: ${mergeMethod}`, () => forge.enableAutoMerge(mr.iid, mergeMethod));
45
+ }
46
+ catch (error) {
47
+ const message = error instanceof Error ? error.message : String(error);
48
+ noteRisk('medium', `could not enable auto-merge (${message}) — merge manually once CI is green`);
49
+ }
50
+ }
51
+ export async function openMergeRequest(forge, worktreePath, branch, targetBranch, intent, agentName, checks, autoMergeOnGreen = false, mergeMethod = 'squash') {
24
52
  await runStep(10, '⬆', 'Pushing your branch', `push ${branch} to origin`, () => push(worktreePath, 'origin', branch));
25
53
  const existing = await forge.findExistingMr(branch);
26
54
  if (existing) {
27
55
  skipStep(11, '🔀', 'Opening merge request', `an MR/PR already exists for this branch — reusing ${existing.webUrl} instead of opening a new one`);
56
+ // Deliberately does not (re-)enable auto-merge here: a resumed run reusing
57
+ // an existing MR/PR must not silently turn on something a human may have
58
+ // turned off since it was opened.
28
59
  return existing;
29
60
  }
30
61
  const description = buildDescription(intent, agentName, checks);
31
62
  const mr = await runStep(11, '🔀', 'Opening merge request', `target branch: ${targetBranch}`, () => forge.createMergeRequest({ sourceBranch: branch, targetBranch, title: intent.commitMessage, description }));
32
63
  note(mr.webUrl);
64
+ await maybeEnableAutoMerge(forge, mr, autoMergeOnGreen, mergeMethod);
33
65
  return mr;
34
66
  }
35
67
  //# sourceMappingURL=openMergeRequest.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"openMergeRequest.js","sourceRoot":"","sources":["../../src/workflow/openMergeRequest.ts"],"names":[],"mappings":"AAAA,2GAA2G;AAE3G,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAIzD,wGAAwG;AACxG,SAAS,YAAY,CAAC,MAAqB;IACzC,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAChH,CAAC;AAED,yGAAyG;AACzG,MAAM,UAAU,gBAAgB,CAAC,MAAsB,EAAE,SAAiB,EAAE,MAAqB;IAC/F,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9F,MAAM,aAAa,GAAG,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3E,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAExE,OAAO,CACL,eAAe,MAAM,CAAC,MAAM,MAAM;QAClC,gBAAgB,MAAM,CAAC,OAAO,MAAM;QACpC,sBAAsB,WAAW,MAAM;QACvC,aAAa,IAAI,MAAM,MAAM,CAAC,UAAU,MAAM;QAC9C,gBAAgB,YAAY,CAAC,MAAM,CAAC,MAAM;QAC1C,wBAAwB,aAAa,MAAM;QAC3C,OAAO;QACP,2DAA2D,SAAS,MAAM;QAC1E,8HAA8H,CAC/H,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,KAAkB,EAClB,YAAoB,EACpB,MAAc,EACd,YAAoB,EACpB,MAAsB,EACtB,SAAiB,EACjB,MAAqB;IAErB,MAAM,OAAO,CAAC,EAAE,EAAE,GAAG,EAAE,qBAAqB,EAAE,QAAQ,MAAM,YAAY,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;IAEtH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;IACpD,IAAI,QAAQ,EAAE,CAAC;QACb,QAAQ,CAAC,EAAE,EAAE,IAAI,EAAE,uBAAuB,EAAE,qDAAqD,QAAQ,CAAC,MAAM,+BAA+B,CAAC,CAAC;QACjJ,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM,WAAW,GAAG,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;IAEhE,MAAM,EAAE,GAAG,MAAM,OAAO,CACtB,EAAE,EACF,IAAI,EACJ,uBAAuB,EACvB,kBAAkB,YAAY,EAAE,EAChC,GAAG,EAAE,CAAC,KAAK,CAAC,kBAAkB,CAAC,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,CAAC,aAAa,EAAE,WAAW,EAAE,CAAC,CACjH,CAAC;IACF,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;IAChB,OAAO,EAAE,CAAC;AACZ,CAAC"}
1
+ {"version":3,"file":"openMergeRequest.js","sourceRoot":"","sources":["../../src/workflow/openMergeRequest.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  /** Top-level control flow wiring the user's workflow stages together — see ui/steps.ts's TOTAL_STAGES for the full numbered sequence. */
2
- import type { RunPhase } from '../types.js';
2
+ import type { CapturedIntent, CheckResult, PipelineWorkerConfig, RunPhase, RunState } from '../types.js';
3
3
  /**
4
4
  * True once the run has an open MR/PR — from that point, `pipeline-worker
5
5
  * resume` needs the worktree to still exist to keep pushing CI-fix/conflict
@@ -13,4 +13,16 @@ export interface RunWorkflowOptions {
13
13
  /** Ticket/issue id to interpolate into config.branchPattern's {ticket} placeholder, if it has one. */
14
14
  ticket?: string;
15
15
  }
16
+ /**
17
+ * Stage 7: run build/lint/test, reporting and recording the outcome. Returns
18
+ * null (already logged/recorded, exitCode set) when a check failed. Exported
19
+ * for reuse by adoptBranch.ts's "no PR/MR yet" path, which runs this exact
20
+ * stage before opening a new PR/MR for a branch pipeline-worker never created.
21
+ */
22
+ export declare function runAndReportChecks(config: PipelineWorkerConfig, worktreePath: string, state: RunState, repoRoot: string): Promise<CheckResult[] | null>;
23
+ /**
24
+ * Stage 8 (optional): add a changelog bullet for this change, or announce the
25
+ * skip when disabled. Exported for reuse by adoptBranch.ts.
26
+ */
27
+ export declare function maybeUpdateChangelog(config: PipelineWorkerConfig, worktreePath: string, intent: CapturedIntent): Promise<void>;
16
28
  export declare function runWorkflow(repoRoot: string, options?: RunWorkflowOptions): Promise<void>;
@@ -5,7 +5,8 @@ import { selectAgent } from '../agent/index.js';
5
5
  import { captureDiff, resetRepo } from '../git/diff.js';
6
6
  import { buildBranchName } from '../git/branchName.js';
7
7
  import { createWorktree, syncWithOrigin, applyDiffToWorktree, removeWorktree, renameBranch, generateTempBranchName } from '../git/worktree.js';
8
- import { currentBranch, commit, stageAll, findUnresolvedConflictMarkers } from '../git/commit.js';
8
+ import { currentBranch, commit, stageAll, findUnresolvedConflictMarkers, forcePushWithLease } from '../git/commit.js';
9
+ import { squashCommitsSinceMergeBase } from '../git/squash.js';
9
10
  import { captureIntent } from './captureIntent.js';
10
11
  import { runChecks } from './runChecks.js';
11
12
  import { updateChangelog } from './updateChangelog.js';
@@ -85,8 +86,13 @@ async function captureIntentAndBranch(agent, config, options, worktreePath, chan
85
86
  }
86
87
  return { intent, actualBranchName };
87
88
  }
88
- /** Stage 7: run build/lint/test, reporting and recording the outcome. Returns null (already logged/recorded, exitCode set) when a check failed. */
89
- async function runAndReportChecks(config, worktreePath, state, repoRoot) {
89
+ /**
90
+ * Stage 7: run build/lint/test, reporting and recording the outcome. Returns
91
+ * null (already logged/recorded, exitCode set) when a check failed. Exported
92
+ * for reuse by adoptBranch.ts's "no PR/MR yet" path, which runs this exact
93
+ * stage before opening a new PR/MR for a branch pipeline-worker never created.
94
+ */
95
+ export async function runAndReportChecks(config, worktreePath, state, repoRoot) {
90
96
  const checks = await runStep(7, '✅', 'Running checks', 'build, lint, and test — whichever your repo has configured', () => runChecks(config, worktreePath));
91
97
  for (const check of checks)
92
98
  note(`${check.name}: ${check.ok ? 'passed' : 'failed'} (${(check.durationMs / 1000).toFixed(1)}s)`);
@@ -101,8 +107,11 @@ async function runAndReportChecks(config, worktreePath, state, repoRoot) {
101
107
  recordEvent(repoRoot, state, `Checks passed (${checks.map((c) => c.name).join(', ')})`);
102
108
  return checks;
103
109
  }
104
- /** Stage 8 (optional): add a changelog bullet for this change, or announce the skip when disabled. */
105
- async function maybeUpdateChangelog(config, worktreePath, intent) {
110
+ /**
111
+ * Stage 8 (optional): add a changelog bullet for this change, or announce the
112
+ * skip when disabled. Exported for reuse by adoptBranch.ts.
113
+ */
114
+ export async function maybeUpdateChangelog(config, worktreePath, intent) {
106
115
  if (config.updateChangelog) {
107
116
  await runStep(8, '📝', 'Updating changelog', "add a bullet under CHANGELOG.md's [Unreleased] section", async () => {
108
117
  updateChangelog(worktreePath, intent);
@@ -120,7 +129,7 @@ async function commitAndOpenMr(forge, worktreePath, state, targetBranch, intent,
120
129
  // everything staged; without this commit the push would carry no
121
130
  // changes and the MR would be empty.
122
131
  () => commit(worktreePath, intent.commitMessage));
123
- const mr = await openMergeRequest(forge, worktreePath, state.branch, targetBranch, intent, config.agent, checks);
132
+ const mr = await openMergeRequest(forge, worktreePath, state.branch, targetBranch, intent, config.agent, checks, config.autoMergeOnGreen, config.mergeMethod);
124
133
  state.mrIid = mr.iid;
125
134
  state.phase = 'mr';
126
135
  recordEvent(repoRoot, state, `Opened MR/PR ${mr.webUrl}`);
@@ -139,10 +148,35 @@ async function maybeCleanupEarly(config, repoRoot, untrackedFiles, branch, relea
139
148
  releaseLock();
140
149
  }
141
150
  }
151
+ /**
152
+ * Opt-in (config.squashOnMerge): collapses every commit this run made on the
153
+ * branch into one, titled from the captured intent, then force-pushes.
154
+ * Best-effort — never fails an otherwise-successful run. In particular, if
155
+ * config.autoMergeOnGreen is also on, the forge may have already merged (and
156
+ * possibly deleted) the branch via its own webhook before this runs; that
157
+ * shows up here as a push failure and is logged as a no-op, not an error.
158
+ */
159
+ async function maybeSquashCommits(config, worktreePath, branch, targetBranch, intent) {
160
+ if (!config.squashOnMerge) {
161
+ skipStep('12.8', '📚', 'Squashing run commits', 'config.squashOnMerge is disabled');
162
+ return;
163
+ }
164
+ try {
165
+ await runStep('12.8', '📚', 'Squashing run commits', `collapsing into one commit: "${intent.commitMessage}"`, async () => {
166
+ await squashCommitsSinceMergeBase(worktreePath, targetBranch, intent.commitMessage);
167
+ await forcePushWithLease(worktreePath, 'origin', branch);
168
+ });
169
+ }
170
+ catch (error) {
171
+ const message = error instanceof Error ? error.message : String(error);
172
+ noteRisk('low', `could not squash run commits (${message}) — likely already merged; leaving history as-is`);
173
+ }
174
+ }
142
175
  /** After watchPipeline settles: report the final outcome and run stage 13's cleanup if it hasn't already run early. */
143
176
  // fallow-ignore-next-line complexity
144
- async function finalizeRun(finalPhase, config, mr, state, repoRoot, untrackedFiles) {
177
+ async function finalizeRun(finalPhase, config, mr, state, repoRoot, untrackedFiles, worktreePath, targetBranch, intent) {
145
178
  if (finalPhase === 'done') {
179
+ await maybeSquashCommits(config, worktreePath, state.branch, targetBranch, intent);
146
180
  const detail = state.pipelineId !== undefined ? `MR ${mr.webUrl} passed CI` : `MR ${mr.webUrl} opened — no CI pipeline found, nothing to watch`;
147
181
  step('🎉', 'Done', detail);
148
182
  if (config.cleanupOnSuccess) {
@@ -183,7 +217,7 @@ export async function runWorkflow(repoRoot, options = {}) {
183
217
  const targetBranch = await currentBranch(repoRoot);
184
218
  const tempBranch = generateTempBranchName();
185
219
  const worktreePath = await runStep(2, '🌳', 'Creating worktree', `create worktree with name ${tempBranch}`, () => createWorktree(repoRoot, tempBranch));
186
- let state = { branch: tempBranch, targetBranch, worktreePath, attempt: 0, phase: 'diff' };
220
+ let state = { branch: tempBranch, targetBranch, worktreePath, ciFixAttempt: 0, conflictAttempt: 0, phase: 'diff' };
187
221
  recordEvent(repoRoot, state, `Created worktree at ${worktreePath} (temp branch ${tempBranch})`);
188
222
  const { cleanup, markDone } = makeIdempotentCleanup(() => removeWorktree(repoRoot, worktreePath));
189
223
  registerExitSignals((exitCode) => {
@@ -223,7 +257,7 @@ export async function runWorkflow(repoRoot, options = {}) {
223
257
  // boundary so TS uses the declared RunPhase return type instead of the
224
258
  // 'mr' literal it narrowed state.phase to just before the call.
225
259
  const finalPhase = readPhase(state);
226
- await finalizeRun(finalPhase, config, mr, state, repoRoot, untrackedFiles);
260
+ await finalizeRun(finalPhase, config, mr, state, repoRoot, untrackedFiles, worktreePath, targetBranch, intent);
227
261
  }
228
262
  catch (error) {
229
263
  const message = error instanceof Error ? error.message : String(error);
@@ -1 +1 @@
1
- {"version":3,"file":"orchestrate.js","sourceRoot":"","sources":["../../src/workflow/orchestrate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"orchestrate.js","sourceRoot":"","sources":["../../src/workflow/orchestrate.ts"],"names":[],"mappings":"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"}
@@ -32,12 +32,14 @@ import type { ForgeName, PipelineWorkerConfig, Pipeline, RunState } from '../typ
32
32
  * no-pipeline grace window below must never fire in that case, only when
33
33
  * there's genuinely no CI config to run.
34
34
  *
35
- * Checks only the conventional path for each forge; a GitLab project whose
36
- * "CI/CD configuration file" setting points elsewhere (a custom path or a
37
- * separate repo) reads as unconfigured here and keeps the old grace-window
38
- * behavior a known gap, not a false "no CI".
35
+ * Checks the conventional path first, then for GitLab, which has no
36
+ * equivalent on GitHub — the project's actual "CI/CD configuration file"
37
+ * setting via forgeClient.getCiConfigPath(), so a project using a non-default
38
+ * path isn't wrongly read as unconfigured. A `path@group/project` value
39
+ * (referencing another project's config) is treated as configured without a
40
+ * local filesystem check, since that path can't be resolved locally at all.
39
41
  */
40
- export declare function hasCiConfig(worktreePath: string, forge: ForgeName): boolean;
42
+ export declare function hasCiConfig(worktreePath: string, forgeClient: ForgeClient, forgeName: ForgeName): Promise<boolean>;
41
43
  export type PollOutcome = {
42
44
  kind: 'pipeline';
43
45
  pipeline: Pipeline;
@@ -47,4 +49,45 @@ export type PollOutcome = {
47
49
  kind: 'no-pipeline';
48
50
  };
49
51
  export declare function pollForNextAction(forge: ForgeClient, mrIid: number, intervalMs: number, previousPipelineId?: number, noPipelineGraceMs?: number, ciConfigured?: boolean): Promise<PollOutcome>;
52
+ /**
53
+ * Merges origin/targetBranch into the worktree's branch to clear a confirmed
54
+ * merge conflict, asking the agent to resolve any real conflict markers, then
55
+ * re-verifies build/lint/test locally before ever pushing — a merge (or the
56
+ * agent's conflict resolution) can silently break the build even with no
57
+ * literal conflict markers left, and catching that here costs seconds
58
+ * instead of a full remote CI round-trip. Each inner iteration (whether
59
+ * resolving conflict markers or fixing a local-check regression the merge
60
+ * introduced) commits its own work before checking, so a stuck agent that
61
+ * makes no further changes is always caught precisely as "produced no
62
+ * changes *this* iteration", not conflated with an earlier iteration's
63
+ * still-uncommitted edits.
64
+ *
65
+ * Uses state.conflictAttempt/config.maxFixAttempts — a budget independent of
66
+ * runCiFixAttempt's state.ciFixAttempt, so a long-lived PR needing several
67
+ * trivial rebases can't exhaust the budget meant for real bug-fixing.
68
+ * Returns false when it escalated instead of resolving (caller should stop
69
+ * the run in that case).
70
+ */
71
+ export declare function tryResolveConflicts(forge: ForgeClient, agent: AgentAdapter, config: PipelineWorkerConfig, worktreePath: string, branch: string, targetBranch: string, mrIid: number, state: RunState, repoRoot: string): Promise<boolean>;
72
+ /** What watchPipeline's outer polling loop should do next, since only it can act on `continue`/`return`. */
73
+ type PipelineOutcomeResult = {
74
+ action: 'stop';
75
+ } | {
76
+ action: 'continue';
77
+ previousPipelineId: number;
78
+ };
79
+ /**
80
+ * Attempts one CI fix: diagnose/fix via the agent, then re-verify build/lint/test
81
+ * locally *before* pushing — a broken fix costs seconds to catch here instead
82
+ * of a full remote CI round-trip. If the local check still fails, loops back
83
+ * and asks the agent again (feeding it the local failure) without pushing or
84
+ * spending another CI cycle, up to the shared budget. Each iteration commits
85
+ * its own edit before checking, so a stuck agent that stops making changes is
86
+ * always caught precisely as "produced no changes *this* iteration".
87
+ *
88
+ * Uses state.ciFixAttempt/config.maxFixAttempts — a budget independent of
89
+ * tryResolveConflicts's state.conflictAttempt.
90
+ */
91
+ export declare function runCiFixAttempt(forge: ForgeClient, config: PipelineWorkerConfig, agent: AgentAdapter, worktreePath: string, branch: string, mrIid: number, pipeline: Pipeline, state: RunState, repoRoot: string): Promise<PipelineOutcomeResult>;
50
92
  export declare function watchPipeline(forge: ForgeClient, config: PipelineWorkerConfig, agent: AgentAdapter, worktreePath: string, branch: string, targetBranch: string, mrIid: number, state: RunState, repoRoot: string): Promise<void>;
93
+ export {};
@@ -29,9 +29,12 @@ import { randomUUID } from 'node:crypto';
29
29
  import { tmpdir } from 'node:os';
30
30
  import { promisify } from 'node:util';
31
31
  import { stageAll, commit, push, hasChanges, listConflictedFiles, findUnresolvedConflictMarkers } from '../git/commit.js';
32
+ import { runChecks } from './runChecks.js';
32
33
  import { recordEvent } from '../state/runState.js';
33
34
  import { step, runStep, note, reportAgentInvocation } from '../ui/steps.js';
34
35
  const execFileAsync = promisify(execFile);
36
+ /** Tail-truncation cap for a failing check's stderr fed back to the agent — mirrors agent/claude.ts's own cap on error output it surfaces. */
37
+ const MAX_LOCAL_CHECK_OUTPUT_CHARS = 4000;
35
38
  const MAX_POLL_WINDOW_MS = 2 * 60 * 60 * 1000; // per pipeline attempt, as a safety net
36
39
  // How long to tolerate zero pipelines before concluding the repo has no CI
37
40
  // configured for this MR/PR, rather than CI simply not having registered
@@ -40,7 +43,7 @@ const MAX_POLL_WINDOW_MS = 2 * 60 * 60 * 1000; // per pipeline attempt, as a saf
40
43
  // no CI config file in the worktree — once either is untrue, an empty
41
44
  // result can't mean "no CI".
42
45
  const NO_PIPELINE_GRACE_MS = 60 * 1000;
43
- const TERMINAL_STATUSES = ['success', 'failed', 'canceled', 'skipped'];
46
+ const TERMINAL_STATUSES = ['success', 'failed', 'canceled', 'skipped', 'manual', 'scheduled'];
44
47
  function sleep(ms) {
45
48
  return new Promise((resolve) => setTimeout(resolve, ms));
46
49
  }
@@ -51,14 +54,23 @@ function sleep(ms) {
51
54
  * no-pipeline grace window below must never fire in that case, only when
52
55
  * there's genuinely no CI config to run.
53
56
  *
54
- * Checks only the conventional path for each forge; a GitLab project whose
55
- * "CI/CD configuration file" setting points elsewhere (a custom path or a
56
- * separate repo) reads as unconfigured here and keeps the old grace-window
57
- * behavior a known gap, not a false "no CI".
57
+ * Checks the conventional path first, then for GitLab, which has no
58
+ * equivalent on GitHub — the project's actual "CI/CD configuration file"
59
+ * setting via forgeClient.getCiConfigPath(), so a project using a non-default
60
+ * path isn't wrongly read as unconfigured. A `path@group/project` value
61
+ * (referencing another project's config) is treated as configured without a
62
+ * local filesystem check, since that path can't be resolved locally at all.
58
63
  */
59
- export function hasCiConfig(worktreePath, forge) {
60
- if (forge === 'gitlab') {
61
- return existsSync(join(worktreePath, '.gitlab-ci.yml'));
64
+ export async function hasCiConfig(worktreePath, forgeClient, forgeName) {
65
+ if (forgeName === 'gitlab') {
66
+ if (existsSync(join(worktreePath, '.gitlab-ci.yml')))
67
+ return true;
68
+ const customPath = await forgeClient.getCiConfigPath();
69
+ if (!customPath)
70
+ return false;
71
+ if (customPath.includes('@'))
72
+ return true;
73
+ return existsSync(join(worktreePath, customPath));
62
74
  }
63
75
  const workflowsDir = join(worktreePath, '.github', 'workflows');
64
76
  return existsSync(workflowsDir) && readdirSync(workflowsDir).some((f) => f.endsWith('.yml') || f.endsWith('.yaml'));
@@ -195,38 +207,85 @@ async function finalizeMergeResolution(worktreePath, targetBranch) {
195
207
  await execFileAsync('git', ['add', '-A'], { cwd: worktreePath });
196
208
  await commit(worktreePath, `merge: resolve conflicts with origin/${targetBranch}`);
197
209
  }
210
+ /** Feeds a check that still fails locally back to the agent — used both by the CI-fix loop and the conflict-resolution loop once they've moved past their first (merge/CI-diagnosis) step. */
211
+ function buildLocalCheckFixPrompt(check) {
212
+ const tail = check.stderr.slice(-MAX_LOCAL_CHECK_OUTPUT_CHARS);
213
+ return (`Your last change still fails locally: the "${check.name}" check failed when re-run in this worktree ` +
214
+ `(nothing has been pushed yet, so CI hasn't seen this). Fix the underlying issue so ${check.name} passes.\n\n` +
215
+ `--- ${check.name} output (tail) ---\n${tail}`);
216
+ }
198
217
  /**
199
218
  * Merges origin/targetBranch into the worktree's branch to clear a confirmed
200
- * merge conflict, asking the agent to resolve any real conflict markers.
201
- * Shares state.attempt/config.maxFixAttempts with the CI-fix loopboth are
202
- * "automated intervention attempts before giving up and escalating to a
203
- * human" from the same budget. Returns false when it escalated instead of
204
- * resolving (caller should stop the run in that case).
219
+ * merge conflict, asking the agent to resolve any real conflict markers, then
220
+ * re-verifies build/lint/test locally before ever pushinga merge (or the
221
+ * agent's conflict resolution) can silently break the build even with no
222
+ * literal conflict markers left, and catching that here costs seconds
223
+ * instead of a full remote CI round-trip. Each inner iteration (whether
224
+ * resolving conflict markers or fixing a local-check regression the merge
225
+ * introduced) commits its own work before checking, so a stuck agent that
226
+ * makes no further changes is always caught precisely as "produced no
227
+ * changes *this* iteration", not conflated with an earlier iteration's
228
+ * still-uncommitted edits.
229
+ *
230
+ * Uses state.conflictAttempt/config.maxFixAttempts — a budget independent of
231
+ * runCiFixAttempt's state.ciFixAttempt, so a long-lived PR needing several
232
+ * trivial rebases can't exhaust the budget meant for real bug-fixing.
233
+ * Returns false when it escalated instead of resolving (caller should stop
234
+ * the run in that case).
205
235
  */
206
236
  // fallow-ignore-next-line complexity
207
- async function tryResolveConflicts(forge, agent, config, worktreePath, branch, targetBranch, mrIid, state, repoRoot) {
208
- state.attempt += 1;
209
- recordEvent(repoRoot, state, `Merge conflicts detected; attempt ${state.attempt}/${config.maxFixAttempts} — merging origin/${targetBranch} into ${branch}`);
210
- step('⚠️', 'Merge conflicts detected', `attempt ${state.attempt}/${config.maxFixAttempts} merging origin/${targetBranch} into ${branch}`);
211
- if (state.attempt > config.maxFixAttempts) {
212
- await escalate(forge, mrIid, formatEscalationNote(`Automated fix attempts exhausted (${state.attempt - 1} attempt(s))`, [`❌ Merge conflicts with \`origin/${targetBranch}\` could not be resolved automatically`], 'This MR/PR needs a human to resolve them.'), state, repoRoot);
213
- return false;
214
- }
215
- const cleanMerge = await attemptCleanMerge(worktreePath, targetBranch);
216
- if (!cleanMerge) {
217
- const conflictedFiles = await listConflictedFiles(worktreePath);
218
- if (conflictedFiles.length === 0) {
219
- throw new Error(`git merge origin/${targetBranch} failed for a reason other than conflicts — check the worktree for details`);
220
- }
221
- const stillConflicted = await resolveConflictsWithAgent(agent, worktreePath, conflictedFiles);
222
- if (stillConflicted.length > 0) {
223
- await escalate(forge, mrIid, formatEscalationNote('Merge conflict resolution failed', [`❌ Agent left ${stillConflicted.length} file(s) still conflicted: ${stillConflicted.join(', ')}`], 'Escalating to a human.'), state, repoRoot);
237
+ export async function tryResolveConflicts(forge, agent, config, worktreePath, branch, targetBranch, mrIid, state, repoRoot) {
238
+ let lastLocalFailure;
239
+ for (;;) {
240
+ state.conflictAttempt += 1;
241
+ recordEvent(repoRoot, state, `Merge conflicts detected; attempt ${state.conflictAttempt}/${config.maxFixAttempts} — merging origin/${targetBranch} into ${branch}`);
242
+ step('⚠️', 'Merge conflicts detected', `attempt ${state.conflictAttempt}/${config.maxFixAttempts} merging origin/${targetBranch} into ${branch}`);
243
+ if (state.conflictAttempt > config.maxFixAttempts) {
244
+ const bullet = lastLocalFailure
245
+ ? `❌ Merge resolved, but local checks still fail (${lastLocalFailure.name}) — never reached CI`
246
+ : `❌ Merge conflicts with \`origin/${targetBranch}\` could not be resolved automatically`;
247
+ await escalate(forge, mrIid, formatEscalationNote(`Automated fix attempts exhausted (${state.conflictAttempt - 1} attempt(s))`, [bullet], 'This MR/PR needs a human to resolve them.'), state, repoRoot);
224
248
  return false;
225
249
  }
226
- await finalizeMergeResolution(worktreePath, targetBranch);
250
+ if (lastLocalFailure === undefined) {
251
+ const cleanMerge = await attemptCleanMerge(worktreePath, targetBranch);
252
+ if (!cleanMerge) {
253
+ const conflictedFiles = await listConflictedFiles(worktreePath);
254
+ if (conflictedFiles.length === 0) {
255
+ throw new Error(`git merge origin/${targetBranch} failed for a reason other than conflicts — check the worktree for details`);
256
+ }
257
+ const stillConflicted = await resolveConflictsWithAgent(agent, worktreePath, conflictedFiles);
258
+ if (stillConflicted.length > 0) {
259
+ await escalate(forge, mrIid, formatEscalationNote('Merge conflict resolution failed', [`❌ Agent left ${stillConflicted.length} file(s) still conflicted: ${stillConflicted.join(', ')}`], 'Escalating to a human.'), state, repoRoot);
260
+ return false;
261
+ }
262
+ await finalizeMergeResolution(worktreePath, targetBranch);
263
+ }
264
+ // else: attemptCleanMerge's `git merge --no-edit` already auto-committed — nothing left to stage here.
265
+ }
266
+ else {
267
+ const agentResult = await runStep('12.36', '🔧', 'Fixing local check failure after merge', `asking the agent to fix the ${lastLocalFailure.name} failure the merge introduced`, () => agent.invoke({ prompt: buildLocalCheckFixPrompt(lastLocalFailure), cwd: worktreePath, permissionMode: 'acceptEdits' }));
268
+ reportAgentInvocation(agentResult, worktreePath);
269
+ if (!(await hasChanges(worktreePath))) {
270
+ await escalate(forge, mrIid, formatEscalationNote('Fix attempt produced no changes', [
271
+ `❌ Merge resolved, but local checks still fail (${lastLocalFailure.name})`,
272
+ `⚠️ Agent fix attempt ${state.conflictAttempt} made no changes to push`,
273
+ ], 'Escalating to a human.'), state, repoRoot);
274
+ return false;
275
+ }
276
+ await stageAll(worktreePath);
277
+ await commit(worktreePath, `fix: address local check failure after merge (attempt ${state.conflictAttempt})`);
278
+ }
279
+ const localChecks = await runStep('12.35', '🔍', 'Verifying merge locally', 'build/lint/test in the worktree before pushing, so a broken merge/fix never costs a full CI round-trip', () => runChecks(config, worktreePath));
280
+ const failed = localChecks.find((c) => !c.ok);
281
+ if (failed) {
282
+ note(`${failed.name} failed locally after merge — asking the agent again without pushing`);
283
+ lastLocalFailure = failed;
284
+ continue;
285
+ }
286
+ await runStep('12.4', '⬆', 'Pushing the merge', `push ${branch} to origin`, () => push(worktreePath, 'origin', branch));
287
+ return true;
227
288
  }
228
- await runStep('12.4', '⬆', 'Pushing the merge', `push ${branch} to origin`, () => push(worktreePath, 'origin', branch));
229
- return true;
230
289
  }
231
290
  /** No CI ran at all (repo has no workflow configured for this MR/PR) — there's nothing to poll for, so stop instead of spinning until the 2-hour safety window would otherwise time out. */
232
291
  async function handleNoPipelineOutcome(state, repoRoot) {
@@ -234,35 +293,63 @@ async function handleNoPipelineOutcome(state, repoRoot) {
234
293
  state.phase = 'done';
235
294
  recordEvent(repoRoot, state, 'No CI pipeline found for this MR/PR — nothing to watch');
236
295
  }
237
- /** Attempts one CI fix: diagnose/fix via the agent, then commit and push. Shares state.attempt/config.maxFixAttempts with the conflict-resolution loop. */
238
- async function runCiFixAttempt(forge, config, agent, worktreePath, branch, mrIid, pipeline, state, repoRoot) {
239
- state.attempt += 1;
240
- recordEvent(repoRoot, state, `Pipeline failed; attempt ${state.attempt}/${config.maxFixAttempts} ${pipeline.webUrl}`);
241
- step('💥', 'Pipeline failed', `attempt ${state.attempt}/${config.maxFixAttempts} ${pipeline.webUrl}`);
242
- if (state.attempt > config.maxFixAttempts) {
243
- await escalate(forge, mrIid, formatEscalationNote(`Automated fix attempts exhausted (${state.attempt - 1} attempt(s))`, [`❌ CI pipeline: still failing — ${pipeline.webUrl}`], 'Needs a human to take over.'), state, repoRoot);
244
- return { action: 'stop' };
245
- }
246
- const mcpConfigPath = writeAgentMcpConfig();
247
- let agentResult;
248
- try {
249
- agentResult = await runStep('12.5', '🔧', 'Fixing CI failure', `asking the agent to diagnose and fix ${pipeline.webUrl} via whatever ${forgeLabel(config.forge)} MCP tooling is available`, () => agent.invoke({ prompt: buildFixPrompt(pipeline, config.forge), cwd: worktreePath, mcpConfigPath, permissionMode: 'acceptEdits' }));
250
- }
251
- finally {
252
- unlinkSync(mcpConfigPath);
253
- }
254
- reportAgentInvocation(agentResult, worktreePath);
255
- if (!(await hasChanges(worktreePath))) {
256
- // Re-pushing an identical tree would never produce a new pipeline; stop here.
257
- await escalate(forge, mrIid, formatEscalationNote('Fix attempt produced no changes', [`❌ CI pipeline: still failing — ${pipeline.webUrl}`, `⚠️ Agent fix attempt ${state.attempt} made no changes to push`], 'Escalating to a human.'), state, repoRoot);
258
- return { action: 'stop' };
296
+ /**
297
+ * Attempts one CI fix: diagnose/fix via the agent, then re-verify build/lint/test
298
+ * locally *before* pushing — a broken fix costs seconds to catch here instead
299
+ * of a full remote CI round-trip. If the local check still fails, loops back
300
+ * and asks the agent again (feeding it the local failure) without pushing or
301
+ * spending another CI cycle, up to the shared budget. Each iteration commits
302
+ * its own edit before checking, so a stuck agent that stops making changes is
303
+ * always caught precisely as "produced no changes *this* iteration".
304
+ *
305
+ * Uses state.ciFixAttempt/config.maxFixAttempts — a budget independent of
306
+ * tryResolveConflicts's state.conflictAttempt.
307
+ */
308
+ // fallow-ignore-next-line complexity
309
+ export async function runCiFixAttempt(forge, config, agent, worktreePath, branch, mrIid, pipeline, state, repoRoot) {
310
+ let lastLocalFailure;
311
+ for (;;) {
312
+ state.ciFixAttempt += 1;
313
+ recordEvent(repoRoot, state, `Pipeline failed; attempt ${state.ciFixAttempt}/${config.maxFixAttempts} — ${pipeline.webUrl}`);
314
+ step('💥', 'Pipeline failed', `attempt ${state.ciFixAttempt}/${config.maxFixAttempts} — ${pipeline.webUrl}`);
315
+ if (state.ciFixAttempt > config.maxFixAttempts) {
316
+ const bullet = lastLocalFailure
317
+ ? `❌ Agent's fix still fails locally (${lastLocalFailure.name}) never reached CI again`
318
+ : `❌ CI pipeline: still failing — ${pipeline.webUrl}`;
319
+ await escalate(forge, mrIid, formatEscalationNote(`Automated fix attempts exhausted (${state.ciFixAttempt - 1} attempt(s))`, [bullet], 'Needs a human to take over.'), state, repoRoot);
320
+ return { action: 'stop' };
321
+ }
322
+ const prompt = lastLocalFailure ? buildLocalCheckFixPrompt(lastLocalFailure) : buildFixPrompt(pipeline, config.forge);
323
+ const mcpConfigPath = writeAgentMcpConfig();
324
+ let agentResult;
325
+ try {
326
+ agentResult = await runStep('12.5', '🔧', 'Fixing CI failure', lastLocalFailure
327
+ ? `asking the agent to fix the ${lastLocalFailure.name} failure its last edit left behind`
328
+ : `asking the agent to diagnose and fix ${pipeline.webUrl} via whatever ${forgeLabel(config.forge)} MCP tooling is available`, () => agent.invoke({ prompt, cwd: worktreePath, mcpConfigPath, permissionMode: 'acceptEdits' }));
329
+ }
330
+ finally {
331
+ unlinkSync(mcpConfigPath);
332
+ }
333
+ reportAgentInvocation(agentResult, worktreePath);
334
+ if (!(await hasChanges(worktreePath))) {
335
+ // Re-pushing an identical tree would never produce a new pipeline; stop here.
336
+ await escalate(forge, mrIid, formatEscalationNote('Fix attempt produced no changes', [`❌ CI pipeline: still failing — ${pipeline.webUrl}`, `⚠️ Agent fix attempt ${state.ciFixAttempt} made no changes to push`], 'Escalating to a human.'), state, repoRoot);
337
+ return { action: 'stop' };
338
+ }
339
+ await runStep('12.55', '📦', 'Committing the fix', `commit attempt ${state.ciFixAttempt} on ${branch}`, async () => {
340
+ await stageAll(worktreePath);
341
+ await commit(worktreePath, `fix: address CI failure (attempt ${state.ciFixAttempt})`);
342
+ });
343
+ const localChecks = await runStep('12.56', '🔍', 'Verifying fix locally', 'build/lint/test in the worktree before spending a CI cycle on it', () => runChecks(config, worktreePath));
344
+ const failed = localChecks.find((c) => !c.ok);
345
+ if (failed) {
346
+ note(`${failed.name} still fails locally — asking the agent again without pushing`);
347
+ lastLocalFailure = failed;
348
+ continue;
349
+ }
350
+ await runStep('12.6', '⬆', 'Pushing the fix', `push ${branch} to origin`, () => push(worktreePath, 'origin', branch));
351
+ return { action: 'continue', previousPipelineId: pipeline.id };
259
352
  }
260
- await runStep('12.6', '⬆', 'Pushing the fix', `commit and push attempt ${state.attempt} to ${branch}`, async () => {
261
- await stageAll(worktreePath);
262
- await commit(worktreePath, `fix: address CI failure (attempt ${state.attempt})`);
263
- await push(worktreePath, 'origin', branch);
264
- });
265
- return { action: 'continue', previousPipelineId: pipeline.id };
266
353
  }
267
354
  /** Dispatches on a terminal pipeline's status: done (success), escalate (canceled/skipped), or attempt a CI fix (failed). */
268
355
  async function handlePipelineTerminal(forge, config, agent, worktreePath, branch, mrIid, pipeline, state, repoRoot) {
@@ -284,7 +371,7 @@ async function handlePipelineTerminal(forge, config, agent, worktreePath, branch
284
371
  // fallow-ignore-next-line complexity
285
372
  export async function watchPipeline(forge, config, agent, worktreePath, branch, targetBranch, mrIid, state, repoRoot) {
286
373
  const intervalMs = config.pollIntervalSeconds * 1000;
287
- const ciConfigured = hasCiConfig(worktreePath, config.forge);
374
+ const ciConfigured = await hasCiConfig(worktreePath, forge, config.forge);
288
375
  state.phase = 'watch';
289
376
  recordEvent(repoRoot, state, 'Started watching pipeline');
290
377
  let previousPipelineId;