@delegoapp/runner 0.5.2 → 0.5.5

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.
@@ -1,6 +1,9 @@
1
1
  import { promptThinkingInstruction } from '../thinking.js';
2
2
  export function promptForJob(job) {
3
3
  const thinkingInstruction = promptThinkingInstruction(job.executionPreferences.thinking);
4
+ const pullRequestInstruction = job.publishingPolicy.autoCreatePr
5
+ ? 'Do NOT create a pull request yourself (do not run `gh pr create` or any equivalent), and do NOT create or switch git branches (no `git checkout -b`, no `git switch -c`). Leave your work on the branch that is already checked out. The runner will commit your changes and open the pull request automatically once you exit.'
6
+ : `If you create a pull request, start the PR title with: ${job.linearIssue.identifier}:`;
4
7
  return [
5
8
  // This directive must come first — before task content — so Claude enters
6
9
  // execution with the correct mental model. When placed at the end, Claude
@@ -19,7 +22,7 @@ export function promptForJob(job) {
19
22
  '',
20
23
  `Linear issue: ${job.linearIssue.identifier}${job.linearIssue.title ? ` - ${job.linearIssue.title}` : ''}`,
21
24
  job.linearIssue.url ? `URL: ${job.linearIssue.url}` : null,
22
- `If you create a pull request, start the PR title with: ${job.linearIssue.identifier}:`,
25
+ pullRequestInstruction,
23
26
  '',
24
27
  `Requested executor: ${job.executionPreferences.executor}`,
25
28
  job.executionPreferences.model
@@ -20,12 +20,50 @@ function parsePrNumberFromUrl(url) {
20
20
  const value = Number(url.match(/\/pull\/(\d+)(?:\b|$)/)?.[1]);
21
21
  return Number.isFinite(value) ? value : null;
22
22
  }
23
+ function isAlreadyExistsError(stderr, stdout) {
24
+ // `gh pr create` writes "a pull request for branch ... already exists" to
25
+ // stderr when the agent already opened a PR for this branch. Match on the
26
+ // signal phrase to avoid coupling to gh's exact formatting.
27
+ return /already exists/i.test(stderr) || /already exists/i.test(stdout);
28
+ }
29
+ async function viewExistingPr(repositoryPath, branchName) {
30
+ const view = await runCommand('gh', ['pr', 'view', branchName, '--json', 'url,number,title,isDraft'], repositoryPath);
31
+ if (view.code !== 0) {
32
+ return null;
33
+ }
34
+ try {
35
+ const parsed = JSON.parse(view.stdout);
36
+ const url = typeof parsed.url === 'string' ? parsed.url : null;
37
+ if (!url) {
38
+ return null;
39
+ }
40
+ return {
41
+ url,
42
+ number: typeof parsed.number === 'number' ? parsed.number : null,
43
+ title: typeof parsed.title === 'string' ? parsed.title : '',
44
+ isDraft: typeof parsed.isDraft === 'boolean' ? parsed.isDraft : false,
45
+ };
46
+ }
47
+ catch {
48
+ return null;
49
+ }
50
+ }
23
51
  export async function publishBranchAndMaybePr(repositoryPath, job, branchName, commit) {
24
52
  validatePublishingPolicy(job.publishingPolicy);
25
53
  if (!job.publishingPolicy.autoPush) {
26
54
  return null;
27
55
  }
28
- const push = await runCommand('git', ['push', '-u', 'origin', branchName], repositoryPath);
56
+ // Push the current HEAD to the runner's branch ref explicitly, rather than
57
+ // pushing `branchName` by name. An executor that ignores the "do not create
58
+ // branches" instruction may `git checkout -b` and commit its work on a
59
+ // different branch; HEAD then advances (so the pipeline sees new commits and
60
+ // reaches publishing) while the runner's named branch still points at the
61
+ // starting SHA. Pushing `branchName` by name would then upload a commitless
62
+ // branch and `gh pr create` fails with "No commits between main and <branch>".
63
+ // `HEAD:refs/heads/<branchName>` always carries the actual work onto the
64
+ // runner's branch; when HEAD already is `branchName` it behaves identically
65
+ // to pushing the branch by name.
66
+ const push = await runCommand('git', ['push', '-u', 'origin', `HEAD:refs/heads/${branchName}`], repositoryPath);
29
67
  if (push.code !== 0) {
30
68
  throw new Error(`Unable to push branch ${branchName}: ${push.stderr || push.stdout || 'unknown git error'}`);
31
69
  }
@@ -48,6 +86,20 @@ export async function publishBranchAndMaybePr(repositoryPath, job, branchName, c
48
86
  }
49
87
  const pr = await runCommand('gh', args, repositoryPath);
50
88
  if (pr.code !== 0) {
89
+ if (isAlreadyExistsError(pr.stderr, pr.stdout)) {
90
+ const existing = await viewExistingPr(repositoryPath, branchName);
91
+ if (existing) {
92
+ return {
93
+ url: existing.url,
94
+ number: existing.number ?? parsePrNumberFromUrl(existing.url),
95
+ title: existing.title || title,
96
+ branchName,
97
+ draft: existing.isDraft,
98
+ repositorySlug: job.repositorySlug,
99
+ pushed: true,
100
+ };
101
+ }
102
+ }
51
103
  throw new Error(`Unable to create GitHub pull request: ${pr.stderr || pr.stdout || 'unknown gh error'}`);
52
104
  }
53
105
  const url = pr.stdout
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@delegoapp/runner",
3
- "version": "0.5.2",
3
+ "version": "0.5.5",
4
4
  "private": false,
5
5
  "description": "Delego runner — polls the Delego relay and executes Linear-delegated coding jobs via Codex CLI or Claude Code.",
6
6
  "keywords": [
@@ -11,6 +11,11 @@
11
11
  "linear",
12
12
  "runner"
13
13
  ],
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/zietek/delego-saas.git",
17
+ "directory": "packages/runner"
18
+ },
14
19
  "license": "UNLICENSED",
15
20
  "bin": {
16
21
  "delego-runner": "dist/bin.js"