patchrelay 0.8.9 → 0.9.1
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/README.md +64 -62
- package/dist/agent-session-plan.js +17 -17
- package/dist/build-info.json +3 -3
- package/dist/cli/args.js +1 -1
- package/dist/cli/commands/issues.js +18 -18
- package/dist/cli/data.js +109 -298
- package/dist/cli/formatters/text.js +22 -28
- package/dist/cli/help.js +7 -7
- package/dist/cli/index.js +3 -3
- package/dist/config.js +13 -166
- package/dist/db/migrations.js +46 -154
- package/dist/db.js +369 -45
- package/dist/factory-state.js +55 -0
- package/dist/github-webhook-handler.js +199 -0
- package/dist/github-webhooks.js +166 -0
- package/dist/hook-runner.js +28 -0
- package/dist/http.js +48 -22
- package/dist/issue-query-service.js +33 -38
- package/dist/linear-workflow.js +5 -118
- package/dist/preflight.js +1 -6
- package/dist/project-resolution.js +12 -1
- package/dist/run-orchestrator.js +446 -0
- package/dist/{stage-reporting.js → run-reporting.js} +11 -13
- package/dist/service-runtime.js +12 -61
- package/dist/service-webhooks.js +7 -52
- package/dist/service.js +39 -61
- package/dist/webhook-handler.js +387 -0
- package/dist/webhook-installation-handler.js +3 -8
- package/package.json +2 -1
- package/dist/db/authoritative-ledger-store.js +0 -536
- package/dist/db/issue-projection-store.js +0 -54
- package/dist/db/issue-workflow-coordinator.js +0 -320
- package/dist/db/issue-workflow-store.js +0 -194
- package/dist/db/run-report-store.js +0 -33
- package/dist/db/stage-event-store.js +0 -33
- package/dist/db/webhook-event-store.js +0 -59
- package/dist/db-ports.js +0 -5
- package/dist/ledger-ports.js +0 -1
- package/dist/reconciliation-action-applier.js +0 -68
- package/dist/reconciliation-actions.js +0 -1
- package/dist/reconciliation-engine.js +0 -350
- package/dist/reconciliation-snapshot-builder.js +0 -135
- package/dist/reconciliation-types.js +0 -1
- package/dist/service-stage-finalizer.js +0 -753
- package/dist/service-stage-runner.js +0 -336
- package/dist/service-webhook-processor.js +0 -411
- package/dist/stage-agent-activity-publisher.js +0 -59
- package/dist/stage-event-ports.js +0 -1
- package/dist/stage-failure.js +0 -92
- package/dist/stage-handoff.js +0 -107
- package/dist/stage-launch.js +0 -84
- package/dist/stage-lifecycle-publisher.js +0 -284
- package/dist/stage-turn-input-dispatcher.js +0 -104
- package/dist/webhook-agent-session-handler.js +0 -228
- package/dist/webhook-comment-handler.js +0 -141
- package/dist/webhook-desired-stage-recorder.js +0 -122
- package/dist/webhook-event-ports.js +0 -1
- package/dist/workflow-policy.js +0 -149
- package/dist/workflow-ports.js +0 -1
- /package/dist/{installation-ports.js → github-types.js} +0 -0
package/README.md
CHANGED
|
@@ -2,17 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
PatchRelay is a self-hosted harness for Linear-driven Codex work on your own machine.
|
|
4
4
|
|
|
5
|
-
It receives Linear webhooks, routes issues to the right local repository, prepares durable issue worktrees, runs
|
|
5
|
+
It receives Linear webhooks, routes issues to the right local repository, prepares durable issue worktrees, runs Codex sessions through `codex app-server`, and keeps the whole run observable and resumable from the CLI. GitHub webhooks drive reactive loops for CI repair, review fixes, and merge queue failures.
|
|
6
6
|
|
|
7
7
|
PatchRelay is the system around the model:
|
|
8
8
|
|
|
9
|
-
- webhook intake and verification
|
|
9
|
+
- webhook intake and verification (Linear and GitHub)
|
|
10
10
|
- Linear OAuth and workspace installations
|
|
11
11
|
- issue-to-repo routing
|
|
12
12
|
- issue worktree and branch lifecycle
|
|
13
|
-
-
|
|
13
|
+
- run orchestration and thread continuity
|
|
14
|
+
- reactive CI repair, review fix, and merge queue repair loops
|
|
14
15
|
- native Linear agent input forwarding into active runs
|
|
15
|
-
- read-only inspection and
|
|
16
|
+
- read-only inspection and run reporting
|
|
16
17
|
|
|
17
18
|
If you want Codex to work inside your real repos with your real tools, secrets, SSH access, and deployment surface, PatchRelay is the harness that makes that loop reliable.
|
|
18
19
|
|
|
@@ -20,20 +21,22 @@ If you want Codex to work inside your real repos with your real tools, secrets,
|
|
|
20
21
|
|
|
21
22
|
- Keep the agent in the real environment instead of rebuilding that environment in a hosted sandbox.
|
|
22
23
|
- Use your existing machine, repos, secrets, SSH config, shell tools, and deployment access.
|
|
23
|
-
- Keep deterministic workflow logic outside the model: routing,
|
|
24
|
+
- Keep deterministic workflow logic outside the model: routing, run orchestration, worktree ownership, and reporting.
|
|
24
25
|
- Choose the Codex approval and sandbox settings that match your risk tolerance.
|
|
25
|
-
- Let Linear drive the loop through delegation
|
|
26
|
+
- Let Linear drive the loop through delegation and native agent sessions.
|
|
27
|
+
- Let GitHub drive reactive loops through PR reviews and CI check events.
|
|
26
28
|
- Drop into the exact issue worktree and resume control manually when needed.
|
|
27
29
|
|
|
28
30
|
## What PatchRelay Owns
|
|
29
31
|
|
|
30
32
|
PatchRelay does the deterministic harness work that you do not want to re-implement around every model run:
|
|
31
33
|
|
|
32
|
-
- verifies and deduplicates Linear webhooks
|
|
33
|
-
- maps issue events to the correct local project
|
|
34
|
+
- verifies and deduplicates Linear and GitHub webhooks
|
|
35
|
+
- maps issue events to the correct local project
|
|
34
36
|
- creates and reuses one durable worktree and branch per issue lifecycle
|
|
35
|
-
- starts
|
|
36
|
-
-
|
|
37
|
+
- starts Codex threads for implementation runs
|
|
38
|
+
- triggers reactive runs for CI failures, review feedback, and merge queue failures
|
|
39
|
+
- persists enough state to correlate the Linear issue, local workspace, run, and Codex thread
|
|
37
40
|
- reports progress back to Linear and forwards follow-up agent input into active runs
|
|
38
41
|
- exposes CLI and optional read-only inspection surfaces so operators can understand what happened
|
|
39
42
|
|
|
@@ -41,10 +44,10 @@ PatchRelay does the deterministic harness work that you do not want to re-implem
|
|
|
41
44
|
|
|
42
45
|
PatchRelay works best when read as five layers with clear ownership:
|
|
43
46
|
|
|
44
|
-
- policy layer: repo workflow files
|
|
45
|
-
- coordination layer: issue claiming,
|
|
47
|
+
- policy layer: repo workflow files (`IMPLEMENTATION_WORKFLOW.md`, `REVIEW_WORKFLOW.md`)
|
|
48
|
+
- coordination layer: issue claiming, run scheduling, retry budgets, and reconciliation
|
|
46
49
|
- execution layer: durable worktrees, Codex threads, and queued turn input delivery
|
|
47
|
-
- integration layer: Linear webhooks, OAuth, project routing, and
|
|
50
|
+
- integration layer: Linear webhooks, GitHub webhooks, OAuth, project routing, and state sync
|
|
48
51
|
- observability layer: CLI inspection, reports, event trails, and operator endpoints
|
|
49
52
|
|
|
50
53
|
That separation is intentional. PatchRelay is not the policy itself and it is not the coding agent. It is the harness that keeps those pieces coordinated in a real repository with real operational state.
|
|
@@ -53,11 +56,11 @@ That separation is intentional. PatchRelay is not the policy itself and it is no
|
|
|
53
56
|
|
|
54
57
|
PatchRelay is designed for a local, operator-owned setup:
|
|
55
58
|
|
|
56
|
-
- PatchRelay service runs on your machine or server
|
|
59
|
+
- PatchRelay service runs on your machine or server (default `127.0.0.1:8787`)
|
|
57
60
|
- Codex runs through `codex app-server`
|
|
58
61
|
- Linear is the control surface
|
|
59
62
|
- `patchrelay` CLI is the operator interface
|
|
60
|
-
- a reverse proxy exposes the Linear-facing routes
|
|
63
|
+
- a reverse proxy exposes the Linear-facing and GitHub-facing webhook routes
|
|
61
64
|
|
|
62
65
|
Linux and Node.js `24+` are the intended runtime.
|
|
63
66
|
|
|
@@ -67,53 +70,59 @@ You will also need:
|
|
|
67
70
|
- `codex`
|
|
68
71
|
- a Linear OAuth app for this PatchRelay deployment
|
|
69
72
|
- a Linear webhook secret
|
|
70
|
-
- a public HTTPS entrypoint such as Caddy, nginx, or a tunnel so Linear can reach your PatchRelay
|
|
71
|
-
|
|
72
|
-
For the exact OAuth app settings and webhook categories, use the Linear onboarding guide.
|
|
73
|
+
- a public HTTPS entrypoint such as Caddy, nginx, or a tunnel so Linear and GitHub can reach your PatchRelay webhooks
|
|
73
74
|
|
|
74
75
|
## How It Works
|
|
75
76
|
|
|
76
|
-
1. A human delegates PatchRelay on an issue to start automation
|
|
77
|
+
1. A human delegates PatchRelay on an issue to start automation.
|
|
77
78
|
2. PatchRelay verifies the webhook and routes the issue to the right local project.
|
|
78
|
-
3. Delegated issues create or reuse the issue worktree and launch
|
|
79
|
+
3. Delegated issues create or reuse the issue worktree and launch an implementation run through `codex app-server`.
|
|
79
80
|
4. PatchRelay persists thread ids, run state, and observations so the work stays inspectable and resumable.
|
|
80
|
-
5.
|
|
81
|
+
5. GitHub webhooks drive reactive loops: CI repair on check failures, review fix on changes requested, and merge queue repair on queue failures.
|
|
82
|
+
6. Native agent prompts and Linear comments can steer the active run. An operator can take over from the exact same worktree when needed.
|
|
83
|
+
|
|
84
|
+
## Factory State Machine
|
|
85
|
+
|
|
86
|
+
Each issue progresses through a factory state machine:
|
|
87
|
+
|
|
88
|
+
```text
|
|
89
|
+
delegated → preparing → implementing → pr_open → awaiting_review
|
|
90
|
+
→ changes_requested (review fix run) → back to implementing
|
|
91
|
+
→ repairing_ci (CI repair run) → back to pr_open
|
|
92
|
+
→ awaiting_queue → done (merged)
|
|
93
|
+
→ repairing_queue (queue repair run) → back to pr_open
|
|
94
|
+
→ escalated or failed (when retry budgets are exhausted)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Run types:
|
|
98
|
+
|
|
99
|
+
- `implementation` — initial coding work
|
|
100
|
+
- `review_fix` — address reviewer feedback
|
|
101
|
+
- `ci_repair` — fix failing CI checks
|
|
102
|
+
- `queue_repair` — fix merge queue failures
|
|
81
103
|
|
|
82
104
|
## Restart And Reconciliation
|
|
83
105
|
|
|
84
106
|
PatchRelay treats restart safety as part of the harness contract, not as a best-effort extra.
|
|
85
107
|
|
|
86
|
-
After a restart, the service
|
|
108
|
+
After a restart, the service can answer:
|
|
87
109
|
|
|
88
110
|
- which issue owns each active worktree
|
|
89
|
-
- which
|
|
111
|
+
- which run was active or queued
|
|
90
112
|
- which Codex thread and turn belong to that work
|
|
91
113
|
- whether the issue is still eligible to continue
|
|
92
114
|
- whether the run should resume, hand off, or fail back to a human state
|
|
93
115
|
|
|
94
|
-
This is why PatchRelay keeps a
|
|
95
|
-
|
|
96
|
-
## Workflow Configuration
|
|
97
|
-
|
|
98
|
-
PatchRelay keeps workflow configuration simple:
|
|
99
|
-
|
|
100
|
-
- route issues to a project by team, issue prefix, or labels
|
|
101
|
-
- when an issue is delegated to PatchRelay, it looks at the current Linear state
|
|
102
|
-
- that Linear state selects the matching workflow to run
|
|
103
|
-
- that workflow selects the repo-local workflow file
|
|
116
|
+
This is why PatchRelay keeps a durable `issues` and `runs` table alongside Codex thread history and Linear state. The goal is not to duplicate the model transcript. The goal is to make automation restartable, inspectable, and recoverable when the process or machine is interrupted.
|
|
104
117
|
|
|
105
|
-
|
|
118
|
+
## Workflow Files
|
|
106
119
|
|
|
107
|
-
-
|
|
108
|
-
- which Linear states should wake each workflow
|
|
109
|
-
- which workflow file belongs to each workflow
|
|
110
|
-
- which active state PatchRelay should set while that workflow is running
|
|
120
|
+
PatchRelay uses repo-local workflow files as prompts for Codex runs:
|
|
111
121
|
|
|
112
|
-
|
|
122
|
+
- `IMPLEMENTATION_WORKFLOW.md` — used for implementation, CI repair, and queue repair runs
|
|
123
|
+
- `REVIEW_WORKFLOW.md` — used for review fix runs
|
|
113
124
|
|
|
114
|
-
|
|
115
|
-
- a push-to-main project can automate implementation and review, then let GitHub Actions handle deployment while PatchRelay moves failures back to `Human Needed`
|
|
116
|
-
- a project with a QA gate can add a `qa` workflow bound to `Ready for QA`
|
|
125
|
+
These files define how the agent should work in that repository. Keep them short and action-oriented.
|
|
117
126
|
|
|
118
127
|
## Access Control
|
|
119
128
|
|
|
@@ -183,13 +192,11 @@ The generated `~/.config/patchrelay/patchrelay.json` is machine-level service co
|
|
|
183
192
|
|
|
184
193
|
### 5. Add workflow docs to the repo
|
|
185
194
|
|
|
186
|
-
|
|
195
|
+
PatchRelay looks for:
|
|
187
196
|
|
|
188
197
|
```text
|
|
189
198
|
IMPLEMENTATION_WORKFLOW.md
|
|
190
199
|
REVIEW_WORKFLOW.md
|
|
191
|
-
DEPLOY_WORKFLOW.md
|
|
192
|
-
CLEANUP_WORKFLOW.md
|
|
193
200
|
```
|
|
194
201
|
|
|
195
202
|
These files define how the agent should work in that repo.
|
|
@@ -214,14 +221,14 @@ Important:
|
|
|
214
221
|
|
|
215
222
|
- Linear needs a public HTTPS URL to reach your webhook.
|
|
216
223
|
- `patchrelay init <public-base-url>` writes `server.public_base_url`, which PatchRelay uses when it prints webhook URLs.
|
|
217
|
-
- For ingress, OAuth app setup, and webhook details, use the self-hosting
|
|
224
|
+
- For ingress, OAuth app setup, and webhook details, use the self-hosting docs.
|
|
218
225
|
|
|
219
226
|
## Daily Loop
|
|
220
227
|
|
|
221
228
|
1. Delegate a Linear issue to the PatchRelay app.
|
|
222
|
-
2.
|
|
223
|
-
3.
|
|
224
|
-
4.
|
|
229
|
+
2. Linear sends the delegation and agent-session webhooks to PatchRelay, which creates or reuses the issue worktree and launches an implementation run.
|
|
230
|
+
3. Follow up in the Linear agent session to steer the active run or wake it with fresh input while it remains delegated.
|
|
231
|
+
4. GitHub webhooks automatically trigger CI repair, review fix, or merge queue repair runs when needed.
|
|
225
232
|
5. Watch progress from the terminal or open the same worktree and take over manually.
|
|
226
233
|
|
|
227
234
|
Useful commands:
|
|
@@ -233,7 +240,7 @@ Useful commands:
|
|
|
233
240
|
- `patchrelay events APP-123 --follow`
|
|
234
241
|
- `patchrelay worktree APP-123 --cd`
|
|
235
242
|
- `patchrelay open APP-123`
|
|
236
|
-
- `patchrelay retry APP-123
|
|
243
|
+
- `patchrelay retry APP-123`
|
|
237
244
|
|
|
238
245
|
`patchrelay open` is the handoff bridge: it opens Codex in the issue worktree and resumes the existing thread when PatchRelay has one.
|
|
239
246
|
|
|
@@ -244,27 +251,22 @@ Today that takeover path is intentionally YOLO mode: it launches Codex with `--d
|
|
|
244
251
|
PatchRelay keeps enough durable state to answer the questions that matter during and after a run:
|
|
245
252
|
|
|
246
253
|
- which worktree and branch belong to an issue
|
|
247
|
-
- which
|
|
254
|
+
- which run is active or queued
|
|
248
255
|
- which Codex thread owns the current work
|
|
249
256
|
- what the agent said
|
|
250
257
|
- which commands it ran
|
|
251
258
|
- which files it changed
|
|
252
|
-
- whether the
|
|
259
|
+
- whether the run completed, failed, or needs handoff
|
|
253
260
|
|
|
254
261
|
## Docs
|
|
255
262
|
|
|
256
263
|
Use the README for the product overview and quick start. Use the docs for operating details:
|
|
257
264
|
|
|
258
|
-
- [Self-hosting and deployment](
|
|
259
|
-
- [
|
|
260
|
-
- [
|
|
261
|
-
- [
|
|
262
|
-
- [
|
|
263
|
-
- [Authoritative vs derived state](https://github.com/krasnoperov/patchrelay/blob/main/docs/state-authority.md)
|
|
264
|
-
- [Persistence audit](https://github.com/krasnoperov/patchrelay/blob/main/docs/persistence-audit.md)
|
|
265
|
-
- [Codex integration details](https://github.com/krasnoperov/patchrelay/blob/main/docs/codex-workflow.md)
|
|
266
|
-
- [Workflow file requirements](https://github.com/krasnoperov/patchrelay/blob/main/docs/IMPLEMENTATION_WORKFLOW_REQUIREMENTS.md)
|
|
267
|
-
- [Security policy](https://github.com/krasnoperov/patchrelay/blob/main/SECURITY.md)
|
|
265
|
+
- [Self-hosting and deployment](./docs/self-hosting.md)
|
|
266
|
+
- [Architecture](./docs/architecture.md)
|
|
267
|
+
- [Design principles](./docs/design-docs/core-beliefs.md)
|
|
268
|
+
- [External reference patterns](./docs/references/external-patterns.md)
|
|
269
|
+
- [Security policy](./SECURITY.md)
|
|
268
270
|
|
|
269
271
|
## Status
|
|
270
272
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
function
|
|
2
|
-
return
|
|
1
|
+
function formatRunLabel(runType) {
|
|
2
|
+
return runType.replace(/[-_]+/g, " ");
|
|
3
3
|
}
|
|
4
4
|
function titleCase(value) {
|
|
5
5
|
return value
|
|
@@ -8,27 +8,27 @@ function titleCase(value) {
|
|
|
8
8
|
.map((word) => word[0].toUpperCase() + word.slice(1))
|
|
9
9
|
.join(" ");
|
|
10
10
|
}
|
|
11
|
-
function buildPlan(
|
|
12
|
-
const
|
|
11
|
+
function buildPlan(runType, statuses) {
|
|
12
|
+
const label = titleCase(formatRunLabel(runType));
|
|
13
13
|
return [
|
|
14
14
|
{ content: "Prepare workspace", status: statuses[0] },
|
|
15
|
-
{ content: `Run ${
|
|
16
|
-
{ content: "Review
|
|
15
|
+
{ content: `Run ${label}`, status: statuses[1] },
|
|
16
|
+
{ content: "Review outcome", status: statuses[2] },
|
|
17
17
|
];
|
|
18
18
|
}
|
|
19
|
-
export function buildPreparingSessionPlan(
|
|
20
|
-
return buildPlan(
|
|
19
|
+
export function buildPreparingSessionPlan(runType) {
|
|
20
|
+
return buildPlan(runType, ["inProgress", "pending", "pending"]);
|
|
21
21
|
}
|
|
22
|
-
export function buildRunningSessionPlan(
|
|
23
|
-
return buildPlan(
|
|
22
|
+
export function buildRunningSessionPlan(runType) {
|
|
23
|
+
return buildPlan(runType, ["completed", "inProgress", "pending"]);
|
|
24
24
|
}
|
|
25
|
-
export function buildCompletedSessionPlan(
|
|
26
|
-
return buildPlan(
|
|
25
|
+
export function buildCompletedSessionPlan(runType) {
|
|
26
|
+
return buildPlan(runType, ["completed", "completed", "completed"]);
|
|
27
27
|
}
|
|
28
|
-
export function buildAwaitingHandoffSessionPlan(
|
|
29
|
-
return buildPlan(
|
|
28
|
+
export function buildAwaitingHandoffSessionPlan(runType) {
|
|
29
|
+
return buildPlan(runType, ["completed", "completed", "inProgress"]);
|
|
30
30
|
}
|
|
31
|
-
export function buildFailedSessionPlan(
|
|
32
|
-
const workflowStepStatus =
|
|
33
|
-
return buildPlan(
|
|
31
|
+
export function buildFailedSessionPlan(runType, run) {
|
|
32
|
+
const workflowStepStatus = run?.threadId || run?.turnId ? "completed" : "inProgress";
|
|
33
|
+
return buildPlan(runType, ["completed", workflowStepStatus, "pending"]);
|
|
34
34
|
}
|
package/dist/build-info.json
CHANGED
package/dist/cli/args.js
CHANGED
|
@@ -69,7 +69,7 @@ export function resolveCommand(parsed) {
|
|
|
69
69
|
export function hasHelpFlag(parsed) {
|
|
70
70
|
return parsed.flags.get("help") === true;
|
|
71
71
|
}
|
|
72
|
-
export function
|
|
72
|
+
export function getRunTypeFlag(value) {
|
|
73
73
|
if (typeof value !== "string") {
|
|
74
74
|
return undefined;
|
|
75
75
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { setTimeout as delay } from "node:timers/promises";
|
|
2
|
-
import {
|
|
2
|
+
import { getRunTypeFlag, parsePositiveIntegerFlag } from "../args.js";
|
|
3
3
|
import { formatJson } from "../formatters/json.js";
|
|
4
4
|
import { formatEvents, formatInspect, formatList, formatLive, formatOpen, formatReport, formatRetry, formatWorktree } from "../formatters/text.js";
|
|
5
5
|
import { buildOpenCommand } from "../interactive.js";
|
|
@@ -28,7 +28,7 @@ export async function handleLiveCommand(params) {
|
|
|
28
28
|
throw new Error(`No active stage found for ${issueKey}`);
|
|
29
29
|
}
|
|
30
30
|
writeOutput(params.stdout, params.json ? formatJson(result) : formatLive(result));
|
|
31
|
-
if (!watch || result.
|
|
31
|
+
if (!watch || result.run.status !== "running") {
|
|
32
32
|
break;
|
|
33
33
|
}
|
|
34
34
|
await delay(2000);
|
|
@@ -41,13 +41,13 @@ export async function handleReportCommand(params) {
|
|
|
41
41
|
throw new Error("report requires <issueKey>.");
|
|
42
42
|
}
|
|
43
43
|
const reportOptions = {};
|
|
44
|
-
const
|
|
45
|
-
if (
|
|
46
|
-
reportOptions.
|
|
44
|
+
const runType = getRunTypeFlag(params.parsed.flags.get("run-type"));
|
|
45
|
+
if (runType) {
|
|
46
|
+
reportOptions.runType = runType;
|
|
47
47
|
}
|
|
48
|
-
const
|
|
49
|
-
if (
|
|
50
|
-
reportOptions.
|
|
48
|
+
const runId = parsePositiveIntegerFlag(params.parsed.flags.get("run"), "--run");
|
|
49
|
+
if (runId !== undefined) {
|
|
50
|
+
reportOptions.runId = runId;
|
|
51
51
|
}
|
|
52
52
|
const result = params.data.report(issueKey, reportOptions);
|
|
53
53
|
if (!result) {
|
|
@@ -63,22 +63,22 @@ export async function handleEventsCommand(params) {
|
|
|
63
63
|
}
|
|
64
64
|
const follow = params.parsed.flags.get("follow") === true;
|
|
65
65
|
let afterId;
|
|
66
|
-
let
|
|
66
|
+
let runId = parsePositiveIntegerFlag(params.parsed.flags.get("run"), "--run");
|
|
67
67
|
do {
|
|
68
68
|
const result = params.data.events(issueKey, {
|
|
69
|
-
...(
|
|
69
|
+
...(runId !== undefined ? { runId } : {}),
|
|
70
70
|
...(typeof params.parsed.flags.get("method") === "string" ? { method: String(params.parsed.flags.get("method")) } : {}),
|
|
71
71
|
...(afterId !== undefined ? { afterId } : {}),
|
|
72
72
|
});
|
|
73
73
|
if (!result) {
|
|
74
|
-
throw new Error(`
|
|
74
|
+
throw new Error(`Run not found for ${issueKey}`);
|
|
75
75
|
}
|
|
76
|
-
|
|
76
|
+
runId = result.run.id;
|
|
77
77
|
if (result.events.length > 0) {
|
|
78
78
|
writeOutput(params.stdout, params.json ? formatJson(result) : formatEvents(result));
|
|
79
79
|
afterId = result.events.at(-1)?.id;
|
|
80
80
|
}
|
|
81
|
-
if (!follow || result.
|
|
81
|
+
if (!follow || result.run.status !== "running") {
|
|
82
82
|
break;
|
|
83
83
|
}
|
|
84
84
|
await delay(2000);
|
|
@@ -115,7 +115,7 @@ export async function handleOpenCommand(params) {
|
|
|
115
115
|
if (!result) {
|
|
116
116
|
throw new Error(`Workspace not found for ${issueKey}`);
|
|
117
117
|
}
|
|
118
|
-
const openCommand = buildOpenCommand(params.config, result.
|
|
118
|
+
const openCommand = buildOpenCommand(params.config, result.worktreePath, result.resumeThreadId);
|
|
119
119
|
writeOutput(params.stdout, formatOpen(result, openCommand));
|
|
120
120
|
return 0;
|
|
121
121
|
}
|
|
@@ -123,7 +123,7 @@ export async function handleOpenCommand(params) {
|
|
|
123
123
|
if (!result) {
|
|
124
124
|
throw new Error(`Workspace not found for ${issueKey}`);
|
|
125
125
|
}
|
|
126
|
-
const openCommand = buildOpenCommand(params.config, result.
|
|
126
|
+
const openCommand = buildOpenCommand(params.config, result.worktreePath, result.resumeThreadId);
|
|
127
127
|
return await params.runInteractive(openCommand.command, openCommand.args);
|
|
128
128
|
}
|
|
129
129
|
export async function handleRetryCommand(params) {
|
|
@@ -132,9 +132,9 @@ export async function handleRetryCommand(params) {
|
|
|
132
132
|
throw new Error("retry requires <issueKey>.");
|
|
133
133
|
}
|
|
134
134
|
const retryOptions = {};
|
|
135
|
-
const
|
|
136
|
-
if (
|
|
137
|
-
retryOptions.
|
|
135
|
+
const runType = getRunTypeFlag(params.parsed.flags.get("run-type"));
|
|
136
|
+
if (runType) {
|
|
137
|
+
retryOptions.runType = runType;
|
|
138
138
|
}
|
|
139
139
|
if (typeof params.parsed.flags.get("reason") === "string") {
|
|
140
140
|
retryOptions.reason = String(params.parsed.flags.get("reason"));
|