@neriros/ralphy 2.16.5 → 2.16.6
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 +136 -139
- 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.6")
|
|
35033
|
+
return "2.16.6";
|
|
35034
35034
|
} catch {}
|
|
35035
35035
|
const dirsToTry = [];
|
|
35036
35036
|
try {
|
|
@@ -59616,19 +59616,73 @@ var init_config = __esm(() => {
|
|
|
59616
59616
|
});
|
|
59617
59617
|
});
|
|
59618
59618
|
|
|
59619
|
+
// packages/log/src/log.ts
|
|
59620
|
+
import { appendFile } from "fs/promises";
|
|
59621
|
+
import { join as join11, dirname as dirname4 } from "path";
|
|
59622
|
+
import { homedir as homedir2 } from "os";
|
|
59623
|
+
import { mkdir as mkdir2 } from "fs/promises";
|
|
59624
|
+
function fmt(type, text) {
|
|
59625
|
+
return `[${new Date().toISOString()}] [${type}] ${text}
|
|
59626
|
+
`;
|
|
59627
|
+
}
|
|
59628
|
+
function write(path, line) {
|
|
59629
|
+
appendFile(path, line).catch(() => {
|
|
59630
|
+
return;
|
|
59631
|
+
});
|
|
59632
|
+
}
|
|
59633
|
+
function logSession(text, workerLogFile) {
|
|
59634
|
+
const clean = text.replace(ANSI_RE, "").trim();
|
|
59635
|
+
if (!clean)
|
|
59636
|
+
return;
|
|
59637
|
+
const line = fmt("session", clean);
|
|
59638
|
+
write(AGENT_LOG_PATH, line);
|
|
59639
|
+
if (workerLogFile)
|
|
59640
|
+
write(workerLogFile, line);
|
|
59641
|
+
}
|
|
59642
|
+
function logCoord(text, workerLogFile) {
|
|
59643
|
+
const clean = text.replace(ANSI_RE, "").trim();
|
|
59644
|
+
if (!clean)
|
|
59645
|
+
return;
|
|
59646
|
+
const line = fmt("coord", clean);
|
|
59647
|
+
write(AGENT_LOG_PATH, line);
|
|
59648
|
+
if (workerLogFile)
|
|
59649
|
+
write(workerLogFile, line);
|
|
59650
|
+
}
|
|
59651
|
+
function logPhase(changeName, workerLogFile, phase, detail) {
|
|
59652
|
+
const msg = `${changeName}: ${phase}${detail ? ` (${detail})` : ""}`;
|
|
59653
|
+
const line = fmt("phase", msg);
|
|
59654
|
+
write(AGENT_LOG_PATH, line);
|
|
59655
|
+
write(workerLogFile, line);
|
|
59656
|
+
}
|
|
59657
|
+
function logOutput(workerLogFile, text) {
|
|
59658
|
+
write(workerLogFile, fmt("output", text));
|
|
59659
|
+
}
|
|
59660
|
+
async function initWorkerLog(logFile) {
|
|
59661
|
+
await mkdir2(dirname4(logFile), { recursive: true });
|
|
59662
|
+
await Bun.write(logFile, "");
|
|
59663
|
+
}
|
|
59664
|
+
var ANSI_RE, AGENT_LOG_PATH;
|
|
59665
|
+
var init_log = __esm(() => {
|
|
59666
|
+
ANSI_RE = /\x1b(?:\[[0-9;]*[A-Za-z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|.)/g;
|
|
59667
|
+
AGENT_LOG_PATH = join11(homedir2(), ".ralph", "agent-mode.log");
|
|
59668
|
+
mkdir2(dirname4(AGENT_LOG_PATH), { recursive: true }).catch(() => {
|
|
59669
|
+
return;
|
|
59670
|
+
});
|
|
59671
|
+
});
|
|
59672
|
+
|
|
59619
59673
|
// packages/core/src/layout.ts
|
|
59620
|
-
import { join as
|
|
59674
|
+
import { join as join12 } from "path";
|
|
59621
59675
|
function projectLayout(root) {
|
|
59622
|
-
const statesDir =
|
|
59623
|
-
const tasksDir =
|
|
59676
|
+
const statesDir = join12(root, ".ralph", "tasks");
|
|
59677
|
+
const tasksDir = join12(root, "openspec", "changes");
|
|
59624
59678
|
return {
|
|
59625
59679
|
root,
|
|
59626
59680
|
statesDir,
|
|
59627
59681
|
tasksDir,
|
|
59628
|
-
agentStateFile:
|
|
59629
|
-
changeDir: (name) =>
|
|
59630
|
-
taskStateDir: (name) =>
|
|
59631
|
-
stateFile: (name) =>
|
|
59682
|
+
agentStateFile: join12(root, ".ralph", "agent-state.json"),
|
|
59683
|
+
changeDir: (name) => join12(tasksDir, name),
|
|
59684
|
+
taskStateDir: (name) => join12(statesDir, name),
|
|
59685
|
+
stateFile: (name) => join12(statesDir, name, STATE_FILE2)
|
|
59632
59686
|
};
|
|
59633
59687
|
}
|
|
59634
59688
|
var STATE_FILE2 = ".ralph-state.json";
|
|
@@ -60221,19 +60275,19 @@ var init_coordinator = __esm(() => {
|
|
|
60221
60275
|
});
|
|
60222
60276
|
|
|
60223
60277
|
// apps/cli/src/agent/scaffold.ts
|
|
60224
|
-
import { join as
|
|
60225
|
-
import { mkdir as
|
|
60278
|
+
import { join as join13 } from "path";
|
|
60279
|
+
import { mkdir as mkdir3 } from "fs/promises";
|
|
60226
60280
|
function changeNameForIssue(issue) {
|
|
60227
60281
|
const slug = issue.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
|
|
60228
60282
|
return slug ? `${issue.identifier.toLowerCase()}-${slug}` : issue.identifier.toLowerCase();
|
|
60229
60283
|
}
|
|
60230
60284
|
async function scaffoldChangeForIssue(tasksDir, statesDir, issue, comments = [], appendPrompt = "") {
|
|
60231
60285
|
const name = changeNameForIssue(issue);
|
|
60232
|
-
const changeDir =
|
|
60233
|
-
const stateDir =
|
|
60234
|
-
await
|
|
60235
|
-
await
|
|
60236
|
-
await
|
|
60286
|
+
const changeDir = join13(tasksDir, name);
|
|
60287
|
+
const stateDir = join13(statesDir, name);
|
|
60288
|
+
await mkdir3(changeDir, { recursive: true });
|
|
60289
|
+
await mkdir3(join13(changeDir, "specs"), { recursive: true });
|
|
60290
|
+
await mkdir3(stateDir, { recursive: true });
|
|
60237
60291
|
const commentsBlock = comments.length > 0 ? [
|
|
60238
60292
|
"",
|
|
60239
60293
|
"## Linear comments",
|
|
@@ -60284,26 +60338,26 @@ async function scaffoldChangeForIssue(tasksDir, statesDir, issue, comments = [],
|
|
|
60284
60338
|
""
|
|
60285
60339
|
].join(`
|
|
60286
60340
|
`);
|
|
60287
|
-
await Bun.write(
|
|
60288
|
-
await Bun.write(
|
|
60289
|
-
await Bun.write(
|
|
60341
|
+
await Bun.write(join13(changeDir, "proposal.md"), proposal);
|
|
60342
|
+
await Bun.write(join13(changeDir, "tasks.md"), tasks);
|
|
60343
|
+
await Bun.write(join13(changeDir, "design.md"), design);
|
|
60290
60344
|
return name;
|
|
60291
60345
|
}
|
|
60292
60346
|
var init_scaffold = () => {};
|
|
60293
60347
|
|
|
60294
60348
|
// apps/cli/src/agent/worktree.ts
|
|
60295
|
-
import { basename, join as
|
|
60296
|
-
import { homedir as
|
|
60349
|
+
import { basename, join as join14 } from "path";
|
|
60350
|
+
import { homedir as homedir3 } from "os";
|
|
60297
60351
|
import { exists } from "fs/promises";
|
|
60298
60352
|
function worktreesDir(projectRoot) {
|
|
60299
|
-
return
|
|
60353
|
+
return join14(homedir3(), ".ralph", basename(projectRoot), "worktrees");
|
|
60300
60354
|
}
|
|
60301
60355
|
function branchForChange(changeName) {
|
|
60302
60356
|
return `ralph/${changeName}`;
|
|
60303
60357
|
}
|
|
60304
60358
|
async function createWorktree(projectRoot, changeName, runner) {
|
|
60305
60359
|
const dir = worktreesDir(projectRoot);
|
|
60306
|
-
const cwd2 =
|
|
60360
|
+
const cwd2 = join14(dir, changeName);
|
|
60307
60361
|
const branch = branchForChange(changeName);
|
|
60308
60362
|
const list = await runner.run(["worktree", "list", "--porcelain"], projectRoot);
|
|
60309
60363
|
if (list.stdout.includes(`worktree ${cwd2}
|
|
@@ -60360,8 +60414,8 @@ async function isWorktreeSafeToRemove(cwd2, base2, runner) {
|
|
|
60360
60414
|
return { safe: true, dirty, unpushedCommits };
|
|
60361
60415
|
}
|
|
60362
60416
|
async function seedWorktreeMcpConfig(projectRoot, worktreeCwd) {
|
|
60363
|
-
const dst =
|
|
60364
|
-
const src =
|
|
60417
|
+
const dst = join14(worktreeCwd, ".mcp.json");
|
|
60418
|
+
const src = join14(projectRoot, ".mcp.json");
|
|
60365
60419
|
const source = await exists(dst) ? dst : await exists(src) ? src : null;
|
|
60366
60420
|
if (!source)
|
|
60367
60421
|
return;
|
|
@@ -60375,7 +60429,7 @@ async function seedWorktreeMcpConfig(projectRoot, worktreeCwd) {
|
|
|
60375
60429
|
if (servers && typeof servers === "object") {
|
|
60376
60430
|
for (const cfg of Object.values(servers)) {
|
|
60377
60431
|
if (Array.isArray(cfg.args)) {
|
|
60378
|
-
cfg.args = cfg.args.map((a) => typeof a === "string" && a.startsWith(".ralph/") ?
|
|
60432
|
+
cfg.args = cfg.args.map((a) => typeof a === "string" && a.startsWith(".ralph/") ? join14(projectRoot, a) : a);
|
|
60379
60433
|
}
|
|
60380
60434
|
}
|
|
60381
60435
|
}
|
|
@@ -60558,7 +60612,7 @@ var init_ci = __esm(() => {
|
|
|
60558
60612
|
});
|
|
60559
60613
|
|
|
60560
60614
|
// apps/cli/src/agent/post-task.ts
|
|
60561
|
-
import { join as
|
|
60615
|
+
import { join as join15 } from "path";
|
|
60562
60616
|
async function reactivateState(stateFilePath, log2, changeName) {
|
|
60563
60617
|
const file = Bun.file(stateFilePath);
|
|
60564
60618
|
if (!await file.exists())
|
|
@@ -60577,7 +60631,7 @@ async function reactivateState(stateFilePath, log2, changeName) {
|
|
|
60577
60631
|
}
|
|
60578
60632
|
async function runWorkerWithFixTask(ctx, heading, body) {
|
|
60579
60633
|
try {
|
|
60580
|
-
await prependFixTask(
|
|
60634
|
+
await prependFixTask(join15(ctx.changeDir, "tasks.md"), heading, body);
|
|
60581
60635
|
} catch (err) {
|
|
60582
60636
|
ctx.log(`! could not prepend fix task: ${err.message}`, "red");
|
|
60583
60637
|
return 1;
|
|
@@ -60889,8 +60943,8 @@ var init_post_task = __esm(() => {
|
|
|
60889
60943
|
});
|
|
60890
60944
|
|
|
60891
60945
|
// apps/cli/src/agent/wire.ts
|
|
60892
|
-
import { join as
|
|
60893
|
-
import { mkdir as
|
|
60946
|
+
import { join as join16 } from "path";
|
|
60947
|
+
import { mkdir as mkdir4 } from "fs/promises";
|
|
60894
60948
|
function traceCmdRunner(base2, onStart, onEnd) {
|
|
60895
60949
|
return {
|
|
60896
60950
|
run: async (cmd, cwd2) => {
|
|
@@ -60951,7 +61005,7 @@ function buildAgentCoordinator(input) {
|
|
|
60951
61005
|
onWorkerOutput,
|
|
60952
61006
|
onWorkerCmd
|
|
60953
61007
|
} = input;
|
|
60954
|
-
const logsDir =
|
|
61008
|
+
const logsDir = join16(projectRoot, ".ralph", "logs");
|
|
60955
61009
|
const concurrency = args.concurrency || cfg.concurrency;
|
|
60956
61010
|
const pollInterval = args.pollInterval || cfg.pollIntervalSeconds;
|
|
60957
61011
|
const indicators = mergeIndicators(cfg.linear.indicators, args.indicators);
|
|
@@ -61143,8 +61197,8 @@ function buildAgentCoordinator(input) {
|
|
|
61143
61197
|
} else {
|
|
61144
61198
|
changeName = changeNameForIssue(issue);
|
|
61145
61199
|
const wtLayout = projectLayout(workerCwd);
|
|
61146
|
-
await
|
|
61147
|
-
await
|
|
61200
|
+
await mkdir4(wtLayout.changeDir(changeName), { recursive: true });
|
|
61201
|
+
await mkdir4(wtLayout.taskStateDir(changeName), { recursive: true });
|
|
61148
61202
|
}
|
|
61149
61203
|
cwdByChange.set(changeName, workerCwd);
|
|
61150
61204
|
statesDirByChange.set(changeName, scaffoldStatesDir);
|
|
@@ -61153,7 +61207,7 @@ function buildAgentCoordinator(input) {
|
|
|
61153
61207
|
branchByChange.set(changeName, branch);
|
|
61154
61208
|
if (mode === "conflict-fix") {
|
|
61155
61209
|
const wtLayout = projectLayout(workerCwd);
|
|
61156
|
-
const tasksFile =
|
|
61210
|
+
const tasksFile = join16(wtLayout.changeDir(changeName), "tasks.md");
|
|
61157
61211
|
const prUrl = prByChange.get(changeName);
|
|
61158
61212
|
const body = [
|
|
61159
61213
|
`The PR for this change has merge conflicts with \`${cfg.prBaseBranch}\`.`,
|
|
@@ -61231,21 +61285,8 @@ PR: ${prUrl}` : ""
|
|
|
61231
61285
|
return c;
|
|
61232
61286
|
}
|
|
61233
61287
|
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;
|
|
61288
|
+
const logFilePath = join16(logsDir, `${changeName}.log`);
|
|
61289
|
+
const ANSI_RE2 = /\x1b(?:\[[0-9;]*[A-Za-z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|.)/g;
|
|
61249
61290
|
const BOX_ONLY_RE = /^[\s\u2500\u2502\u256D\u256E\u2570\u256F\u254C\u2504\u2501\u2503]+$/;
|
|
61250
61291
|
const STATUS_BAR_LINE_RE = /^[\u280B\u2819\u2839\u2838\u283C\u2834\u2826\u2827\u2807\u280F\u2713\u2717]\s+iter\s+\d+/;
|
|
61251
61292
|
const ITER_HEADER_LINE_RE = /^\u2500\u2500/;
|
|
@@ -61258,7 +61299,6 @@ PR: ${prUrl}` : ""
|
|
|
61258
61299
|
const reader = stream.getReader();
|
|
61259
61300
|
const decoder = new TextDecoder;
|
|
61260
61301
|
let buf = "";
|
|
61261
|
-
const writer = await ensureLogWriter();
|
|
61262
61302
|
try {
|
|
61263
61303
|
while (true) {
|
|
61264
61304
|
const { value, done } = await reader.read();
|
|
@@ -61271,26 +61311,20 @@ PR: ${prUrl}` : ""
|
|
|
61271
61311
|
`)) >= 0) {
|
|
61272
61312
|
const line = buf.slice(0, nl);
|
|
61273
61313
|
buf = buf.slice(nl + 1);
|
|
61274
|
-
const clean = line.replace(
|
|
61275
|
-
if (
|
|
61276
|
-
|
|
61277
|
-
`);
|
|
61314
|
+
const clean = line.replace(ANSI_RE2, "").trim();
|
|
61315
|
+
if (clean && isLogWorthy(clean))
|
|
61316
|
+
logOutput(logFilePath, clean);
|
|
61278
61317
|
if (line)
|
|
61279
61318
|
onWorkerOutput?.(changeName, label === "err" ? `! ${line}` : line);
|
|
61280
61319
|
}
|
|
61281
61320
|
}
|
|
61282
61321
|
if (buf) {
|
|
61283
|
-
const clean = buf.replace(
|
|
61284
|
-
if (
|
|
61285
|
-
|
|
61286
|
-
`);
|
|
61322
|
+
const clean = buf.replace(ANSI_RE2, "").trim();
|
|
61323
|
+
if (clean && isLogWorthy(clean))
|
|
61324
|
+
logOutput(logFilePath, clean);
|
|
61287
61325
|
onWorkerOutput?.(changeName, label === "err" ? `! ${buf}` : buf);
|
|
61288
61326
|
}
|
|
61289
|
-
} catch {}
|
|
61290
|
-
try {
|
|
61291
|
-
writer?.flush();
|
|
61292
|
-
} catch {}
|
|
61293
|
-
}
|
|
61327
|
+
} catch {}
|
|
61294
61328
|
}
|
|
61295
61329
|
const p = Bun.spawn({
|
|
61296
61330
|
cmd,
|
|
@@ -61299,13 +61333,10 @@ PR: ${prUrl}` : ""
|
|
|
61299
61333
|
stderr: "pipe",
|
|
61300
61334
|
stdin: "ignore"
|
|
61301
61335
|
});
|
|
61302
|
-
(
|
|
61303
|
-
|
|
61304
|
-
|
|
61305
|
-
|
|
61306
|
-
--- ${note} ---
|
|
61307
|
-
`);
|
|
61308
|
-
})();
|
|
61336
|
+
initWorkerLog(logFilePath).then(() => {
|
|
61337
|
+
if (note)
|
|
61338
|
+
logSession(note, logFilePath);
|
|
61339
|
+
});
|
|
61309
61340
|
pump(p.stdout, "out");
|
|
61310
61341
|
pump(p.stderr, "err");
|
|
61311
61342
|
return { exited: p.exited, kill: () => p.kill(), logFilePath };
|
|
@@ -61316,7 +61347,7 @@ PR: ${prUrl}` : ""
|
|
|
61316
61347
|
let logFilePath;
|
|
61317
61348
|
let handle;
|
|
61318
61349
|
if (injected) {
|
|
61319
|
-
logFilePath =
|
|
61350
|
+
logFilePath = join16(logsDir, `${changeName}.log`);
|
|
61320
61351
|
handle = injected(buildTaskCmdFor(changeName), cwd2);
|
|
61321
61352
|
} else {
|
|
61322
61353
|
const r = defaultSpawn(changeName, buildTaskCmdFor(changeName), cwd2, `spawn at ${new Date().toISOString()}`);
|
|
@@ -61500,6 +61531,7 @@ function describeIndicators(indicators, team, assignee) {
|
|
|
61500
61531
|
}
|
|
61501
61532
|
var bunGitRunner, bunCmdRunner;
|
|
61502
61533
|
var init_wire = __esm(() => {
|
|
61534
|
+
init_log();
|
|
61503
61535
|
init_layout();
|
|
61504
61536
|
init_types2();
|
|
61505
61537
|
init_coordinator();
|
|
@@ -61546,7 +61578,7 @@ var exports_json_runner = {};
|
|
|
61546
61578
|
__export(exports_json_runner, {
|
|
61547
61579
|
runAgentJson: () => runAgentJson
|
|
61548
61580
|
});
|
|
61549
|
-
import { join as
|
|
61581
|
+
import { join as join20 } from "path";
|
|
61550
61582
|
import { mkdir as mkdir6 } from "fs/promises";
|
|
61551
61583
|
import { homedir as homedir4 } from "os";
|
|
61552
61584
|
function cleanOutputLine2(raw) {
|
|
@@ -61571,7 +61603,7 @@ async function runAgentJson({
|
|
|
61571
61603
|
statesDir,
|
|
61572
61604
|
tasksDir
|
|
61573
61605
|
}) {
|
|
61574
|
-
await mkdir6(
|
|
61606
|
+
await mkdir6(join20(homedir4(), ".ralph"), { recursive: true }).catch(() => {
|
|
61575
61607
|
return;
|
|
61576
61608
|
});
|
|
61577
61609
|
const cfgPath = await ensureRalphyConfig(projectRoot);
|
|
@@ -61677,7 +61709,7 @@ var init_json_runner = __esm(() => {
|
|
|
61677
61709
|
});
|
|
61678
61710
|
|
|
61679
61711
|
// apps/cli/src/index.ts
|
|
61680
|
-
import { resolve as resolve2, join as
|
|
61712
|
+
import { resolve as resolve2, join as join21, dirname as dirname6 } from "path";
|
|
61681
61713
|
import { exists as exists2, mkdir as mkdir7, rm } from "fs/promises";
|
|
61682
61714
|
|
|
61683
61715
|
// node_modules/.bun/ink@5.2.1+1f88f629f0141b18/node_modules/ink/build/render.js
|
|
@@ -66800,7 +66832,7 @@ function createDefaultContext() {
|
|
|
66800
66832
|
|
|
66801
66833
|
// apps/cli/src/components/App.tsx
|
|
66802
66834
|
var import_react58 = __toESM(require_react(), 1);
|
|
66803
|
-
import { join as
|
|
66835
|
+
import { join as join19 } from "path";
|
|
66804
66836
|
|
|
66805
66837
|
// packages/core/src/state.ts
|
|
66806
66838
|
init_types2();
|
|
@@ -72377,10 +72409,8 @@ var import_react57 = __toESM(require_react(), 1);
|
|
|
72377
72409
|
init_cli();
|
|
72378
72410
|
init_config();
|
|
72379
72411
|
init_wire();
|
|
72380
|
-
import { join as
|
|
72412
|
+
import { join as join17 } from "path";
|
|
72381
72413
|
import { pathToFileURL } from "url";
|
|
72382
|
-
import { homedir as homedir3 } from "os";
|
|
72383
|
-
import { appendFile, mkdir as mkdir4 } from "fs/promises";
|
|
72384
72414
|
|
|
72385
72415
|
// packages/core/src/progress.ts
|
|
72386
72416
|
function countProgress(content) {
|
|
@@ -72390,6 +72420,7 @@ function countProgress(content) {
|
|
|
72390
72420
|
}
|
|
72391
72421
|
|
|
72392
72422
|
// apps/cli/src/components/AgentMode.tsx
|
|
72423
|
+
init_log();
|
|
72393
72424
|
var jsx_dev_runtime9 = __toESM(require_jsx_dev_runtime(), 1);
|
|
72394
72425
|
var lineCounter = 0;
|
|
72395
72426
|
function nextId() {
|
|
@@ -72592,28 +72623,7 @@ function displayTailLines(activeCount) {
|
|
|
72592
72623
|
return 8;
|
|
72593
72624
|
return 5;
|
|
72594
72625
|
}
|
|
72595
|
-
var AGENT_LOG_PATH = join16(homedir3(), ".ralph", "agent-mode.log");
|
|
72596
72626
|
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
72627
|
function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
72618
72628
|
const { exit } = use_app_default();
|
|
72619
72629
|
const { stdout } = use_stdout_default();
|
|
@@ -72627,15 +72637,15 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
72627
72637
|
const nextPollAtRef = import_react57.useRef(0);
|
|
72628
72638
|
const cfgRef = import_react57.useRef(null);
|
|
72629
72639
|
const [pollStatus, setPollStatus] = import_react57.useState({ state: "idle", lastFound: null, lastAdded: null, lastAt: null, filterDesc: "" });
|
|
72630
|
-
function appendLog(text, color) {
|
|
72640
|
+
function appendLog(text, color, workerLogFile) {
|
|
72631
72641
|
setLogs((prev) => [...prev, { id: nextId(), text, color }]);
|
|
72632
|
-
|
|
72642
|
+
logCoord(text, workerLogFile);
|
|
72633
72643
|
}
|
|
72634
72644
|
import_react57.useEffect(() => {
|
|
72635
72645
|
let pollTimer = null;
|
|
72636
72646
|
let cancelled = false;
|
|
72637
72647
|
async function init2() {
|
|
72638
|
-
|
|
72648
|
+
logSession(`=== session start ${SESSION_START} ===`);
|
|
72639
72649
|
const cfgPath = await ensureRalphyConfig(projectRoot);
|
|
72640
72650
|
const cfg2 = await loadRalphyConfig(projectRoot);
|
|
72641
72651
|
cfgRef.current = cfg2;
|
|
@@ -72656,17 +72666,11 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
72656
72666
|
onLog: appendLog,
|
|
72657
72667
|
onWorkersChanged: () => setTick((t) => t + 1),
|
|
72658
72668
|
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
|
-
});
|
|
72669
|
+
logSession(`worker-started ${changeName} log=${logFile}`, logFile);
|
|
72665
72670
|
workerMetaRef.current.set(changeName, {
|
|
72666
72671
|
startedAt: Date.now(),
|
|
72667
72672
|
statesDir: dir,
|
|
72668
72673
|
logFile,
|
|
72669
|
-
phaseLogFile,
|
|
72670
72674
|
changeDir,
|
|
72671
72675
|
iter: 0,
|
|
72672
72676
|
phase: "working",
|
|
@@ -72680,26 +72684,19 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
72680
72684
|
});
|
|
72681
72685
|
},
|
|
72682
72686
|
onWorkerExited: (changeName) => {
|
|
72683
|
-
writeAgentLog(`worker-exited ${changeName}`);
|
|
72684
72687
|
const m = workerMetaRef.current.get(changeName);
|
|
72685
|
-
|
|
72686
|
-
writePhaseLog(m.phaseLogFile, `=== worker-exited ===`);
|
|
72687
|
-
}
|
|
72688
|
+
logSession(`worker-exited ${changeName}`, m?.logFile);
|
|
72688
72689
|
workerMetaRef.current.delete(changeName);
|
|
72689
72690
|
},
|
|
72690
72691
|
onWorkerPhase: (changeName, phase, detail) => {
|
|
72691
72692
|
const m = workerMetaRef.current.get(changeName);
|
|
72692
72693
|
if (!m)
|
|
72693
72694
|
return;
|
|
72694
|
-
if (m.phase !== phase)
|
|
72695
|
-
writeAgentLog(`phase ${changeName}: ${phase}${detail ? ` (${detail})` : ""}`);
|
|
72695
|
+
if (m.phase !== phase)
|
|
72696
72696
|
m.phaseStartedAt = Date.now();
|
|
72697
|
-
}
|
|
72698
72697
|
m.phase = phase;
|
|
72699
72698
|
m.phaseDetail = detail ?? "";
|
|
72700
|
-
|
|
72701
|
-
writePhaseLog(m.phaseLogFile, `${phase}${detail ? ` (${detail})` : ""}`);
|
|
72702
|
-
}
|
|
72699
|
+
logPhase(changeName, m.logFile, phase, detail);
|
|
72703
72700
|
},
|
|
72704
72701
|
onWorkerOutput: (changeName, line) => {
|
|
72705
72702
|
const m = workerMetaRef.current.get(changeName);
|
|
@@ -72780,7 +72777,7 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
72780
72777
|
(async () => {
|
|
72781
72778
|
for (const [changeName, meta] of workerMetaRef.current) {
|
|
72782
72779
|
try {
|
|
72783
|
-
const file = Bun.file(
|
|
72780
|
+
const file = Bun.file(join17(meta.statesDir, changeName, ".ralph-state.json"));
|
|
72784
72781
|
if (await file.exists()) {
|
|
72785
72782
|
const json = await file.json();
|
|
72786
72783
|
meta.iter = json.iteration ?? meta.iter;
|
|
@@ -72790,7 +72787,7 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
72790
72787
|
}
|
|
72791
72788
|
if (meta.changeDir) {
|
|
72792
72789
|
try {
|
|
72793
|
-
const tasksFile = Bun.file(
|
|
72790
|
+
const tasksFile = Bun.file(join17(meta.changeDir, "tasks.md"));
|
|
72794
72791
|
if (await tasksFile.exists()) {
|
|
72795
72792
|
const text = await tasksFile.text();
|
|
72796
72793
|
const match = text.match(/^- \[ \] (.+)$/m);
|
|
@@ -73450,11 +73447,11 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
73450
73447
|
}
|
|
73451
73448
|
|
|
73452
73449
|
// packages/openspec/src/openspec-change-store.ts
|
|
73453
|
-
import { join as
|
|
73450
|
+
import { join as join18, dirname as dirname5 } from "path";
|
|
73454
73451
|
import { readdir, mkdir as mkdir5 } from "fs/promises";
|
|
73455
73452
|
function resolveOpenspecBin() {
|
|
73456
73453
|
const pkgJsonPath = Bun.resolveSync("@fission-ai/openspec/package.json", import.meta.dir);
|
|
73457
|
-
return
|
|
73454
|
+
return join18(dirname5(pkgJsonPath), "bin", "openspec.js");
|
|
73458
73455
|
}
|
|
73459
73456
|
function runOpenspec(args, options = {}) {
|
|
73460
73457
|
const stdio = options.inherit ? ["inherit", "inherit", "inherit"] : ["ignore", "pipe", "pipe"];
|
|
@@ -73480,7 +73477,7 @@ class OpenSpecChangeStore {
|
|
|
73480
73477
|
}
|
|
73481
73478
|
}
|
|
73482
73479
|
getChangeDirectory(name) {
|
|
73483
|
-
return
|
|
73480
|
+
return join18("openspec", "changes", name);
|
|
73484
73481
|
}
|
|
73485
73482
|
async listChanges() {
|
|
73486
73483
|
const result2 = runOpenspec(["list", "--json"]);
|
|
@@ -73494,7 +73491,7 @@ class OpenSpecChangeStore {
|
|
|
73494
73491
|
}
|
|
73495
73492
|
} catch {}
|
|
73496
73493
|
}
|
|
73497
|
-
const changesDir =
|
|
73494
|
+
const changesDir = join18("openspec", "changes");
|
|
73498
73495
|
if (!await Bun.file(changesDir).exists())
|
|
73499
73496
|
return [];
|
|
73500
73497
|
try {
|
|
@@ -73505,18 +73502,18 @@ class OpenSpecChangeStore {
|
|
|
73505
73502
|
}
|
|
73506
73503
|
}
|
|
73507
73504
|
async readTaskList(name) {
|
|
73508
|
-
const file = Bun.file(
|
|
73505
|
+
const file = Bun.file(join18("openspec", "changes", name, "tasks.md"));
|
|
73509
73506
|
if (!await file.exists())
|
|
73510
73507
|
return "";
|
|
73511
73508
|
return await file.text();
|
|
73512
73509
|
}
|
|
73513
73510
|
async writeTaskList(name, content) {
|
|
73514
|
-
const path =
|
|
73511
|
+
const path = join18("openspec", "changes", name, "tasks.md");
|
|
73515
73512
|
await mkdir5(dirname5(path), { recursive: true });
|
|
73516
73513
|
await Bun.write(path, content);
|
|
73517
73514
|
}
|
|
73518
73515
|
async appendSteering(name, message) {
|
|
73519
|
-
const path =
|
|
73516
|
+
const path = join18("openspec", "changes", name, "steering.md");
|
|
73520
73517
|
const file = Bun.file(path);
|
|
73521
73518
|
const existing = await file.exists() ? await file.text() : null;
|
|
73522
73519
|
const updated = existing ? `${message}
|
|
@@ -73527,7 +73524,7 @@ ${existing.trimStart()}` : `${message}
|
|
|
73527
73524
|
await Bun.write(path, updated);
|
|
73528
73525
|
}
|
|
73529
73526
|
async readSection(name, artifact, heading) {
|
|
73530
|
-
const file = Bun.file(
|
|
73527
|
+
const file = Bun.file(join18("openspec", "changes", name, artifact));
|
|
73531
73528
|
if (!await file.exists())
|
|
73532
73529
|
return "";
|
|
73533
73530
|
const content = await file.text();
|
|
@@ -73645,8 +73642,8 @@ function App2({ args, statesDir, tasksDir, projectRoot }) {
|
|
|
73645
73642
|
message: "Error: --name is required for status mode"
|
|
73646
73643
|
}, undefined, false, undefined, this);
|
|
73647
73644
|
}
|
|
73648
|
-
const stateDir =
|
|
73649
|
-
if (getStorage().read(
|
|
73645
|
+
const stateDir = join19(statesDir, args.name);
|
|
73646
|
+
if (getStorage().read(join19(stateDir, ".ralph-state.json")) === null) {
|
|
73650
73647
|
return /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(ErrorMessage, {
|
|
73651
73648
|
message: `Error: change '${args.name}' not found`
|
|
73652
73649
|
}, undefined, false, undefined, this);
|
|
@@ -73698,7 +73695,7 @@ if (typeof globalThis.Bun === "undefined") {
|
|
|
73698
73695
|
async function findProjectRoot() {
|
|
73699
73696
|
let dir = process.cwd();
|
|
73700
73697
|
while (dir !== "/") {
|
|
73701
|
-
if (await exists2(
|
|
73698
|
+
if (await exists2(join21(dir, "openspec")))
|
|
73702
73699
|
return dir;
|
|
73703
73700
|
dir = resolve2(dir, "..");
|
|
73704
73701
|
}
|
|
@@ -73739,7 +73736,7 @@ try {
|
|
|
73739
73736
|
const tasksDir = layout.tasksDir;
|
|
73740
73737
|
if (args.mode === "init") {
|
|
73741
73738
|
await mkdir7(statesDir, { recursive: true });
|
|
73742
|
-
const openspecBin =
|
|
73739
|
+
const openspecBin = join21(dirname6(Bun.resolveSync("@fission-ai/openspec/package.json", import.meta.dir)), "bin", "openspec.js");
|
|
73743
73740
|
Bun.spawnSync({
|
|
73744
73741
|
cmd: [process.execPath, openspecBin, "init", "--tools", "none", "--force"],
|
|
73745
73742
|
stdio: ["inherit", "inherit", "inherit"],
|
|
@@ -73752,9 +73749,9 @@ try {
|
|
|
73752
73749
|
`);
|
|
73753
73750
|
process.exit(1);
|
|
73754
73751
|
}
|
|
73755
|
-
const worktreeDir =
|
|
73756
|
-
const changeDir =
|
|
73757
|
-
const stateDir =
|
|
73752
|
+
const worktreeDir = join21(worktreesDir(projectRoot), args.name);
|
|
73753
|
+
const changeDir = join21(tasksDir, args.name);
|
|
73754
|
+
const stateDir = join21(statesDir, args.name);
|
|
73758
73755
|
const branch = `ralph/${args.name}`;
|
|
73759
73756
|
const removed = [];
|
|
73760
73757
|
if (await exists2(worktreeDir)) {
|
|
@@ -73806,13 +73803,13 @@ try {
|
|
|
73806
73803
|
process.exit(0);
|
|
73807
73804
|
}
|
|
73808
73805
|
if (args.mode === "task" && args.name) {
|
|
73809
|
-
await mkdir7(
|
|
73810
|
-
await mkdir7(
|
|
73806
|
+
await mkdir7(join21(statesDir, args.name), { recursive: true });
|
|
73807
|
+
await mkdir7(join21(tasksDir, args.name), { recursive: true });
|
|
73811
73808
|
}
|
|
73812
73809
|
if (args.mode === "agent") {
|
|
73813
73810
|
await mkdir7(statesDir, { recursive: true });
|
|
73814
73811
|
await mkdir7(tasksDir, { recursive: true });
|
|
73815
|
-
await mkdir7(
|
|
73812
|
+
await mkdir7(join21(projectRoot, ".ralph"), { recursive: true });
|
|
73816
73813
|
}
|
|
73817
73814
|
if (args.mode === "agent" && args.jsonOutput) {
|
|
73818
73815
|
const { runAgentJson: runAgentJson2 } = await Promise.resolve().then(() => (init_json_runner(), exports_json_runner));
|