@neriros/ralphy 2.7.5 → 2.7.7
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 +23 -12
- package/dist/cli/index.js +156 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -104,11 +104,15 @@ Defaults are written to `ralphy.config.json` on first run; CLI flags override co
|
|
|
104
104
|
"doneStatus": "In Review",
|
|
105
105
|
"doneLabel": "ralphy-done",
|
|
106
106
|
"postComments": true,
|
|
107
|
+
"updateEveryIterations": 10,
|
|
107
108
|
},
|
|
108
109
|
"useWorktree": true,
|
|
109
110
|
"cleanupWorktreeOnSuccess": false,
|
|
110
111
|
"setupScript": "bun install",
|
|
111
112
|
"teardownScript": "git status",
|
|
113
|
+
"appendPrompt": "Always run lint before committing.",
|
|
114
|
+
"createPrOnSuccess": true,
|
|
115
|
+
"prBaseBranch": "main",
|
|
112
116
|
}
|
|
113
117
|
```
|
|
114
118
|
|
|
@@ -120,6 +124,12 @@ With `--worktree` (or `useWorktree: true` in config) each task runs in an isolat
|
|
|
120
124
|
|
|
121
125
|
Use `setupScript` (run inside the worktree right after scaffolding) to install dependencies, copy `.env`, etc. Use `teardownScript` (run after the loop exits, before any worktree cleanup) to gather artifacts or roll back local mutations. Both run via `sh -c`; failures are logged but never block the loop. With `cleanupWorktreeOnSuccess: true` the worktree is removed when the worker exits 0 — failed workers always keep their worktree (and branch) for human inspection.
|
|
122
126
|
|
|
127
|
+
**`appendPrompt`** (or `--prompt` in agent mode) is appended to every scaffolded `proposal.md` under an `## Additional instructions` section — use it for cross-cutting guidance every task should see.
|
|
128
|
+
|
|
129
|
+
**`updateEveryIterations`** (default `10`, `0` disables) posts a "🔄 Ralph progress update: iteration N" comment on the Linear issue every N task iterations. Requires `postComments: true`.
|
|
130
|
+
|
|
131
|
+
**`createPrOnSuccess`** (or `--create-pr`) pushes the worker's branch and opens a GitHub PR via `gh` after a clean exit. Requires `--worktree` (the PR needs a branch to point at) and the `gh` CLI authenticated. The PR title is `<ID>: <title>`, the body links the Linear issue. If a PR already exists for the branch the existing URL is reported (idempotent for retries). `prBaseBranch` defaults to `main`.
|
|
132
|
+
|
|
123
133
|
Failed workers (non-zero exit) are not marked processed, so they'll be retried on the next poll. SIGINT/SIGTERM cleanly stops polling and kills active workers. All Linear side effects are best-effort — failures log a warning but never block the task loop.
|
|
124
134
|
|
|
125
135
|
## CLI Options
|
|
@@ -143,18 +153,19 @@ Failed workers (non-zero exit) are not marked processed, so they'll be retried o
|
|
|
143
153
|
|
|
144
154
|
### Agent mode flags
|
|
145
155
|
|
|
146
|
-
| Option | Description
|
|
147
|
-
| ----------------------------- |
|
|
148
|
-
| `--linear-team <key>` | Linear team key (e.g. `ENG`)
|
|
149
|
-
| `--linear-assignee <id>` | Filter by assignee (user id, email, or `me`)
|
|
150
|
-
| `--linear-status <name>` | Filter by status name (repeatable)
|
|
151
|
-
| `--linear-label <name>` | Filter by label name (repeatable, any-of)
|
|
152
|
-
| `--poll-interval <s>` | Seconds between Linear polls (default: 60)
|
|
153
|
-
| `--concurrency <n>` | Max concurrent task loops (default: 1)
|
|
154
|
-
| `--worktree` | Run each task in its own git worktree
|
|
155
|
-
| `--in-progress-status <name>` | Linear status to set when work starts
|
|
156
|
-
| `--done-status <name>` | Linear status to set on successful completion
|
|
157
|
-
| `--done-label <name>` | Linear label to add on successful completion
|
|
156
|
+
| Option | Description |
|
|
157
|
+
| ----------------------------- | --------------------------------------------------------------------- |
|
|
158
|
+
| `--linear-team <key>` | Linear team key (e.g. `ENG`) |
|
|
159
|
+
| `--linear-assignee <id>` | Filter by assignee (user id, email, or `me`) |
|
|
160
|
+
| `--linear-status <name>` | Filter by status name (repeatable) |
|
|
161
|
+
| `--linear-label <name>` | Filter by label name (repeatable, any-of) |
|
|
162
|
+
| `--poll-interval <s>` | Seconds between Linear polls (default: 60) |
|
|
163
|
+
| `--concurrency <n>` | Max concurrent task loops (default: 1) |
|
|
164
|
+
| `--worktree` | Run each task in its own git worktree |
|
|
165
|
+
| `--in-progress-status <name>` | Linear status to set when work starts |
|
|
166
|
+
| `--done-status <name>` | Linear status to set on successful completion |
|
|
167
|
+
| `--done-label <name>` | Linear label to add on successful completion |
|
|
168
|
+
| `--create-pr` | Push worker branch + open a GitHub PR on success (needs `--worktree`) |
|
|
158
169
|
|
|
159
170
|
## OpenSpec Flow
|
|
160
171
|
|
package/dist/cli/index.js
CHANGED
|
@@ -56131,7 +56131,7 @@ var HELP_TEXT = [
|
|
|
56131
56131
|
"",
|
|
56132
56132
|
"Options:",
|
|
56133
56133
|
" --name <name> Change name (required for most commands)",
|
|
56134
|
-
" --prompt <text> Task description",
|
|
56134
|
+
" --prompt <text> Task description (in agent mode: appended to every scaffolded proposal)",
|
|
56135
56135
|
" --prompt-file <path> Read prompt from file",
|
|
56136
56136
|
" --model <model> Set model (haiku|sonnet|opus)",
|
|
56137
56137
|
" --claude [model] Use Claude engine (haiku|sonnet|opus, default: opus)",
|
|
@@ -56156,6 +56156,7 @@ var HELP_TEXT = [
|
|
|
56156
56156
|
" --in-progress-status <name> Linear status to set when work starts on an issue",
|
|
56157
56157
|
" --done-status <name> Linear status to set when work completes successfully",
|
|
56158
56158
|
" --done-label <name> Linear label to add when work completes successfully",
|
|
56159
|
+
" --create-pr Push the worker branch and open a GitHub PR on success (needs --worktree)",
|
|
56159
56160
|
"",
|
|
56160
56161
|
" --help, -h Show this help message",
|
|
56161
56162
|
"",
|
|
@@ -56195,7 +56196,8 @@ async function parseArgs(argv) {
|
|
|
56195
56196
|
worktree: false,
|
|
56196
56197
|
inProgressStatus: "",
|
|
56197
56198
|
doneStatus: "",
|
|
56198
|
-
doneLabel: ""
|
|
56199
|
+
doneLabel: "",
|
|
56200
|
+
createPr: false
|
|
56199
56201
|
};
|
|
56200
56202
|
let expectModel = false;
|
|
56201
56203
|
let expectModelFlag = false;
|
|
@@ -56416,6 +56418,9 @@ async function parseArgs(argv) {
|
|
|
56416
56418
|
case "--done-label":
|
|
56417
56419
|
expectDoneLabel = true;
|
|
56418
56420
|
break;
|
|
56421
|
+
case "--create-pr":
|
|
56422
|
+
result2.createPr = true;
|
|
56423
|
+
break;
|
|
56419
56424
|
default:
|
|
56420
56425
|
if (VALID_MODES.has(arg)) {
|
|
56421
56426
|
result2.mode = arg;
|
|
@@ -69772,7 +69777,7 @@ function changeNameForIssue(issue) {
|
|
|
69772
69777
|
const slug = issue.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
|
|
69773
69778
|
return slug ? `${issue.identifier.toLowerCase()}-${slug}` : issue.identifier.toLowerCase();
|
|
69774
69779
|
}
|
|
69775
|
-
async function scaffoldChangeForIssue(tasksDir, statesDir, issue, comments = []) {
|
|
69780
|
+
async function scaffoldChangeForIssue(tasksDir, statesDir, issue, comments = [], appendPrompt = "") {
|
|
69776
69781
|
const name = changeNameForIssue(issue);
|
|
69777
69782
|
const changeDir = join11(tasksDir, name);
|
|
69778
69783
|
const stateDir = join11(statesDir, name);
|
|
@@ -69802,6 +69807,7 @@ async function scaffoldChangeForIssue(tasksDir, statesDir, issue, comments = [])
|
|
|
69802
69807
|
"",
|
|
69803
69808
|
issue.description?.trim() || "_No description provided in Linear._",
|
|
69804
69809
|
...commentsBlock,
|
|
69810
|
+
...appendPrompt.trim() ? ["", "## Additional instructions", "", appendPrompt.trim()] : [],
|
|
69805
69811
|
"",
|
|
69806
69812
|
"## Steering",
|
|
69807
69813
|
"",
|
|
@@ -69843,6 +69849,9 @@ var RalphyConfigSchema = exports_external.object({
|
|
|
69843
69849
|
cleanupWorktreeOnSuccess: exports_external.boolean().default(false),
|
|
69844
69850
|
setupScript: exports_external.string().optional(),
|
|
69845
69851
|
teardownScript: exports_external.string().optional(),
|
|
69852
|
+
appendPrompt: exports_external.string().optional(),
|
|
69853
|
+
createPrOnSuccess: exports_external.boolean().default(false),
|
|
69854
|
+
prBaseBranch: exports_external.string().default("main"),
|
|
69846
69855
|
engine: exports_external.enum(["claude", "codex"]).default("claude"),
|
|
69847
69856
|
model: exports_external.enum(["haiku", "sonnet", "opus"]).default("opus"),
|
|
69848
69857
|
linear: exports_external.object({
|
|
@@ -69853,7 +69862,8 @@ var RalphyConfigSchema = exports_external.object({
|
|
|
69853
69862
|
inProgressStatus: exports_external.string().optional(),
|
|
69854
69863
|
doneStatus: exports_external.string().optional(),
|
|
69855
69864
|
doneLabel: exports_external.string().optional(),
|
|
69856
|
-
postComments: exports_external.boolean().default(true)
|
|
69865
|
+
postComments: exports_external.boolean().default(true),
|
|
69866
|
+
updateEveryIterations: exports_external.number().int().nonnegative().default(10)
|
|
69857
69867
|
}).default({ statuses: [], labels: [], postComments: true })
|
|
69858
69868
|
}).default({
|
|
69859
69869
|
concurrency: 1,
|
|
@@ -69939,8 +69949,37 @@ class AgentCoordinator {
|
|
|
69939
69949
|
state.lastPollAt = new Date().toISOString();
|
|
69940
69950
|
await this.deps.saveState(state);
|
|
69941
69951
|
this.spawnNext();
|
|
69952
|
+
await this.reportProgress();
|
|
69942
69953
|
return { found: issues.length, added };
|
|
69943
69954
|
}
|
|
69955
|
+
async reportProgress() {
|
|
69956
|
+
const updater = this.deps.updater;
|
|
69957
|
+
const everyN = this.opts.commentEveryIterations ?? 0;
|
|
69958
|
+
if (everyN <= 0 || !updater || this.opts.postComments === false || !this.deps.getIterationCount) {
|
|
69959
|
+
return;
|
|
69960
|
+
}
|
|
69961
|
+
for (const w of this.workers) {
|
|
69962
|
+
let count;
|
|
69963
|
+
try {
|
|
69964
|
+
count = await this.deps.getIterationCount(w.changeName);
|
|
69965
|
+
} catch (err) {
|
|
69966
|
+
this.deps.onLog(`! iteration count read failed for ${w.issueIdentifier}: ${err.message}`, "yellow");
|
|
69967
|
+
continue;
|
|
69968
|
+
}
|
|
69969
|
+
if (count < everyN)
|
|
69970
|
+
continue;
|
|
69971
|
+
const currMilestone = Math.floor(count / everyN);
|
|
69972
|
+
const lastMilestone = Math.floor(w.lastReportedIteration / everyN);
|
|
69973
|
+
if (currMilestone <= lastMilestone)
|
|
69974
|
+
continue;
|
|
69975
|
+
try {
|
|
69976
|
+
await updater.postComment(w.issue, `\uD83D\uDD04 Ralph progress update: iteration ${count} on \`${w.changeName}\``);
|
|
69977
|
+
w.lastReportedIteration = count;
|
|
69978
|
+
} catch (err) {
|
|
69979
|
+
this.deps.onLog(`! Linear progress comment failed for ${w.issueIdentifier}: ${err.message}`, "red");
|
|
69980
|
+
}
|
|
69981
|
+
}
|
|
69982
|
+
}
|
|
69944
69983
|
spawnNext() {
|
|
69945
69984
|
if (this.stopped || !this.state)
|
|
69946
69985
|
return;
|
|
@@ -69970,7 +70009,9 @@ class AgentCoordinator {
|
|
|
69970
70009
|
changeName,
|
|
69971
70010
|
issueId: issue.id,
|
|
69972
70011
|
issueIdentifier: issue.identifier,
|
|
69973
|
-
|
|
70012
|
+
issue,
|
|
70013
|
+
kill: handle.kill,
|
|
70014
|
+
lastReportedIteration: 0
|
|
69974
70015
|
};
|
|
69975
70016
|
this.workers.push(worker);
|
|
69976
70017
|
this.pendingIds.delete(issue.id);
|
|
@@ -70099,6 +70140,53 @@ async function removeWorktree(projectRoot, cwd2, runner) {
|
|
|
70099
70140
|
await runner.run(["worktree", "remove", "--force", cwd2], projectRoot);
|
|
70100
70141
|
}
|
|
70101
70142
|
|
|
70143
|
+
// apps/cli/src/agent/pr.ts
|
|
70144
|
+
function defaultTitle(issue) {
|
|
70145
|
+
return `${issue.identifier}: ${issue.title}`;
|
|
70146
|
+
}
|
|
70147
|
+
function defaultBody(issue, branch) {
|
|
70148
|
+
return [
|
|
70149
|
+
`Auto-generated by Ralph for ${issue.identifier}.`,
|
|
70150
|
+
"",
|
|
70151
|
+
`Source: ${issue.url}`,
|
|
70152
|
+
`Branch: \`${branch}\``,
|
|
70153
|
+
"",
|
|
70154
|
+
issue.description?.trim() ? `## Description
|
|
70155
|
+
|
|
70156
|
+
${issue.description.trim()}` : ""
|
|
70157
|
+
].filter(Boolean).join(`
|
|
70158
|
+
`);
|
|
70159
|
+
}
|
|
70160
|
+
async function createPullRequest(input, runner) {
|
|
70161
|
+
const base2 = input.base ?? "main";
|
|
70162
|
+
const log2 = await runner.run(["git", "log", "--oneline", `${base2}..HEAD`, "--no-merges"], input.cwd);
|
|
70163
|
+
if (log2.stdout.trim() === "")
|
|
70164
|
+
return null;
|
|
70165
|
+
await runner.run(["git", "push", "-u", "origin", input.branch], input.cwd);
|
|
70166
|
+
const existing = await runner.run([
|
|
70167
|
+
"gh",
|
|
70168
|
+
"pr",
|
|
70169
|
+
"list",
|
|
70170
|
+
"--head",
|
|
70171
|
+
input.branch,
|
|
70172
|
+
"--state",
|
|
70173
|
+
"open",
|
|
70174
|
+
"--json",
|
|
70175
|
+
"url",
|
|
70176
|
+
"--jq",
|
|
70177
|
+
".[0].url // empty"
|
|
70178
|
+
], input.cwd);
|
|
70179
|
+
const existingUrl = existing.stdout.trim();
|
|
70180
|
+
if (existingUrl)
|
|
70181
|
+
return { url: existingUrl, created: false };
|
|
70182
|
+
const title = defaultTitle(input.issue);
|
|
70183
|
+
const body = defaultBody(input.issue, input.branch);
|
|
70184
|
+
const created = await runner.run(["gh", "pr", "create", "--base", base2, "--title", title, "--body", body], input.cwd);
|
|
70185
|
+
const url = created.stdout.trim().split(`
|
|
70186
|
+
`).pop() ?? "";
|
|
70187
|
+
return { url, created: true };
|
|
70188
|
+
}
|
|
70189
|
+
|
|
70102
70190
|
// apps/cli/src/components/AgentMode.tsx
|
|
70103
70191
|
var jsx_dev_runtime9 = __toESM(require_jsx_dev_runtime(), 1);
|
|
70104
70192
|
import { join as join14 } from "path";
|
|
@@ -70117,6 +70205,21 @@ var bunGitRunner = {
|
|
|
70117
70205
|
return { stdout, stderr };
|
|
70118
70206
|
}
|
|
70119
70207
|
};
|
|
70208
|
+
var bunCmdRunner = {
|
|
70209
|
+
run: async (cmd, cwd2) => {
|
|
70210
|
+
const proc = Bun.spawn({ cmd, cwd: cwd2, stdout: "pipe", stderr: "pipe" });
|
|
70211
|
+
const stdout = await new Response(proc.stdout).text();
|
|
70212
|
+
const stderr = await new Response(proc.stderr).text();
|
|
70213
|
+
const code = await proc.exited;
|
|
70214
|
+
if (code !== 0) {
|
|
70215
|
+
const err = new Error(`command \`${cmd[0]}\` failed`);
|
|
70216
|
+
err.stderr = stderr;
|
|
70217
|
+
err.code = code;
|
|
70218
|
+
throw err;
|
|
70219
|
+
}
|
|
70220
|
+
return { stdout, stderr };
|
|
70221
|
+
}
|
|
70222
|
+
};
|
|
70120
70223
|
var lineCounter = 0;
|
|
70121
70224
|
function nextId() {
|
|
70122
70225
|
lineCounter += 1;
|
|
@@ -70146,10 +70249,13 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
70146
70249
|
exit();
|
|
70147
70250
|
return;
|
|
70148
70251
|
}
|
|
70252
|
+
const inProgressName = args.inProgressStatus || cfg.linear.inProgressStatus;
|
|
70253
|
+
const baseStatuses = args.linearStatus.length ? args.linearStatus : cfg.linear.statuses;
|
|
70254
|
+
const effectiveStatuses = inProgressName && baseStatuses.length > 0 && !baseStatuses.includes(inProgressName) ? [...baseStatuses, inProgressName] : baseStatuses;
|
|
70149
70255
|
const filter2 = {
|
|
70150
70256
|
team: args.linearTeam || cfg.linear.team,
|
|
70151
70257
|
assignee: args.linearAssignee || cfg.linear.assignee,
|
|
70152
|
-
statuses:
|
|
70258
|
+
statuses: effectiveStatuses,
|
|
70153
70259
|
labels: args.linearLabel.length ? args.linearLabel : cfg.linear.labels
|
|
70154
70260
|
};
|
|
70155
70261
|
const stateCache = new Map;
|
|
@@ -70157,6 +70263,9 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
70157
70263
|
const teamKeyOf = (issue) => issue.identifier.split("-")[0];
|
|
70158
70264
|
const useWorktree = args.worktree || cfg.useWorktree;
|
|
70159
70265
|
const cwdByChange = new Map;
|
|
70266
|
+
const statesDirByChange = new Map;
|
|
70267
|
+
const branchByChange = new Map;
|
|
70268
|
+
const issueByChange = new Map;
|
|
70160
70269
|
async function runScript(label, cmd, cwd2) {
|
|
70161
70270
|
appendLog(` ${label}: ${cmd}`, "gray");
|
|
70162
70271
|
const proc = Bun.spawn({
|
|
@@ -70185,11 +70294,13 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
70185
70294
|
let workerCwd = projectRoot;
|
|
70186
70295
|
let scaffoldTasksDir = tasksDir;
|
|
70187
70296
|
let scaffoldStatesDir = statesDir;
|
|
70297
|
+
let workerBranch = null;
|
|
70188
70298
|
const probeName = issue.identifier.toLowerCase();
|
|
70189
70299
|
if (useWorktree) {
|
|
70190
70300
|
try {
|
|
70191
70301
|
const wt = await createWorktree(projectRoot, probeName, bunGitRunner);
|
|
70192
70302
|
workerCwd = wt.cwd;
|
|
70303
|
+
workerBranch = wt.branch;
|
|
70193
70304
|
scaffoldTasksDir = join14(wt.cwd, "openspec", "changes");
|
|
70194
70305
|
scaffoldStatesDir = join14(wt.cwd, ".ralph", "tasks");
|
|
70195
70306
|
appendLog(` ${issue.identifier} worktree: ${wt.cwd} (${wt.branch})`, "gray");
|
|
@@ -70197,8 +70308,13 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
70197
70308
|
appendLog(`! worktree create failed for ${issue.identifier}: ${err.message} \u2014 falling back to project root`, "yellow");
|
|
70198
70309
|
}
|
|
70199
70310
|
}
|
|
70200
|
-
const
|
|
70311
|
+
const appendPrompt = args.prompt || cfg.appendPrompt || "";
|
|
70312
|
+
const changeName = await scaffoldChangeForIssue(scaffoldTasksDir, scaffoldStatesDir, issue, comments, appendPrompt);
|
|
70201
70313
|
cwdByChange.set(changeName, workerCwd);
|
|
70314
|
+
statesDirByChange.set(changeName, scaffoldStatesDir);
|
|
70315
|
+
issueByChange.set(changeName, issue);
|
|
70316
|
+
if (workerBranch)
|
|
70317
|
+
branchByChange.set(changeName, workerBranch);
|
|
70202
70318
|
if (cfg.setupScript) {
|
|
70203
70319
|
await runScript("setup", cfg.setupScript, workerCwd);
|
|
70204
70320
|
}
|
|
@@ -70228,14 +70344,33 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
70228
70344
|
stderr: "ignore",
|
|
70229
70345
|
stdin: "ignore"
|
|
70230
70346
|
});
|
|
70347
|
+
const wantPr = args.createPr || cfg.createPrOnSuccess;
|
|
70231
70348
|
const wrapped = proc.exited.then(async (code) => {
|
|
70232
70349
|
if (cfg.teardownScript) {
|
|
70233
70350
|
try {
|
|
70234
70351
|
await runScript("teardown", cfg.teardownScript, cwd2);
|
|
70235
70352
|
} catch {}
|
|
70236
70353
|
}
|
|
70354
|
+
const ok = code === 0;
|
|
70355
|
+
if (ok && wantPr) {
|
|
70356
|
+
const branch = branchByChange.get(changeName);
|
|
70357
|
+
const prIssue = issueByChange.get(changeName);
|
|
70358
|
+
if (!branch || !prIssue) {
|
|
70359
|
+
appendLog(`! createPr requested but no worktree branch is tracked for ${changeName} (use --worktree)`, "yellow");
|
|
70360
|
+
} else {
|
|
70361
|
+
try {
|
|
70362
|
+
const pr = await createPullRequest({ cwd: cwd2, branch, issue: prIssue, base: cfg.prBaseBranch }, bunCmdRunner);
|
|
70363
|
+
if (!pr) {
|
|
70364
|
+
appendLog(` no commits ahead of ${cfg.prBaseBranch} \u2014 skipping PR`, "gray");
|
|
70365
|
+
} else {
|
|
70366
|
+
appendLog(` ${pr.created ? "opened" : "found existing"} PR: ${pr.url}`, "green");
|
|
70367
|
+
}
|
|
70368
|
+
} catch (err) {
|
|
70369
|
+
appendLog(`! PR create failed for ${changeName}: ${err.message}`, "red");
|
|
70370
|
+
}
|
|
70371
|
+
}
|
|
70372
|
+
}
|
|
70237
70373
|
if (useWorktree && cwd2 !== projectRoot) {
|
|
70238
|
-
const ok = code === 0;
|
|
70239
70374
|
if (ok && cfg.cleanupWorktreeOnSuccess) {
|
|
70240
70375
|
try {
|
|
70241
70376
|
await removeWorktree(projectRoot, cwd2, bunGitRunner);
|
|
@@ -70246,6 +70381,9 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
70246
70381
|
}
|
|
70247
70382
|
}
|
|
70248
70383
|
cwdByChange.delete(changeName);
|
|
70384
|
+
statesDirByChange.delete(changeName);
|
|
70385
|
+
branchByChange.delete(changeName);
|
|
70386
|
+
issueByChange.delete(changeName);
|
|
70249
70387
|
return code;
|
|
70250
70388
|
});
|
|
70251
70389
|
return { exited: wrapped, kill: () => proc.kill() };
|
|
@@ -70254,6 +70392,14 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
70254
70392
|
saveState: (s) => writeAgentState(projectRoot, s),
|
|
70255
70393
|
onLog: appendLog,
|
|
70256
70394
|
onWorkersChanged: () => setTick((t) => t + 1),
|
|
70395
|
+
getIterationCount: async (changeName) => {
|
|
70396
|
+
const dir = statesDirByChange.get(changeName) ?? statesDir;
|
|
70397
|
+
const file = Bun.file(join14(dir, changeName, ".ralph-state.json"));
|
|
70398
|
+
if (!await file.exists())
|
|
70399
|
+
return 0;
|
|
70400
|
+
const json = await file.json();
|
|
70401
|
+
return json.iteration ?? 0;
|
|
70402
|
+
},
|
|
70257
70403
|
updater: {
|
|
70258
70404
|
postComment: (issue, body) => addIssueComment(apiKey, issue.id, body),
|
|
70259
70405
|
setState: (issue, stateId) => updateIssueState(apiKey, issue.id, stateId),
|
|
@@ -70285,7 +70431,8 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
70285
70431
|
inProgressStatus: args.inProgressStatus || cfg.linear.inProgressStatus,
|
|
70286
70432
|
doneStatus: args.doneStatus || cfg.linear.doneStatus,
|
|
70287
70433
|
doneLabel: args.doneLabel || cfg.linear.doneLabel,
|
|
70288
|
-
postComments: cfg.linear.postComments
|
|
70434
|
+
postComments: cfg.linear.postComments,
|
|
70435
|
+
commentEveryIterations: cfg.linear.updateEveryIterations
|
|
70289
70436
|
});
|
|
70290
70437
|
coordRef.current = coord2;
|
|
70291
70438
|
await coord2.init();
|