@neriros/ralphy 2.7.3 → 2.7.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.
- package/README.md +22 -8
- package/dist/cli/index.js +182 -34
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -105,11 +105,21 @@ Defaults are written to `ralphy.config.json` on first run; CLI flags override co
|
|
|
105
105
|
"doneLabel": "ralphy-done",
|
|
106
106
|
"postComments": true,
|
|
107
107
|
},
|
|
108
|
+
"useWorktree": true,
|
|
109
|
+
"cleanupWorktreeOnSuccess": false,
|
|
110
|
+
"setupScript": "bun install",
|
|
111
|
+
"teardownScript": "git status",
|
|
108
112
|
}
|
|
109
113
|
```
|
|
110
114
|
|
|
111
115
|
`doneStatus` and `doneLabel` are independent — set either, both, or neither. Use `doneLabel` if your team marks completion via a label rather than a workflow state.
|
|
112
116
|
|
|
117
|
+
#### Per-task git worktrees
|
|
118
|
+
|
|
119
|
+
With `--worktree` (or `useWorktree: true` in config) each task runs in an isolated worktree at `.ralph/worktrees/<change-name>` checked out onto a fresh `ralph/<change-name>` branch. The change is scaffolded _inside_ the worktree, and the loop's cwd is the worktree, so concurrent workers can't stomp on each other.
|
|
120
|
+
|
|
121
|
+
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
|
+
|
|
113
123
|
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.
|
|
114
124
|
|
|
115
125
|
## CLI Options
|
|
@@ -133,14 +143,18 @@ Failed workers (non-zero exit) are not marked processed, so they'll be retried o
|
|
|
133
143
|
|
|
134
144
|
### Agent mode flags
|
|
135
145
|
|
|
136
|
-
| Option
|
|
137
|
-
|
|
|
138
|
-
| `--linear-team <key>`
|
|
139
|
-
| `--linear-assignee <id>`
|
|
140
|
-
| `--linear-status <name>`
|
|
141
|
-
| `--linear-label <name>`
|
|
142
|
-
| `--poll-interval <s>`
|
|
143
|
-
| `--concurrency <n>`
|
|
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 |
|
|
144
158
|
|
|
145
159
|
## OpenSpec Flow
|
|
146
160
|
|
package/dist/cli/index.js
CHANGED
|
@@ -49811,10 +49811,10 @@ var require_axios = __commonJS((exports, module) => {
|
|
|
49811
49811
|
} = utils$1;
|
|
49812
49812
|
var globalFetchAPI = (({
|
|
49813
49813
|
Request,
|
|
49814
|
-
Response
|
|
49814
|
+
Response: Response2
|
|
49815
49815
|
}) => ({
|
|
49816
49816
|
Request,
|
|
49817
|
-
Response
|
|
49817
|
+
Response: Response2
|
|
49818
49818
|
}))(utils$1.global);
|
|
49819
49819
|
var {
|
|
49820
49820
|
ReadableStream: ReadableStream$1,
|
|
@@ -49834,11 +49834,11 @@ var require_axios = __commonJS((exports, module) => {
|
|
|
49834
49834
|
const {
|
|
49835
49835
|
fetch: envFetch,
|
|
49836
49836
|
Request,
|
|
49837
|
-
Response
|
|
49837
|
+
Response: Response2
|
|
49838
49838
|
} = env3;
|
|
49839
49839
|
const isFetchSupported = envFetch ? isFunction2(envFetch) : typeof fetch === "function";
|
|
49840
49840
|
const isRequestSupported = isFunction2(Request);
|
|
49841
|
-
const isResponseSupported = isFunction2(
|
|
49841
|
+
const isResponseSupported = isFunction2(Response2);
|
|
49842
49842
|
if (!isFetchSupported) {
|
|
49843
49843
|
return false;
|
|
49844
49844
|
}
|
|
@@ -49860,7 +49860,7 @@ var require_axios = __commonJS((exports, module) => {
|
|
|
49860
49860
|
}
|
|
49861
49861
|
return duplexAccessed && !hasContentType;
|
|
49862
49862
|
});
|
|
49863
|
-
const supportsResponseStream = isResponseSupported && isReadableStreamSupported && test(() => utils$1.isReadableStream(new
|
|
49863
|
+
const supportsResponseStream = isResponseSupported && isReadableStreamSupported && test(() => utils$1.isReadableStream(new Response2("").body));
|
|
49864
49864
|
const resolvers = {
|
|
49865
49865
|
stream: supportsResponseStream && ((res) => res.body)
|
|
49866
49866
|
};
|
|
@@ -49971,7 +49971,7 @@ var require_axios = __commonJS((exports, module) => {
|
|
|
49971
49971
|
});
|
|
49972
49972
|
const responseContentLength = utils$1.toFiniteNumber(response.headers.get("content-length"));
|
|
49973
49973
|
const [onProgress, flush] = onDownloadProgress && progressEventDecorator(responseContentLength, progressEventReducer(asyncDecorator(onDownloadProgress), true)) || [];
|
|
49974
|
-
response = new
|
|
49974
|
+
response = new Response2(trackStream(response.body, DEFAULT_CHUNK_SIZE, onProgress, () => {
|
|
49975
49975
|
flush && flush();
|
|
49976
49976
|
unsubscribe && unsubscribe();
|
|
49977
49977
|
}), options);
|
|
@@ -50006,9 +50006,9 @@ var require_axios = __commonJS((exports, module) => {
|
|
|
50006
50006
|
const {
|
|
50007
50007
|
fetch: fetch2,
|
|
50008
50008
|
Request,
|
|
50009
|
-
Response
|
|
50009
|
+
Response: Response2
|
|
50010
50010
|
} = env3;
|
|
50011
|
-
const seeds = [Request,
|
|
50011
|
+
const seeds = [Request, Response2, fetch2];
|
|
50012
50012
|
let len = seeds.length, i = len, seed, target, map2 = seedCache;
|
|
50013
50013
|
while (i--) {
|
|
50014
50014
|
seed = seeds[i];
|
|
@@ -50548,7 +50548,7 @@ var require_axios = __commonJS((exports, module) => {
|
|
|
50548
50548
|
});
|
|
50549
50549
|
|
|
50550
50550
|
// apps/cli/src/index.ts
|
|
50551
|
-
import { resolve, join as
|
|
50551
|
+
import { resolve, join as join17, dirname as dirname4 } from "path";
|
|
50552
50552
|
import { exists, mkdir as mkdir3 } from "fs/promises";
|
|
50553
50553
|
|
|
50554
50554
|
// node_modules/.bun/ink@5.2.1+1f88f629f0141b18/node_modules/ink/build/render.js
|
|
@@ -56152,6 +56152,10 @@ var HELP_TEXT = [
|
|
|
56152
56152
|
" --linear-label <name> Filter by label name (repeatable, any-of)",
|
|
56153
56153
|
" --poll-interval <s> Seconds between Linear polls (default: 60)",
|
|
56154
56154
|
" --concurrency <n> Max concurrent task loops (default: 1)",
|
|
56155
|
+
" --worktree Run each task in its own git worktree (.ralph/worktrees/<name>)",
|
|
56156
|
+
" --in-progress-status <name> Linear status to set when work starts on an issue",
|
|
56157
|
+
" --done-status <name> Linear status to set when work completes successfully",
|
|
56158
|
+
" --done-label <name> Linear label to add when work completes successfully",
|
|
56155
56159
|
"",
|
|
56156
56160
|
" --help, -h Show this help message",
|
|
56157
56161
|
"",
|
|
@@ -56187,7 +56191,11 @@ async function parseArgs(argv) {
|
|
|
56187
56191
|
linearStatus: [],
|
|
56188
56192
|
linearLabel: [],
|
|
56189
56193
|
pollInterval: 60,
|
|
56190
|
-
concurrency: 1
|
|
56194
|
+
concurrency: 1,
|
|
56195
|
+
worktree: false,
|
|
56196
|
+
inProgressStatus: "",
|
|
56197
|
+
doneStatus: "",
|
|
56198
|
+
doneLabel: ""
|
|
56191
56199
|
};
|
|
56192
56200
|
let expectModel = false;
|
|
56193
56201
|
let expectModelFlag = false;
|
|
@@ -56207,6 +56215,9 @@ async function parseArgs(argv) {
|
|
|
56207
56215
|
let expectLinearLabel = false;
|
|
56208
56216
|
let expectPollInterval = false;
|
|
56209
56217
|
let expectConcurrency = false;
|
|
56218
|
+
let expectInProgressStatus = false;
|
|
56219
|
+
let expectDoneStatus = false;
|
|
56220
|
+
let expectDoneLabel = false;
|
|
56210
56221
|
for (const arg of argv) {
|
|
56211
56222
|
if (expectModel) {
|
|
56212
56223
|
if (VALID_MODELS.has(arg)) {
|
|
@@ -56302,6 +56313,21 @@ async function parseArgs(argv) {
|
|
|
56302
56313
|
expectConcurrency = false;
|
|
56303
56314
|
continue;
|
|
56304
56315
|
}
|
|
56316
|
+
if (expectInProgressStatus) {
|
|
56317
|
+
result2.inProgressStatus = arg;
|
|
56318
|
+
expectInProgressStatus = false;
|
|
56319
|
+
continue;
|
|
56320
|
+
}
|
|
56321
|
+
if (expectDoneStatus) {
|
|
56322
|
+
result2.doneStatus = arg;
|
|
56323
|
+
expectDoneStatus = false;
|
|
56324
|
+
continue;
|
|
56325
|
+
}
|
|
56326
|
+
if (expectDoneLabel) {
|
|
56327
|
+
result2.doneLabel = arg;
|
|
56328
|
+
expectDoneLabel = false;
|
|
56329
|
+
continue;
|
|
56330
|
+
}
|
|
56305
56331
|
switch (arg) {
|
|
56306
56332
|
case "--claude":
|
|
56307
56333
|
if (result2.engineSet && result2.engine !== "claude") {
|
|
@@ -56378,6 +56404,18 @@ async function parseArgs(argv) {
|
|
|
56378
56404
|
case "--concurrency":
|
|
56379
56405
|
expectConcurrency = true;
|
|
56380
56406
|
break;
|
|
56407
|
+
case "--worktree":
|
|
56408
|
+
result2.worktree = true;
|
|
56409
|
+
break;
|
|
56410
|
+
case "--in-progress-status":
|
|
56411
|
+
expectInProgressStatus = true;
|
|
56412
|
+
break;
|
|
56413
|
+
case "--done-status":
|
|
56414
|
+
expectDoneStatus = true;
|
|
56415
|
+
break;
|
|
56416
|
+
case "--done-label":
|
|
56417
|
+
expectDoneLabel = true;
|
|
56418
|
+
break;
|
|
56381
56419
|
default:
|
|
56382
56420
|
if (VALID_MODES.has(arg)) {
|
|
56383
56421
|
result2.mode = arg;
|
|
@@ -56445,7 +56483,7 @@ function createDefaultContext() {
|
|
|
56445
56483
|
|
|
56446
56484
|
// apps/cli/src/components/App.tsx
|
|
56447
56485
|
var import_react58 = __toESM(require_react(), 1);
|
|
56448
|
-
import { join as
|
|
56486
|
+
import { join as join16 } from "path";
|
|
56449
56487
|
|
|
56450
56488
|
// packages/core/src/state.ts
|
|
56451
56489
|
import { join as join2 } from "path";
|
|
@@ -69801,6 +69839,10 @@ var RalphyConfigSchema = exports_external.object({
|
|
|
69801
69839
|
pollIntervalSeconds: exports_external.number().int().positive().default(60),
|
|
69802
69840
|
maxIterationsPerTask: exports_external.number().int().nonnegative().default(0),
|
|
69803
69841
|
maxCostUsdPerTask: exports_external.number().nonnegative().default(0),
|
|
69842
|
+
useWorktree: exports_external.boolean().default(false),
|
|
69843
|
+
cleanupWorktreeOnSuccess: exports_external.boolean().default(false),
|
|
69844
|
+
setupScript: exports_external.string().optional(),
|
|
69845
|
+
teardownScript: exports_external.string().optional(),
|
|
69804
69846
|
engine: exports_external.enum(["claude", "codex"]).default("claude"),
|
|
69805
69847
|
model: exports_external.enum(["haiku", "sonnet", "opus"]).default("opus"),
|
|
69806
69848
|
linear: exports_external.object({
|
|
@@ -70026,8 +70068,55 @@ class AgentCoordinator {
|
|
|
70026
70068
|
}
|
|
70027
70069
|
}
|
|
70028
70070
|
|
|
70071
|
+
// apps/cli/src/agent/worktree.ts
|
|
70072
|
+
import { join as join13 } from "path";
|
|
70073
|
+
function worktreesDir(projectRoot) {
|
|
70074
|
+
return join13(projectRoot, ".ralph", "worktrees");
|
|
70075
|
+
}
|
|
70076
|
+
function branchForChange(changeName) {
|
|
70077
|
+
return `ralph/${changeName}`;
|
|
70078
|
+
}
|
|
70079
|
+
async function createWorktree(projectRoot, changeName, runner) {
|
|
70080
|
+
const dir = worktreesDir(projectRoot);
|
|
70081
|
+
const cwd2 = join13(dir, changeName);
|
|
70082
|
+
const branch = branchForChange(changeName);
|
|
70083
|
+
const list = await runner.run(["worktree", "list", "--porcelain"], projectRoot);
|
|
70084
|
+
if (list.stdout.includes(`worktree ${cwd2}
|
|
70085
|
+
`)) {
|
|
70086
|
+
return { cwd: cwd2, branch };
|
|
70087
|
+
}
|
|
70088
|
+
let branchExists = true;
|
|
70089
|
+
try {
|
|
70090
|
+
await runner.run(["rev-parse", "--verify", "--quiet", `refs/heads/${branch}`], projectRoot);
|
|
70091
|
+
} catch {
|
|
70092
|
+
branchExists = false;
|
|
70093
|
+
}
|
|
70094
|
+
const cmd = branchExists ? ["worktree", "add", cwd2, branch] : ["worktree", "add", "-b", branch, cwd2];
|
|
70095
|
+
await runner.run(cmd, projectRoot);
|
|
70096
|
+
return { cwd: cwd2, branch };
|
|
70097
|
+
}
|
|
70098
|
+
async function removeWorktree(projectRoot, cwd2, runner) {
|
|
70099
|
+
await runner.run(["worktree", "remove", "--force", cwd2], projectRoot);
|
|
70100
|
+
}
|
|
70101
|
+
|
|
70029
70102
|
// apps/cli/src/components/AgentMode.tsx
|
|
70030
70103
|
var jsx_dev_runtime9 = __toESM(require_jsx_dev_runtime(), 1);
|
|
70104
|
+
import { join as join14 } from "path";
|
|
70105
|
+
var bunGitRunner = {
|
|
70106
|
+
run: async (args, cwd2) => {
|
|
70107
|
+
const proc = Bun.spawn({ cmd: ["git", ...args], cwd: cwd2, stdout: "pipe", stderr: "pipe" });
|
|
70108
|
+
const stdout = await new Response(proc.stdout).text();
|
|
70109
|
+
const stderr = await new Response(proc.stderr).text();
|
|
70110
|
+
const code = await proc.exited;
|
|
70111
|
+
if (code !== 0) {
|
|
70112
|
+
const err = new Error("git command failed");
|
|
70113
|
+
err.stderr = stderr;
|
|
70114
|
+
err.code = code;
|
|
70115
|
+
throw err;
|
|
70116
|
+
}
|
|
70117
|
+
return { stdout, stderr };
|
|
70118
|
+
}
|
|
70119
|
+
};
|
|
70031
70120
|
var lineCounter = 0;
|
|
70032
70121
|
function nextId() {
|
|
70033
70122
|
lineCounter += 1;
|
|
@@ -70066,6 +70155,24 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
70066
70155
|
const stateCache = new Map;
|
|
70067
70156
|
const labelCache = new Map;
|
|
70068
70157
|
const teamKeyOf = (issue) => issue.identifier.split("-")[0];
|
|
70158
|
+
const useWorktree = args.worktree || cfg.useWorktree;
|
|
70159
|
+
const cwdByChange = new Map;
|
|
70160
|
+
async function runScript(label, cmd, cwd2) {
|
|
70161
|
+
appendLog(` ${label}: ${cmd}`, "gray");
|
|
70162
|
+
const proc = Bun.spawn({
|
|
70163
|
+
cmd: ["sh", "-c", cmd],
|
|
70164
|
+
cwd: cwd2,
|
|
70165
|
+
stdout: "ignore",
|
|
70166
|
+
stderr: "pipe",
|
|
70167
|
+
stdin: "ignore"
|
|
70168
|
+
});
|
|
70169
|
+
const code = await proc.exited;
|
|
70170
|
+
if (code !== 0) {
|
|
70171
|
+
const stderr = await new Response(proc.stderr).text();
|
|
70172
|
+
appendLog(`! ${label} exited code ${code}${stderr ? `: ${stderr.trim().split(`
|
|
70173
|
+
`)[0]}` : ""}`, "yellow");
|
|
70174
|
+
}
|
|
70175
|
+
}
|
|
70069
70176
|
const coord2 = new AgentCoordinator({
|
|
70070
70177
|
fetchIssues: (f2) => fetchOpenIssues(apiKey, f2),
|
|
70071
70178
|
scaffold: async (issue) => {
|
|
@@ -70075,7 +70182,27 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
70075
70182
|
} catch (err) {
|
|
70076
70183
|
appendLog(`! Linear comment fetch failed for ${issue.identifier}: ${err.message}`, "yellow");
|
|
70077
70184
|
}
|
|
70078
|
-
|
|
70185
|
+
let workerCwd = projectRoot;
|
|
70186
|
+
let scaffoldTasksDir = tasksDir;
|
|
70187
|
+
let scaffoldStatesDir = statesDir;
|
|
70188
|
+
const probeName = issue.identifier.toLowerCase();
|
|
70189
|
+
if (useWorktree) {
|
|
70190
|
+
try {
|
|
70191
|
+
const wt = await createWorktree(projectRoot, probeName, bunGitRunner);
|
|
70192
|
+
workerCwd = wt.cwd;
|
|
70193
|
+
scaffoldTasksDir = join14(wt.cwd, "openspec", "changes");
|
|
70194
|
+
scaffoldStatesDir = join14(wt.cwd, ".ralph", "tasks");
|
|
70195
|
+
appendLog(` ${issue.identifier} worktree: ${wt.cwd} (${wt.branch})`, "gray");
|
|
70196
|
+
} catch (err) {
|
|
70197
|
+
appendLog(`! worktree create failed for ${issue.identifier}: ${err.message} \u2014 falling back to project root`, "yellow");
|
|
70198
|
+
}
|
|
70199
|
+
}
|
|
70200
|
+
const changeName = await scaffoldChangeForIssue(scaffoldTasksDir, scaffoldStatesDir, issue, comments);
|
|
70201
|
+
cwdByChange.set(changeName, workerCwd);
|
|
70202
|
+
if (cfg.setupScript) {
|
|
70203
|
+
await runScript("setup", cfg.setupScript, workerCwd);
|
|
70204
|
+
}
|
|
70205
|
+
return changeName;
|
|
70079
70206
|
},
|
|
70080
70207
|
spawnWorker: (changeName) => {
|
|
70081
70208
|
const cmd = [
|
|
@@ -70093,14 +70220,35 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
70093
70220
|
const maxCost = args.maxCostUsd || cfg.maxCostUsdPerTask;
|
|
70094
70221
|
if (maxCost > 0)
|
|
70095
70222
|
cmd.push("--max-cost", String(maxCost));
|
|
70223
|
+
const cwd2 = cwdByChange.get(changeName) ?? projectRoot;
|
|
70096
70224
|
const proc = Bun.spawn({
|
|
70097
70225
|
cmd,
|
|
70098
|
-
cwd:
|
|
70226
|
+
cwd: cwd2,
|
|
70099
70227
|
stdout: "ignore",
|
|
70100
70228
|
stderr: "ignore",
|
|
70101
70229
|
stdin: "ignore"
|
|
70102
70230
|
});
|
|
70103
|
-
|
|
70231
|
+
const wrapped = proc.exited.then(async (code) => {
|
|
70232
|
+
if (cfg.teardownScript) {
|
|
70233
|
+
try {
|
|
70234
|
+
await runScript("teardown", cfg.teardownScript, cwd2);
|
|
70235
|
+
} catch {}
|
|
70236
|
+
}
|
|
70237
|
+
if (useWorktree && cwd2 !== projectRoot) {
|
|
70238
|
+
const ok = code === 0;
|
|
70239
|
+
if (ok && cfg.cleanupWorktreeOnSuccess) {
|
|
70240
|
+
try {
|
|
70241
|
+
await removeWorktree(projectRoot, cwd2, bunGitRunner);
|
|
70242
|
+
appendLog(` removed worktree ${cwd2}`, "gray");
|
|
70243
|
+
} catch (err) {
|
|
70244
|
+
appendLog(`! worktree remove failed for ${changeName}: ${err.message}`, "yellow");
|
|
70245
|
+
}
|
|
70246
|
+
}
|
|
70247
|
+
}
|
|
70248
|
+
cwdByChange.delete(changeName);
|
|
70249
|
+
return code;
|
|
70250
|
+
});
|
|
70251
|
+
return { exited: wrapped, kill: () => proc.kill() };
|
|
70104
70252
|
},
|
|
70105
70253
|
loadState: () => readAgentState(projectRoot),
|
|
70106
70254
|
saveState: (s) => writeAgentState(projectRoot, s),
|
|
@@ -70134,9 +70282,9 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
70134
70282
|
}, {
|
|
70135
70283
|
concurrency,
|
|
70136
70284
|
filter: filter2,
|
|
70137
|
-
inProgressStatus: cfg.linear.inProgressStatus,
|
|
70138
|
-
doneStatus: cfg.linear.doneStatus,
|
|
70139
|
-
doneLabel: cfg.linear.doneLabel,
|
|
70285
|
+
inProgressStatus: args.inProgressStatus || cfg.linear.inProgressStatus,
|
|
70286
|
+
doneStatus: args.doneStatus || cfg.linear.doneStatus,
|
|
70287
|
+
doneLabel: args.doneLabel || cfg.linear.doneLabel,
|
|
70140
70288
|
postComments: cfg.linear.postComments
|
|
70141
70289
|
});
|
|
70142
70290
|
coordRef.current = coord2;
|
|
@@ -70218,11 +70366,11 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
70218
70366
|
}
|
|
70219
70367
|
|
|
70220
70368
|
// packages/openspec/src/openspec-change-store.ts
|
|
70221
|
-
import { join as
|
|
70369
|
+
import { join as join15, dirname as dirname3 } from "path";
|
|
70222
70370
|
import { readdir, mkdir as mkdir2 } from "fs/promises";
|
|
70223
70371
|
function resolveOpenspecBin() {
|
|
70224
70372
|
const pkgJsonPath = Bun.resolveSync("@fission-ai/openspec/package.json", import.meta.dir);
|
|
70225
|
-
return
|
|
70373
|
+
return join15(dirname3(pkgJsonPath), "bin", "openspec.js");
|
|
70226
70374
|
}
|
|
70227
70375
|
function runOpenspec(args, options = {}) {
|
|
70228
70376
|
const stdio = options.inherit ? ["inherit", "inherit", "inherit"] : ["ignore", "pipe", "pipe"];
|
|
@@ -70248,7 +70396,7 @@ class OpenSpecChangeStore {
|
|
|
70248
70396
|
}
|
|
70249
70397
|
}
|
|
70250
70398
|
getChangeDirectory(name) {
|
|
70251
|
-
return
|
|
70399
|
+
return join15("openspec", "changes", name);
|
|
70252
70400
|
}
|
|
70253
70401
|
async listChanges() {
|
|
70254
70402
|
const result2 = runOpenspec(["list", "--json"]);
|
|
@@ -70262,7 +70410,7 @@ class OpenSpecChangeStore {
|
|
|
70262
70410
|
}
|
|
70263
70411
|
} catch {}
|
|
70264
70412
|
}
|
|
70265
|
-
const changesDir =
|
|
70413
|
+
const changesDir = join15("openspec", "changes");
|
|
70266
70414
|
if (!await Bun.file(changesDir).exists())
|
|
70267
70415
|
return [];
|
|
70268
70416
|
try {
|
|
@@ -70273,18 +70421,18 @@ class OpenSpecChangeStore {
|
|
|
70273
70421
|
}
|
|
70274
70422
|
}
|
|
70275
70423
|
async readTaskList(name) {
|
|
70276
|
-
const file = Bun.file(
|
|
70424
|
+
const file = Bun.file(join15("openspec", "changes", name, "tasks.md"));
|
|
70277
70425
|
if (!await file.exists())
|
|
70278
70426
|
return "";
|
|
70279
70427
|
return await file.text();
|
|
70280
70428
|
}
|
|
70281
70429
|
async writeTaskList(name, content) {
|
|
70282
|
-
const path =
|
|
70430
|
+
const path = join15("openspec", "changes", name, "tasks.md");
|
|
70283
70431
|
await mkdir2(dirname3(path), { recursive: true });
|
|
70284
70432
|
await Bun.write(path, content);
|
|
70285
70433
|
}
|
|
70286
70434
|
async appendSteering(name, message) {
|
|
70287
|
-
const path =
|
|
70435
|
+
const path = join15("openspec", "changes", name, "steering.md");
|
|
70288
70436
|
const file = Bun.file(path);
|
|
70289
70437
|
const existing = await file.exists() ? await file.text() : null;
|
|
70290
70438
|
const updated = existing ? `${message}
|
|
@@ -70295,7 +70443,7 @@ ${existing.trimStart()}` : `${message}
|
|
|
70295
70443
|
await Bun.write(path, updated);
|
|
70296
70444
|
}
|
|
70297
70445
|
async readSection(name, artifact, heading) {
|
|
70298
|
-
const file = Bun.file(
|
|
70446
|
+
const file = Bun.file(join15("openspec", "changes", name, artifact));
|
|
70299
70447
|
if (!await file.exists())
|
|
70300
70448
|
return "";
|
|
70301
70449
|
const content = await file.text();
|
|
@@ -70376,8 +70524,8 @@ function App2({ args, statesDir, tasksDir, projectRoot }) {
|
|
|
70376
70524
|
message: "Error: --name is required for status mode"
|
|
70377
70525
|
}, undefined, false, undefined, this);
|
|
70378
70526
|
}
|
|
70379
|
-
const stateDir =
|
|
70380
|
-
if (getStorage().read(
|
|
70527
|
+
const stateDir = join16(statesDir, args.name);
|
|
70528
|
+
if (getStorage().read(join16(stateDir, ".ralph-state.json")) === null) {
|
|
70381
70529
|
return /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(ErrorMessage, {
|
|
70382
70530
|
message: `Error: change '${args.name}' not found`
|
|
70383
70531
|
}, undefined, false, undefined, this);
|
|
@@ -70434,7 +70582,7 @@ if (typeof globalThis.Bun === "undefined") {
|
|
|
70434
70582
|
async function findProjectRoot() {
|
|
70435
70583
|
let dir = process.cwd();
|
|
70436
70584
|
while (dir !== "/") {
|
|
70437
|
-
if (await exists(
|
|
70585
|
+
if (await exists(join17(dir, "openspec")))
|
|
70438
70586
|
return dir;
|
|
70439
70587
|
dir = resolve(dir, "..");
|
|
70440
70588
|
}
|
|
@@ -70469,11 +70617,11 @@ try {
|
|
|
70469
70617
|
capture("command_run", { mode: args.mode, engine: args.engine, model: args.model });
|
|
70470
70618
|
try {
|
|
70471
70619
|
const projectRoot = await findProjectRoot();
|
|
70472
|
-
const statesDir =
|
|
70473
|
-
const tasksDir =
|
|
70620
|
+
const statesDir = join17(projectRoot, ".ralph", "tasks");
|
|
70621
|
+
const tasksDir = join17(projectRoot, "openspec", "changes");
|
|
70474
70622
|
if (args.mode === "init") {
|
|
70475
70623
|
await mkdir3(statesDir, { recursive: true });
|
|
70476
|
-
const openspecBin =
|
|
70624
|
+
const openspecBin = join17(dirname4(Bun.resolveSync("@fission-ai/openspec/package.json", import.meta.dir)), "bin", "openspec.js");
|
|
70477
70625
|
Bun.spawnSync({
|
|
70478
70626
|
cmd: [process.execPath, openspecBin, "init", "--tools", "none", "--force"],
|
|
70479
70627
|
stdio: ["inherit", "inherit", "inherit"],
|
|
@@ -70481,13 +70629,13 @@ try {
|
|
|
70481
70629
|
});
|
|
70482
70630
|
}
|
|
70483
70631
|
if (args.mode === "task" && args.name) {
|
|
70484
|
-
await mkdir3(
|
|
70485
|
-
await mkdir3(
|
|
70632
|
+
await mkdir3(join17(statesDir, args.name), { recursive: true });
|
|
70633
|
+
await mkdir3(join17(tasksDir, args.name), { recursive: true });
|
|
70486
70634
|
}
|
|
70487
70635
|
if (args.mode === "agent") {
|
|
70488
70636
|
await mkdir3(statesDir, { recursive: true });
|
|
70489
70637
|
await mkdir3(tasksDir, { recursive: true });
|
|
70490
|
-
await mkdir3(
|
|
70638
|
+
await mkdir3(join17(projectRoot, ".ralph"), { recursive: true });
|
|
70491
70639
|
}
|
|
70492
70640
|
await runWithContext(createDefaultContext(), async () => {
|
|
70493
70641
|
const { waitUntilExit } = render_default(import_react59.createElement(App2, { args, statesDir, tasksDir, projectRoot }));
|