@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.
Files changed (3) hide show
  1. package/README.md +16 -1
  2. package/dist/cli/index.js +136 -139
  3. 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.5")
35033
- return "2.16.5";
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 join11 } from "path";
59674
+ import { join as join12 } from "path";
59621
59675
  function projectLayout(root) {
59622
- const statesDir = join11(root, ".ralph", "tasks");
59623
- const tasksDir = join11(root, "openspec", "changes");
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: join11(root, ".ralph", "agent-state.json"),
59629
- changeDir: (name) => join11(tasksDir, name),
59630
- taskStateDir: (name) => join11(statesDir, name),
59631
- stateFile: (name) => join11(statesDir, name, STATE_FILE2)
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 join12 } from "path";
60225
- import { mkdir as mkdir2 } from "fs/promises";
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 = join12(tasksDir, name);
60233
- const stateDir = join12(statesDir, name);
60234
- await mkdir2(changeDir, { recursive: true });
60235
- await mkdir2(join12(changeDir, "specs"), { recursive: true });
60236
- await mkdir2(stateDir, { recursive: true });
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(join12(changeDir, "proposal.md"), proposal);
60288
- await Bun.write(join12(changeDir, "tasks.md"), tasks);
60289
- await Bun.write(join12(changeDir, "design.md"), design);
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 join13 } from "path";
60296
- import { homedir as homedir2 } from "os";
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 join13(homedir2(), ".ralph", basename(projectRoot), "worktrees");
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 = join13(dir, changeName);
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 = join13(worktreeCwd, ".mcp.json");
60364
- const src = join13(projectRoot, ".mcp.json");
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/") ? join13(projectRoot, a) : a);
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 join14 } from "path";
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(join14(ctx.changeDir, "tasks.md"), heading, body);
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 join15 } from "path";
60893
- import { mkdir as mkdir3 } from "fs/promises";
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 = join15(projectRoot, ".ralph", "logs");
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 mkdir3(wtLayout.changeDir(changeName), { recursive: true });
61147
- await mkdir3(wtLayout.taskStateDir(changeName), { recursive: true });
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 = join15(wtLayout.changeDir(changeName), "tasks.md");
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 = join15(logsDir, `${changeName}.log`);
61235
- let logWriter = null;
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(ANSI_RE, "").trim();
61275
- if (writer && clean && isLogWorthy(clean))
61276
- writer.write(clean + `
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(ANSI_RE, "").trim();
61284
- if (writer && clean && isLogWorthy(clean))
61285
- writer.write(clean + `
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 {} finally {
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
- (async () => {
61303
- const writer = await ensureLogWriter();
61304
- if (note && writer)
61305
- writer.write(`
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 = join15(logsDir, `${changeName}.log`);
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 join19 } from "path";
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(join19(homedir4(), ".ralph"), { recursive: true }).catch(() => {
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 join20, dirname as dirname6 } from "path";
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 join18 } from "path";
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 join16, dirname as dirname4 } from "path";
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
- writeAgentLog(text);
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
- writeAgentLog(`=== session start ${SESSION_START} ===`);
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
- writeAgentLog(`worker-started ${changeName} log=${logFile}`);
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
- if (m?.phaseLogFile) {
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
- if (m.phaseLogFile) {
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(join16(meta.statesDir, changeName, ".ralph-state.json"));
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(join16(meta.changeDir, "tasks.md"));
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 join17, dirname as dirname5 } from "path";
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 join17(dirname5(pkgJsonPath), "bin", "openspec.js");
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 join17("openspec", "changes", name);
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 = join17("openspec", "changes");
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(join17("openspec", "changes", name, "tasks.md"));
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 = join17("openspec", "changes", name, "tasks.md");
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 = join17("openspec", "changes", name, "steering.md");
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(join17("openspec", "changes", name, artifact));
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 = join18(statesDir, args.name);
73649
- if (getStorage().read(join18(stateDir, ".ralph-state.json")) === null) {
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(join20(dir, "openspec")))
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 = join20(dirname6(Bun.resolveSync("@fission-ai/openspec/package.json", import.meta.dir)), "bin", "openspec.js");
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 = join20(worktreesDir(projectRoot), args.name);
73756
- const changeDir = join20(tasksDir, args.name);
73757
- const stateDir = join20(statesDir, args.name);
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(join20(statesDir, args.name), { recursive: true });
73810
- await mkdir7(join20(tasksDir, args.name), { recursive: true });
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(join20(projectRoot, ".ralph"), { recursive: true });
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));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neriros/ralphy",
3
- "version": "2.16.5",
3
+ "version": "2.16.6",
4
4
  "description": "An iterative AI task execution framework. Orchestrates multi-phase autonomous work using Claude or Codex engines.",
5
5
  "keywords": [
6
6
  "agent",