@neriros/ralphy 2.16.5 → 2.16.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 +16 -1
- package/dist/cli/index.js +157 -140
- package/dist/mcp/index.js +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -231,9 +231,24 @@ Each worker card shows:
|
|
|
231
231
|
- `▶ TASK` — first unchecked task from `tasks.md`, updated every second
|
|
232
232
|
- `PHASE` with color (cyan = working, yellow = git ops, blue = CI, green = done, red = gave-up) + time in phase
|
|
233
233
|
- `⏵ CMD` when a shell command is in flight (shows the command and how long it's been running)
|
|
234
|
-
- `LOG` — path to the worker's log file for `tail -f`
|
|
234
|
+
- `LOG` — path to the worker's log file for `tail -f` (format: `[ISO] [type] message`)
|
|
235
235
|
- `─ OUTPUT ─` section with live stdout/stderr (scales to fill remaining terminal height for the focused worker)
|
|
236
236
|
|
|
237
|
+
### Log files
|
|
238
|
+
|
|
239
|
+
All log entries use the format `[2024-01-15T12:00:00.000Z] [type] message`. Four log types:
|
|
240
|
+
|
|
241
|
+
| Type | Meaning | Destination |
|
|
242
|
+
| --------- | -------------------------------------------------- | -------------------------------------------------- |
|
|
243
|
+
| `session` | Worker start / stop boundaries | `agent-mode.log` + worker log |
|
|
244
|
+
| `phase` | Phase transitions (working → pushing → ci-poll …) | `agent-mode.log` + worker log |
|
|
245
|
+
| `coord` | Coordinator events (Linear poll, worktree, labels) | `agent-mode.log` + worker log (when task-specific) |
|
|
246
|
+
| `output` | Raw subprocess stdout/stderr lines | Worker log only |
|
|
247
|
+
|
|
248
|
+
- **`~/.ralph/agent-mode.log`** — global session log, appended each agent run
|
|
249
|
+
- **`<projectRoot>/.ralph/logs/<change>.log`** — per-worker unified log; includes output, phases, and coordinator events for that task. `tail -f` this for live progress.
|
|
250
|
+
- **`<taskDir>/LOG.jsonl`** — structured JSON event log for the web UI (each line has a `ts` field)
|
|
251
|
+
|
|
237
252
|
## OpenSpec Flow
|
|
238
253
|
|
|
239
254
|
There are no phases. One loop, one prompt, one `tasks.md` checklist.
|
package/dist/cli/index.js
CHANGED
|
@@ -35029,8 +35029,8 @@ import { readFileSync as readFileSync2 } from "fs";
|
|
|
35029
35029
|
import { resolve } from "path";
|
|
35030
35030
|
function getVersion() {
|
|
35031
35031
|
try {
|
|
35032
|
-
if ("2.16.
|
|
35033
|
-
return "2.16.
|
|
35032
|
+
if ("2.16.7")
|
|
35033
|
+
return "2.16.7";
|
|
35034
35034
|
} catch {}
|
|
35035
35035
|
const dirsToTry = [];
|
|
35036
35036
|
try {
|
|
@@ -35121,6 +35121,7 @@ async function parseArgs(argv) {
|
|
|
35121
35121
|
log: false,
|
|
35122
35122
|
verbose: false,
|
|
35123
35123
|
manualTest: false,
|
|
35124
|
+
fromAgent: false,
|
|
35124
35125
|
linearTeam: "",
|
|
35125
35126
|
linearAssignee: "",
|
|
35126
35127
|
pollInterval: 60,
|
|
@@ -35321,6 +35322,9 @@ async function parseArgs(argv) {
|
|
|
35321
35322
|
case "--manual-test":
|
|
35322
35323
|
result2.manualTest = true;
|
|
35323
35324
|
break;
|
|
35325
|
+
case "--from-agent":
|
|
35326
|
+
result2.fromAgent = true;
|
|
35327
|
+
break;
|
|
35324
35328
|
default:
|
|
35325
35329
|
if (VALID_MODES.has(arg)) {
|
|
35326
35330
|
result2.mode = arg;
|
|
@@ -39426,6 +39430,7 @@ var init_types2 = __esm(() => {
|
|
|
39426
39430
|
engine: exports_external.enum(["claude", "codex"]).default("claude"),
|
|
39427
39431
|
model: exports_external.string().default("opus"),
|
|
39428
39432
|
manualTest: exports_external.boolean().default(false),
|
|
39433
|
+
createPr: exports_external.boolean().default(false),
|
|
39429
39434
|
usage: UsageSchema.default({}),
|
|
39430
39435
|
history: exports_external.array(HistoryEntrySchema).default([]),
|
|
39431
39436
|
metadata: exports_external.object({ branch: exports_external.string().optional() }).default({})
|
|
@@ -59616,19 +59621,73 @@ var init_config = __esm(() => {
|
|
|
59616
59621
|
});
|
|
59617
59622
|
});
|
|
59618
59623
|
|
|
59624
|
+
// packages/log/src/log.ts
|
|
59625
|
+
import { appendFile } from "fs/promises";
|
|
59626
|
+
import { join as join11, dirname as dirname4 } from "path";
|
|
59627
|
+
import { homedir as homedir2 } from "os";
|
|
59628
|
+
import { mkdir as mkdir2 } from "fs/promises";
|
|
59629
|
+
function fmt(type, text) {
|
|
59630
|
+
return `[${new Date().toISOString()}] [${type}] ${text}
|
|
59631
|
+
`;
|
|
59632
|
+
}
|
|
59633
|
+
function write(path, line) {
|
|
59634
|
+
appendFile(path, line).catch(() => {
|
|
59635
|
+
return;
|
|
59636
|
+
});
|
|
59637
|
+
}
|
|
59638
|
+
function logSession(text, workerLogFile) {
|
|
59639
|
+
const clean = text.replace(ANSI_RE, "").trim();
|
|
59640
|
+
if (!clean)
|
|
59641
|
+
return;
|
|
59642
|
+
const line = fmt("session", clean);
|
|
59643
|
+
write(AGENT_LOG_PATH, line);
|
|
59644
|
+
if (workerLogFile)
|
|
59645
|
+
write(workerLogFile, line);
|
|
59646
|
+
}
|
|
59647
|
+
function logCoord(text, workerLogFile) {
|
|
59648
|
+
const clean = text.replace(ANSI_RE, "").trim();
|
|
59649
|
+
if (!clean)
|
|
59650
|
+
return;
|
|
59651
|
+
const line = fmt("coord", clean);
|
|
59652
|
+
write(AGENT_LOG_PATH, line);
|
|
59653
|
+
if (workerLogFile)
|
|
59654
|
+
write(workerLogFile, line);
|
|
59655
|
+
}
|
|
59656
|
+
function logPhase(changeName, workerLogFile, phase, detail) {
|
|
59657
|
+
const msg = `${changeName}: ${phase}${detail ? ` (${detail})` : ""}`;
|
|
59658
|
+
const line = fmt("phase", msg);
|
|
59659
|
+
write(AGENT_LOG_PATH, line);
|
|
59660
|
+
write(workerLogFile, line);
|
|
59661
|
+
}
|
|
59662
|
+
function logOutput(workerLogFile, text) {
|
|
59663
|
+
write(workerLogFile, fmt("output", text));
|
|
59664
|
+
}
|
|
59665
|
+
async function initWorkerLog(logFile) {
|
|
59666
|
+
await mkdir2(dirname4(logFile), { recursive: true });
|
|
59667
|
+
await Bun.write(logFile, "");
|
|
59668
|
+
}
|
|
59669
|
+
var ANSI_RE, AGENT_LOG_PATH;
|
|
59670
|
+
var init_log = __esm(() => {
|
|
59671
|
+
ANSI_RE = /\x1b(?:\[[0-9;]*[A-Za-z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|.)/g;
|
|
59672
|
+
AGENT_LOG_PATH = join11(homedir2(), ".ralph", "agent-mode.log");
|
|
59673
|
+
mkdir2(dirname4(AGENT_LOG_PATH), { recursive: true }).catch(() => {
|
|
59674
|
+
return;
|
|
59675
|
+
});
|
|
59676
|
+
});
|
|
59677
|
+
|
|
59619
59678
|
// packages/core/src/layout.ts
|
|
59620
|
-
import { join as
|
|
59679
|
+
import { join as join12 } from "path";
|
|
59621
59680
|
function projectLayout(root) {
|
|
59622
|
-
const statesDir =
|
|
59623
|
-
const tasksDir =
|
|
59681
|
+
const statesDir = join12(root, ".ralph", "tasks");
|
|
59682
|
+
const tasksDir = join12(root, "openspec", "changes");
|
|
59624
59683
|
return {
|
|
59625
59684
|
root,
|
|
59626
59685
|
statesDir,
|
|
59627
59686
|
tasksDir,
|
|
59628
|
-
agentStateFile:
|
|
59629
|
-
changeDir: (name) =>
|
|
59630
|
-
taskStateDir: (name) =>
|
|
59631
|
-
stateFile: (name) =>
|
|
59687
|
+
agentStateFile: join12(root, ".ralph", "agent-state.json"),
|
|
59688
|
+
changeDir: (name) => join12(tasksDir, name),
|
|
59689
|
+
taskStateDir: (name) => join12(statesDir, name),
|
|
59690
|
+
stateFile: (name) => join12(statesDir, name, STATE_FILE2)
|
|
59632
59691
|
};
|
|
59633
59692
|
}
|
|
59634
59693
|
var STATE_FILE2 = ".ralph-state.json";
|
|
@@ -60221,19 +60280,19 @@ var init_coordinator = __esm(() => {
|
|
|
60221
60280
|
});
|
|
60222
60281
|
|
|
60223
60282
|
// apps/cli/src/agent/scaffold.ts
|
|
60224
|
-
import { join as
|
|
60225
|
-
import { mkdir as
|
|
60283
|
+
import { join as join13 } from "path";
|
|
60284
|
+
import { mkdir as mkdir3 } from "fs/promises";
|
|
60226
60285
|
function changeNameForIssue(issue) {
|
|
60227
60286
|
const slug = issue.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
|
|
60228
60287
|
return slug ? `${issue.identifier.toLowerCase()}-${slug}` : issue.identifier.toLowerCase();
|
|
60229
60288
|
}
|
|
60230
60289
|
async function scaffoldChangeForIssue(tasksDir, statesDir, issue, comments = [], appendPrompt = "") {
|
|
60231
60290
|
const name = changeNameForIssue(issue);
|
|
60232
|
-
const changeDir =
|
|
60233
|
-
const stateDir =
|
|
60234
|
-
await
|
|
60235
|
-
await
|
|
60236
|
-
await
|
|
60291
|
+
const changeDir = join13(tasksDir, name);
|
|
60292
|
+
const stateDir = join13(statesDir, name);
|
|
60293
|
+
await mkdir3(changeDir, { recursive: true });
|
|
60294
|
+
await mkdir3(join13(changeDir, "specs"), { recursive: true });
|
|
60295
|
+
await mkdir3(stateDir, { recursive: true });
|
|
60237
60296
|
const commentsBlock = comments.length > 0 ? [
|
|
60238
60297
|
"",
|
|
60239
60298
|
"## Linear comments",
|
|
@@ -60284,26 +60343,26 @@ async function scaffoldChangeForIssue(tasksDir, statesDir, issue, comments = [],
|
|
|
60284
60343
|
""
|
|
60285
60344
|
].join(`
|
|
60286
60345
|
`);
|
|
60287
|
-
await Bun.write(
|
|
60288
|
-
await Bun.write(
|
|
60289
|
-
await Bun.write(
|
|
60346
|
+
await Bun.write(join13(changeDir, "proposal.md"), proposal);
|
|
60347
|
+
await Bun.write(join13(changeDir, "tasks.md"), tasks);
|
|
60348
|
+
await Bun.write(join13(changeDir, "design.md"), design);
|
|
60290
60349
|
return name;
|
|
60291
60350
|
}
|
|
60292
60351
|
var init_scaffold = () => {};
|
|
60293
60352
|
|
|
60294
60353
|
// apps/cli/src/agent/worktree.ts
|
|
60295
|
-
import { basename, join as
|
|
60296
|
-
import { homedir as
|
|
60354
|
+
import { basename, join as join14 } from "path";
|
|
60355
|
+
import { homedir as homedir3 } from "os";
|
|
60297
60356
|
import { exists } from "fs/promises";
|
|
60298
60357
|
function worktreesDir(projectRoot) {
|
|
60299
|
-
return
|
|
60358
|
+
return join14(homedir3(), ".ralph", basename(projectRoot), "worktrees");
|
|
60300
60359
|
}
|
|
60301
60360
|
function branchForChange(changeName) {
|
|
60302
60361
|
return `ralph/${changeName}`;
|
|
60303
60362
|
}
|
|
60304
60363
|
async function createWorktree(projectRoot, changeName, runner) {
|
|
60305
60364
|
const dir = worktreesDir(projectRoot);
|
|
60306
|
-
const cwd2 =
|
|
60365
|
+
const cwd2 = join14(dir, changeName);
|
|
60307
60366
|
const branch = branchForChange(changeName);
|
|
60308
60367
|
const list = await runner.run(["worktree", "list", "--porcelain"], projectRoot);
|
|
60309
60368
|
if (list.stdout.includes(`worktree ${cwd2}
|
|
@@ -60360,8 +60419,8 @@ async function isWorktreeSafeToRemove(cwd2, base2, runner) {
|
|
|
60360
60419
|
return { safe: true, dirty, unpushedCommits };
|
|
60361
60420
|
}
|
|
60362
60421
|
async function seedWorktreeMcpConfig(projectRoot, worktreeCwd) {
|
|
60363
|
-
const dst =
|
|
60364
|
-
const src =
|
|
60422
|
+
const dst = join14(worktreeCwd, ".mcp.json");
|
|
60423
|
+
const src = join14(projectRoot, ".mcp.json");
|
|
60365
60424
|
const source = await exists(dst) ? dst : await exists(src) ? src : null;
|
|
60366
60425
|
if (!source)
|
|
60367
60426
|
return;
|
|
@@ -60375,7 +60434,7 @@ async function seedWorktreeMcpConfig(projectRoot, worktreeCwd) {
|
|
|
60375
60434
|
if (servers && typeof servers === "object") {
|
|
60376
60435
|
for (const cfg of Object.values(servers)) {
|
|
60377
60436
|
if (Array.isArray(cfg.args)) {
|
|
60378
|
-
cfg.args = cfg.args.map((a) => typeof a === "string" && a.startsWith(".ralph/") ?
|
|
60437
|
+
cfg.args = cfg.args.map((a) => typeof a === "string" && a.startsWith(".ralph/") ? join14(projectRoot, a) : a);
|
|
60379
60438
|
}
|
|
60380
60439
|
}
|
|
60381
60440
|
}
|
|
@@ -60558,7 +60617,7 @@ var init_ci = __esm(() => {
|
|
|
60558
60617
|
});
|
|
60559
60618
|
|
|
60560
60619
|
// apps/cli/src/agent/post-task.ts
|
|
60561
|
-
import { join as
|
|
60620
|
+
import { join as join15 } from "path";
|
|
60562
60621
|
async function reactivateState(stateFilePath, log2, changeName) {
|
|
60563
60622
|
const file = Bun.file(stateFilePath);
|
|
60564
60623
|
if (!await file.exists())
|
|
@@ -60577,7 +60636,7 @@ async function reactivateState(stateFilePath, log2, changeName) {
|
|
|
60577
60636
|
}
|
|
60578
60637
|
async function runWorkerWithFixTask(ctx, heading, body) {
|
|
60579
60638
|
try {
|
|
60580
|
-
await prependFixTask(
|
|
60639
|
+
await prependFixTask(join15(ctx.changeDir, "tasks.md"), heading, body);
|
|
60581
60640
|
} catch (err) {
|
|
60582
60641
|
ctx.log(`! could not prepend fix task: ${err.message}`, "red");
|
|
60583
60642
|
return 1;
|
|
@@ -60889,8 +60948,8 @@ var init_post_task = __esm(() => {
|
|
|
60889
60948
|
});
|
|
60890
60949
|
|
|
60891
60950
|
// apps/cli/src/agent/wire.ts
|
|
60892
|
-
import { join as
|
|
60893
|
-
import { mkdir as
|
|
60951
|
+
import { join as join16 } from "path";
|
|
60952
|
+
import { mkdir as mkdir4 } from "fs/promises";
|
|
60894
60953
|
function traceCmdRunner(base2, onStart, onEnd) {
|
|
60895
60954
|
return {
|
|
60896
60955
|
run: async (cmd, cwd2) => {
|
|
@@ -60951,7 +61010,7 @@ function buildAgentCoordinator(input) {
|
|
|
60951
61010
|
onWorkerOutput,
|
|
60952
61011
|
onWorkerCmd
|
|
60953
61012
|
} = input;
|
|
60954
|
-
const logsDir =
|
|
61013
|
+
const logsDir = join16(projectRoot, ".ralph", "logs");
|
|
60955
61014
|
const concurrency = args.concurrency || cfg.concurrency;
|
|
60956
61015
|
const pollInterval = args.pollInterval || cfg.pollIntervalSeconds;
|
|
60957
61016
|
const indicators = mergeIndicators(cfg.linear.indicators, args.indicators);
|
|
@@ -61143,8 +61202,8 @@ function buildAgentCoordinator(input) {
|
|
|
61143
61202
|
} else {
|
|
61144
61203
|
changeName = changeNameForIssue(issue);
|
|
61145
61204
|
const wtLayout = projectLayout(workerCwd);
|
|
61146
|
-
await
|
|
61147
|
-
await
|
|
61205
|
+
await mkdir4(wtLayout.changeDir(changeName), { recursive: true });
|
|
61206
|
+
await mkdir4(wtLayout.taskStateDir(changeName), { recursive: true });
|
|
61148
61207
|
}
|
|
61149
61208
|
cwdByChange.set(changeName, workerCwd);
|
|
61150
61209
|
statesDirByChange.set(changeName, scaffoldStatesDir);
|
|
@@ -61153,7 +61212,7 @@ function buildAgentCoordinator(input) {
|
|
|
61153
61212
|
branchByChange.set(changeName, branch);
|
|
61154
61213
|
if (mode === "conflict-fix") {
|
|
61155
61214
|
const wtLayout = projectLayout(workerCwd);
|
|
61156
|
-
const tasksFile =
|
|
61215
|
+
const tasksFile = join16(wtLayout.changeDir(changeName), "tasks.md");
|
|
61157
61216
|
const prUrl = prByChange.get(changeName);
|
|
61158
61217
|
const body = [
|
|
61159
61218
|
`The PR for this change has merge conflicts with \`${cfg.prBaseBranch}\`.`,
|
|
@@ -61228,24 +61287,12 @@ PR: ${prUrl}` : ""
|
|
|
61228
61287
|
c.push("--verbose");
|
|
61229
61288
|
if (args.manualTest || cfg.enableManualTest)
|
|
61230
61289
|
c.push("--manual-test");
|
|
61290
|
+
c.push("--from-agent");
|
|
61231
61291
|
return c;
|
|
61232
61292
|
}
|
|
61233
61293
|
function defaultSpawn(changeName, cmd, cwd2, note) {
|
|
61234
|
-
const logFilePath =
|
|
61235
|
-
|
|
61236
|
-
const ensureLogWriter = async () => {
|
|
61237
|
-
if (logWriter)
|
|
61238
|
-
return logWriter;
|
|
61239
|
-
try {
|
|
61240
|
-
await Bun.write(logFilePath, "");
|
|
61241
|
-
logWriter = Bun.file(logFilePath).writer();
|
|
61242
|
-
return logWriter;
|
|
61243
|
-
} catch (err) {
|
|
61244
|
-
onLog(`! could not open worker log ${logFilePath}: ${err.message}`, "yellow");
|
|
61245
|
-
return null;
|
|
61246
|
-
}
|
|
61247
|
-
};
|
|
61248
|
-
const ANSI_RE = /\x1b(?:\[[0-9;]*[A-Za-z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|.)/g;
|
|
61294
|
+
const logFilePath = join16(logsDir, `${changeName}.log`);
|
|
61295
|
+
const ANSI_RE2 = /\x1b(?:\[[0-9;]*[A-Za-z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|.)/g;
|
|
61249
61296
|
const BOX_ONLY_RE = /^[\s\u2500\u2502\u256D\u256E\u2570\u256F\u254C\u2504\u2501\u2503]+$/;
|
|
61250
61297
|
const STATUS_BAR_LINE_RE = /^[\u280B\u2819\u2839\u2838\u283C\u2834\u2826\u2827\u2807\u280F\u2713\u2717]\s+iter\s+\d+/;
|
|
61251
61298
|
const ITER_HEADER_LINE_RE = /^\u2500\u2500/;
|
|
@@ -61258,7 +61305,6 @@ PR: ${prUrl}` : ""
|
|
|
61258
61305
|
const reader = stream.getReader();
|
|
61259
61306
|
const decoder = new TextDecoder;
|
|
61260
61307
|
let buf = "";
|
|
61261
|
-
const writer = await ensureLogWriter();
|
|
61262
61308
|
try {
|
|
61263
61309
|
while (true) {
|
|
61264
61310
|
const { value, done } = await reader.read();
|
|
@@ -61271,26 +61317,20 @@ PR: ${prUrl}` : ""
|
|
|
61271
61317
|
`)) >= 0) {
|
|
61272
61318
|
const line = buf.slice(0, nl);
|
|
61273
61319
|
buf = buf.slice(nl + 1);
|
|
61274
|
-
const clean = line.replace(
|
|
61275
|
-
if (
|
|
61276
|
-
|
|
61277
|
-
`);
|
|
61320
|
+
const clean = line.replace(ANSI_RE2, "").trim();
|
|
61321
|
+
if (clean && isLogWorthy(clean))
|
|
61322
|
+
logOutput(logFilePath, clean);
|
|
61278
61323
|
if (line)
|
|
61279
61324
|
onWorkerOutput?.(changeName, label === "err" ? `! ${line}` : line);
|
|
61280
61325
|
}
|
|
61281
61326
|
}
|
|
61282
61327
|
if (buf) {
|
|
61283
|
-
const clean = buf.replace(
|
|
61284
|
-
if (
|
|
61285
|
-
|
|
61286
|
-
`);
|
|
61328
|
+
const clean = buf.replace(ANSI_RE2, "").trim();
|
|
61329
|
+
if (clean && isLogWorthy(clean))
|
|
61330
|
+
logOutput(logFilePath, clean);
|
|
61287
61331
|
onWorkerOutput?.(changeName, label === "err" ? `! ${buf}` : buf);
|
|
61288
61332
|
}
|
|
61289
|
-
} catch {}
|
|
61290
|
-
try {
|
|
61291
|
-
writer?.flush();
|
|
61292
|
-
} catch {}
|
|
61293
|
-
}
|
|
61333
|
+
} catch {}
|
|
61294
61334
|
}
|
|
61295
61335
|
const p = Bun.spawn({
|
|
61296
61336
|
cmd,
|
|
@@ -61299,13 +61339,10 @@ PR: ${prUrl}` : ""
|
|
|
61299
61339
|
stderr: "pipe",
|
|
61300
61340
|
stdin: "ignore"
|
|
61301
61341
|
});
|
|
61302
|
-
(
|
|
61303
|
-
|
|
61304
|
-
|
|
61305
|
-
|
|
61306
|
-
--- ${note} ---
|
|
61307
|
-
`);
|
|
61308
|
-
})();
|
|
61342
|
+
initWorkerLog(logFilePath).then(() => {
|
|
61343
|
+
if (note)
|
|
61344
|
+
logSession(note, logFilePath);
|
|
61345
|
+
});
|
|
61309
61346
|
pump(p.stdout, "out");
|
|
61310
61347
|
pump(p.stderr, "err");
|
|
61311
61348
|
return { exited: p.exited, kill: () => p.kill(), logFilePath };
|
|
@@ -61316,7 +61353,7 @@ PR: ${prUrl}` : ""
|
|
|
61316
61353
|
let logFilePath;
|
|
61317
61354
|
let handle;
|
|
61318
61355
|
if (injected) {
|
|
61319
|
-
logFilePath =
|
|
61356
|
+
logFilePath = join16(logsDir, `${changeName}.log`);
|
|
61320
61357
|
handle = injected(buildTaskCmdFor(changeName), cwd2);
|
|
61321
61358
|
} else {
|
|
61322
61359
|
const r = defaultSpawn(changeName, buildTaskCmdFor(changeName), cwd2, `spawn at ${new Date().toISOString()}`);
|
|
@@ -61500,6 +61537,7 @@ function describeIndicators(indicators, team, assignee) {
|
|
|
61500
61537
|
}
|
|
61501
61538
|
var bunGitRunner, bunCmdRunner;
|
|
61502
61539
|
var init_wire = __esm(() => {
|
|
61540
|
+
init_log();
|
|
61503
61541
|
init_layout();
|
|
61504
61542
|
init_types2();
|
|
61505
61543
|
init_coordinator();
|
|
@@ -61546,7 +61584,7 @@ var exports_json_runner = {};
|
|
|
61546
61584
|
__export(exports_json_runner, {
|
|
61547
61585
|
runAgentJson: () => runAgentJson
|
|
61548
61586
|
});
|
|
61549
|
-
import { join as
|
|
61587
|
+
import { join as join20 } from "path";
|
|
61550
61588
|
import { mkdir as mkdir6 } from "fs/promises";
|
|
61551
61589
|
import { homedir as homedir4 } from "os";
|
|
61552
61590
|
function cleanOutputLine2(raw) {
|
|
@@ -61571,7 +61609,7 @@ async function runAgentJson({
|
|
|
61571
61609
|
statesDir,
|
|
61572
61610
|
tasksDir
|
|
61573
61611
|
}) {
|
|
61574
|
-
await mkdir6(
|
|
61612
|
+
await mkdir6(join20(homedir4(), ".ralph"), { recursive: true }).catch(() => {
|
|
61575
61613
|
return;
|
|
61576
61614
|
});
|
|
61577
61615
|
const cfgPath = await ensureRalphyConfig(projectRoot);
|
|
@@ -61677,7 +61715,7 @@ var init_json_runner = __esm(() => {
|
|
|
61677
61715
|
});
|
|
61678
61716
|
|
|
61679
61717
|
// apps/cli/src/index.ts
|
|
61680
|
-
import { resolve as resolve2, join as
|
|
61718
|
+
import { resolve as resolve2, join as join21, dirname as dirname6 } from "path";
|
|
61681
61719
|
import { exists as exists2, mkdir as mkdir7, rm } from "fs/promises";
|
|
61682
61720
|
|
|
61683
61721
|
// node_modules/.bun/ink@5.2.1+1f88f629f0141b18/node_modules/ink/build/render.js
|
|
@@ -66800,7 +66838,7 @@ function createDefaultContext() {
|
|
|
66800
66838
|
|
|
66801
66839
|
// apps/cli/src/components/App.tsx
|
|
66802
66840
|
var import_react58 = __toESM(require_react(), 1);
|
|
66803
|
-
import { join as
|
|
66841
|
+
import { join as join19 } from "path";
|
|
66804
66842
|
|
|
66805
66843
|
// packages/core/src/state.ts
|
|
66806
66844
|
init_types2();
|
|
@@ -66853,6 +66891,7 @@ function buildInitialState(options) {
|
|
|
66853
66891
|
engine: options.engine ?? "claude",
|
|
66854
66892
|
model: options.model ?? "opus",
|
|
66855
66893
|
manualTest: options.manualTest ?? false,
|
|
66894
|
+
createPr: options.createPr ?? false,
|
|
66856
66895
|
createdAt: now2,
|
|
66857
66896
|
lastModified: now2,
|
|
66858
66897
|
metadata: { branch }
|
|
@@ -71852,6 +71891,17 @@ function buildTaskPrompt(state, taskDir) {
|
|
|
71852
71891
|
`;
|
|
71853
71892
|
prompt += `Commit all changed files yourself before finishing \u2014 stage files individually (e.g. \`git add path/to/file\`), never \`git add -A\` or \`git commit -am\`. Nothing is committed automatically after you exit.
|
|
71854
71893
|
`;
|
|
71894
|
+
if (state.createPr) {
|
|
71895
|
+
prompt += `
|
|
71896
|
+
When all tasks are complete and all files are committed, push your branch and open a pull request:
|
|
71897
|
+
`;
|
|
71898
|
+
prompt += ` git push -u origin HEAD
|
|
71899
|
+
`;
|
|
71900
|
+
prompt += ` gh pr create --title "${state.name}" --body "Summary of changes for ${state.name}"
|
|
71901
|
+
`;
|
|
71902
|
+
prompt += `Use the change name as the PR title and write a concise summary of the implementation in the body.
|
|
71903
|
+
`;
|
|
71904
|
+
}
|
|
71855
71905
|
return prompt;
|
|
71856
71906
|
}
|
|
71857
71907
|
function checkStopSignal(taskDir, stateDir) {
|
|
@@ -72019,7 +72069,8 @@ function useLoop(opts) {
|
|
|
72019
72069
|
prompt: opts.prompt,
|
|
72020
72070
|
engine: opts.engine,
|
|
72021
72071
|
model: opts.model,
|
|
72022
|
-
manualTest: opts.manualTest
|
|
72072
|
+
manualTest: opts.manualTest,
|
|
72073
|
+
createPr: opts.createPr ?? false
|
|
72023
72074
|
});
|
|
72024
72075
|
writeState(stateDir, currentState);
|
|
72025
72076
|
}
|
|
@@ -72377,10 +72428,8 @@ var import_react57 = __toESM(require_react(), 1);
|
|
|
72377
72428
|
init_cli();
|
|
72378
72429
|
init_config();
|
|
72379
72430
|
init_wire();
|
|
72380
|
-
import { join as
|
|
72431
|
+
import { join as join17 } from "path";
|
|
72381
72432
|
import { pathToFileURL } from "url";
|
|
72382
|
-
import { homedir as homedir3 } from "os";
|
|
72383
|
-
import { appendFile, mkdir as mkdir4 } from "fs/promises";
|
|
72384
72433
|
|
|
72385
72434
|
// packages/core/src/progress.ts
|
|
72386
72435
|
function countProgress(content) {
|
|
@@ -72390,6 +72439,7 @@ function countProgress(content) {
|
|
|
72390
72439
|
}
|
|
72391
72440
|
|
|
72392
72441
|
// apps/cli/src/components/AgentMode.tsx
|
|
72442
|
+
init_log();
|
|
72393
72443
|
var jsx_dev_runtime9 = __toESM(require_jsx_dev_runtime(), 1);
|
|
72394
72444
|
var lineCounter = 0;
|
|
72395
72445
|
function nextId() {
|
|
@@ -72592,28 +72642,7 @@ function displayTailLines(activeCount) {
|
|
|
72592
72642
|
return 8;
|
|
72593
72643
|
return 5;
|
|
72594
72644
|
}
|
|
72595
|
-
var AGENT_LOG_PATH = join16(homedir3(), ".ralph", "agent-mode.log");
|
|
72596
72645
|
var SESSION_START = new Date().toISOString();
|
|
72597
|
-
mkdir4(dirname4(AGENT_LOG_PATH), { recursive: true }).catch(() => {
|
|
72598
|
-
return;
|
|
72599
|
-
});
|
|
72600
|
-
function writeAgentLog(text) {
|
|
72601
|
-
const clean = text.replace(ANSI_STRIP_RE, "").trim();
|
|
72602
|
-
if (!clean)
|
|
72603
|
-
return;
|
|
72604
|
-
const line = `[${new Date().toISOString()}] ${clean}
|
|
72605
|
-
`;
|
|
72606
|
-
appendFile(AGENT_LOG_PATH, line).catch(() => {
|
|
72607
|
-
return;
|
|
72608
|
-
});
|
|
72609
|
-
}
|
|
72610
|
-
function writePhaseLog(phaseLogFile, text) {
|
|
72611
|
-
const line = `[${new Date().toISOString()}] ${text}
|
|
72612
|
-
`;
|
|
72613
|
-
appendFile(phaseLogFile, line).catch(() => {
|
|
72614
|
-
return;
|
|
72615
|
-
});
|
|
72616
|
-
}
|
|
72617
72646
|
function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
72618
72647
|
const { exit } = use_app_default();
|
|
72619
72648
|
const { stdout } = use_stdout_default();
|
|
@@ -72627,15 +72656,15 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
72627
72656
|
const nextPollAtRef = import_react57.useRef(0);
|
|
72628
72657
|
const cfgRef = import_react57.useRef(null);
|
|
72629
72658
|
const [pollStatus, setPollStatus] = import_react57.useState({ state: "idle", lastFound: null, lastAdded: null, lastAt: null, filterDesc: "" });
|
|
72630
|
-
function appendLog(text, color) {
|
|
72659
|
+
function appendLog(text, color, workerLogFile) {
|
|
72631
72660
|
setLogs((prev) => [...prev, { id: nextId(), text, color }]);
|
|
72632
|
-
|
|
72661
|
+
logCoord(text, workerLogFile);
|
|
72633
72662
|
}
|
|
72634
72663
|
import_react57.useEffect(() => {
|
|
72635
72664
|
let pollTimer = null;
|
|
72636
72665
|
let cancelled = false;
|
|
72637
72666
|
async function init2() {
|
|
72638
|
-
|
|
72667
|
+
logSession(`=== session start ${SESSION_START} ===`);
|
|
72639
72668
|
const cfgPath = await ensureRalphyConfig(projectRoot);
|
|
72640
72669
|
const cfg2 = await loadRalphyConfig(projectRoot);
|
|
72641
72670
|
cfgRef.current = cfg2;
|
|
@@ -72656,17 +72685,11 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
72656
72685
|
onLog: appendLog,
|
|
72657
72686
|
onWorkersChanged: () => setTick((t) => t + 1),
|
|
72658
72687
|
onWorkerStarted: (changeName, dir, logFile, changeDir) => {
|
|
72659
|
-
|
|
72660
|
-
const phaseLogFile = logFile.replace(/\.log$/, "-phases.log");
|
|
72661
|
-
mkdir4(dirname4(phaseLogFile), { recursive: true }).then(() => appendFile(phaseLogFile, `=== session ${SESSION_START} | worker-started ${new Date().toISOString()} ===
|
|
72662
|
-
`)).catch(() => {
|
|
72663
|
-
return;
|
|
72664
|
-
});
|
|
72688
|
+
logSession(`worker-started ${changeName} log=${logFile}`, logFile);
|
|
72665
72689
|
workerMetaRef.current.set(changeName, {
|
|
72666
72690
|
startedAt: Date.now(),
|
|
72667
72691
|
statesDir: dir,
|
|
72668
72692
|
logFile,
|
|
72669
|
-
phaseLogFile,
|
|
72670
72693
|
changeDir,
|
|
72671
72694
|
iter: 0,
|
|
72672
72695
|
phase: "working",
|
|
@@ -72680,26 +72703,19 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
72680
72703
|
});
|
|
72681
72704
|
},
|
|
72682
72705
|
onWorkerExited: (changeName) => {
|
|
72683
|
-
writeAgentLog(`worker-exited ${changeName}`);
|
|
72684
72706
|
const m = workerMetaRef.current.get(changeName);
|
|
72685
|
-
|
|
72686
|
-
writePhaseLog(m.phaseLogFile, `=== worker-exited ===`);
|
|
72687
|
-
}
|
|
72707
|
+
logSession(`worker-exited ${changeName}`, m?.logFile);
|
|
72688
72708
|
workerMetaRef.current.delete(changeName);
|
|
72689
72709
|
},
|
|
72690
72710
|
onWorkerPhase: (changeName, phase, detail) => {
|
|
72691
72711
|
const m = workerMetaRef.current.get(changeName);
|
|
72692
72712
|
if (!m)
|
|
72693
72713
|
return;
|
|
72694
|
-
if (m.phase !== phase)
|
|
72695
|
-
writeAgentLog(`phase ${changeName}: ${phase}${detail ? ` (${detail})` : ""}`);
|
|
72714
|
+
if (m.phase !== phase)
|
|
72696
72715
|
m.phaseStartedAt = Date.now();
|
|
72697
|
-
}
|
|
72698
72716
|
m.phase = phase;
|
|
72699
72717
|
m.phaseDetail = detail ?? "";
|
|
72700
|
-
|
|
72701
|
-
writePhaseLog(m.phaseLogFile, `${phase}${detail ? ` (${detail})` : ""}`);
|
|
72702
|
-
}
|
|
72718
|
+
logPhase(changeName, m.logFile, phase, detail);
|
|
72703
72719
|
},
|
|
72704
72720
|
onWorkerOutput: (changeName, line) => {
|
|
72705
72721
|
const m = workerMetaRef.current.get(changeName);
|
|
@@ -72780,7 +72796,7 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
72780
72796
|
(async () => {
|
|
72781
72797
|
for (const [changeName, meta] of workerMetaRef.current) {
|
|
72782
72798
|
try {
|
|
72783
|
-
const file = Bun.file(
|
|
72799
|
+
const file = Bun.file(join17(meta.statesDir, changeName, ".ralph-state.json"));
|
|
72784
72800
|
if (await file.exists()) {
|
|
72785
72801
|
const json = await file.json();
|
|
72786
72802
|
meta.iter = json.iteration ?? meta.iter;
|
|
@@ -72790,7 +72806,7 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
72790
72806
|
}
|
|
72791
72807
|
if (meta.changeDir) {
|
|
72792
72808
|
try {
|
|
72793
|
-
const tasksFile = Bun.file(
|
|
72809
|
+
const tasksFile = Bun.file(join17(meta.changeDir, "tasks.md"));
|
|
72794
72810
|
if (await tasksFile.exists()) {
|
|
72795
72811
|
const text = await tasksFile.text();
|
|
72796
72812
|
const match = text.match(/^- \[ \] (.+)$/m);
|
|
@@ -73450,11 +73466,11 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
73450
73466
|
}
|
|
73451
73467
|
|
|
73452
73468
|
// packages/openspec/src/openspec-change-store.ts
|
|
73453
|
-
import { join as
|
|
73469
|
+
import { join as join18, dirname as dirname5 } from "path";
|
|
73454
73470
|
import { readdir, mkdir as mkdir5 } from "fs/promises";
|
|
73455
73471
|
function resolveOpenspecBin() {
|
|
73456
73472
|
const pkgJsonPath = Bun.resolveSync("@fission-ai/openspec/package.json", import.meta.dir);
|
|
73457
|
-
return
|
|
73473
|
+
return join18(dirname5(pkgJsonPath), "bin", "openspec.js");
|
|
73458
73474
|
}
|
|
73459
73475
|
function runOpenspec(args, options = {}) {
|
|
73460
73476
|
const stdio = options.inherit ? ["inherit", "inherit", "inherit"] : ["ignore", "pipe", "pipe"];
|
|
@@ -73480,7 +73496,7 @@ class OpenSpecChangeStore {
|
|
|
73480
73496
|
}
|
|
73481
73497
|
}
|
|
73482
73498
|
getChangeDirectory(name) {
|
|
73483
|
-
return
|
|
73499
|
+
return join18("openspec", "changes", name);
|
|
73484
73500
|
}
|
|
73485
73501
|
async listChanges() {
|
|
73486
73502
|
const result2 = runOpenspec(["list", "--json"]);
|
|
@@ -73494,7 +73510,7 @@ class OpenSpecChangeStore {
|
|
|
73494
73510
|
}
|
|
73495
73511
|
} catch {}
|
|
73496
73512
|
}
|
|
73497
|
-
const changesDir =
|
|
73513
|
+
const changesDir = join18("openspec", "changes");
|
|
73498
73514
|
if (!await Bun.file(changesDir).exists())
|
|
73499
73515
|
return [];
|
|
73500
73516
|
try {
|
|
@@ -73505,18 +73521,18 @@ class OpenSpecChangeStore {
|
|
|
73505
73521
|
}
|
|
73506
73522
|
}
|
|
73507
73523
|
async readTaskList(name) {
|
|
73508
|
-
const file = Bun.file(
|
|
73524
|
+
const file = Bun.file(join18("openspec", "changes", name, "tasks.md"));
|
|
73509
73525
|
if (!await file.exists())
|
|
73510
73526
|
return "";
|
|
73511
73527
|
return await file.text();
|
|
73512
73528
|
}
|
|
73513
73529
|
async writeTaskList(name, content) {
|
|
73514
|
-
const path =
|
|
73530
|
+
const path = join18("openspec", "changes", name, "tasks.md");
|
|
73515
73531
|
await mkdir5(dirname5(path), { recursive: true });
|
|
73516
73532
|
await Bun.write(path, content);
|
|
73517
73533
|
}
|
|
73518
73534
|
async appendSteering(name, message) {
|
|
73519
|
-
const path =
|
|
73535
|
+
const path = join18("openspec", "changes", name, "steering.md");
|
|
73520
73536
|
const file = Bun.file(path);
|
|
73521
73537
|
const existing = await file.exists() ? await file.text() : null;
|
|
73522
73538
|
const updated = existing ? `${message}
|
|
@@ -73527,7 +73543,7 @@ ${existing.trimStart()}` : `${message}
|
|
|
73527
73543
|
await Bun.write(path, updated);
|
|
73528
73544
|
}
|
|
73529
73545
|
async readSection(name, artifact, heading) {
|
|
73530
|
-
const file = Bun.file(
|
|
73546
|
+
const file = Bun.file(join18("openspec", "changes", name, artifact));
|
|
73531
73547
|
if (!await file.exists())
|
|
73532
73548
|
return "";
|
|
73533
73549
|
const content = await file.text();
|
|
@@ -73619,6 +73635,7 @@ function TaskModeWrapper({ args, statesDir, tasksDir, projectRoot }) {
|
|
|
73619
73635
|
log: args.log,
|
|
73620
73636
|
verbose: args.verbose,
|
|
73621
73637
|
manualTest,
|
|
73638
|
+
createPr: args.fromAgent,
|
|
73622
73639
|
statesDir,
|
|
73623
73640
|
tasksDir,
|
|
73624
73641
|
changeStore: new OpenSpecChangeStore
|
|
@@ -73645,8 +73662,8 @@ function App2({ args, statesDir, tasksDir, projectRoot }) {
|
|
|
73645
73662
|
message: "Error: --name is required for status mode"
|
|
73646
73663
|
}, undefined, false, undefined, this);
|
|
73647
73664
|
}
|
|
73648
|
-
const stateDir =
|
|
73649
|
-
if (getStorage().read(
|
|
73665
|
+
const stateDir = join19(statesDir, args.name);
|
|
73666
|
+
if (getStorage().read(join19(stateDir, ".ralph-state.json")) === null) {
|
|
73650
73667
|
return /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(ErrorMessage, {
|
|
73651
73668
|
message: `Error: change '${args.name}' not found`
|
|
73652
73669
|
}, undefined, false, undefined, this);
|
|
@@ -73698,7 +73715,7 @@ if (typeof globalThis.Bun === "undefined") {
|
|
|
73698
73715
|
async function findProjectRoot() {
|
|
73699
73716
|
let dir = process.cwd();
|
|
73700
73717
|
while (dir !== "/") {
|
|
73701
|
-
if (await exists2(
|
|
73718
|
+
if (await exists2(join21(dir, "openspec")))
|
|
73702
73719
|
return dir;
|
|
73703
73720
|
dir = resolve2(dir, "..");
|
|
73704
73721
|
}
|
|
@@ -73739,7 +73756,7 @@ try {
|
|
|
73739
73756
|
const tasksDir = layout.tasksDir;
|
|
73740
73757
|
if (args.mode === "init") {
|
|
73741
73758
|
await mkdir7(statesDir, { recursive: true });
|
|
73742
|
-
const openspecBin =
|
|
73759
|
+
const openspecBin = join21(dirname6(Bun.resolveSync("@fission-ai/openspec/package.json", import.meta.dir)), "bin", "openspec.js");
|
|
73743
73760
|
Bun.spawnSync({
|
|
73744
73761
|
cmd: [process.execPath, openspecBin, "init", "--tools", "none", "--force"],
|
|
73745
73762
|
stdio: ["inherit", "inherit", "inherit"],
|
|
@@ -73752,9 +73769,9 @@ try {
|
|
|
73752
73769
|
`);
|
|
73753
73770
|
process.exit(1);
|
|
73754
73771
|
}
|
|
73755
|
-
const worktreeDir =
|
|
73756
|
-
const changeDir =
|
|
73757
|
-
const stateDir =
|
|
73772
|
+
const worktreeDir = join21(worktreesDir(projectRoot), args.name);
|
|
73773
|
+
const changeDir = join21(tasksDir, args.name);
|
|
73774
|
+
const stateDir = join21(statesDir, args.name);
|
|
73758
73775
|
const branch = `ralph/${args.name}`;
|
|
73759
73776
|
const removed = [];
|
|
73760
73777
|
if (await exists2(worktreeDir)) {
|
|
@@ -73806,13 +73823,13 @@ try {
|
|
|
73806
73823
|
process.exit(0);
|
|
73807
73824
|
}
|
|
73808
73825
|
if (args.mode === "task" && args.name) {
|
|
73809
|
-
await mkdir7(
|
|
73810
|
-
await mkdir7(
|
|
73826
|
+
await mkdir7(join21(statesDir, args.name), { recursive: true });
|
|
73827
|
+
await mkdir7(join21(tasksDir, args.name), { recursive: true });
|
|
73811
73828
|
}
|
|
73812
73829
|
if (args.mode === "agent") {
|
|
73813
73830
|
await mkdir7(statesDir, { recursive: true });
|
|
73814
73831
|
await mkdir7(tasksDir, { recursive: true });
|
|
73815
|
-
await mkdir7(
|
|
73832
|
+
await mkdir7(join21(projectRoot, ".ralph"), { recursive: true });
|
|
73816
73833
|
}
|
|
73817
73834
|
if (args.mode === "agent" && args.jsonOutput) {
|
|
73818
73835
|
const { runAgentJson: runAgentJson2 } = await Promise.resolve().then(() => (init_json_runner(), exports_json_runner));
|
package/dist/mcp/index.js
CHANGED
|
@@ -23967,6 +23967,7 @@ var StateSchema = exports_external.object({
|
|
|
23967
23967
|
engine: exports_external.enum(["claude", "codex"]).default("claude"),
|
|
23968
23968
|
model: exports_external.string().default("opus"),
|
|
23969
23969
|
manualTest: exports_external.boolean().default(false),
|
|
23970
|
+
createPr: exports_external.boolean().default(false),
|
|
23970
23971
|
usage: UsageSchema.default({}),
|
|
23971
23972
|
history: exports_external.array(HistoryEntrySchema).default([]),
|
|
23972
23973
|
metadata: exports_external.object({ branch: exports_external.string().optional() }).default({})
|
|
@@ -24033,6 +24034,7 @@ function buildInitialState(options) {
|
|
|
24033
24034
|
engine: options.engine ?? "claude",
|
|
24034
24035
|
model: options.model ?? "opus",
|
|
24035
24036
|
manualTest: options.manualTest ?? false,
|
|
24037
|
+
createPr: options.createPr ?? false,
|
|
24036
24038
|
createdAt: now,
|
|
24037
24039
|
lastModified: now,
|
|
24038
24040
|
metadata: { branch }
|