@neriros/ralphy 3.10.0 → 3.10.2

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/dist/mcp/index.js CHANGED
@@ -6615,20 +6615,33 @@ import {
6615
6615
  writeFileSync,
6616
6616
  existsSync,
6617
6617
  unlinkSync,
6618
+ renameSync,
6618
6619
  mkdirSync,
6619
6620
  readdirSync
6620
6621
  } from "fs";
6621
6622
  import { dirname } from "path";
6622
6623
 
6623
6624
  class FileSystemProvider {
6625
+ writeSeq = 0;
6624
6626
  read(path) {
6625
6627
  if (!existsSync(path))
6626
6628
  return null;
6627
6629
  return readFileSync(path, "utf-8");
6628
6630
  }
6629
6631
  write(path, content) {
6630
- mkdirSync(dirname(path), { recursive: true });
6631
- writeFileSync(path, content, "utf-8");
6632
+ const dir = dirname(path);
6633
+ mkdirSync(dir, { recursive: true });
6634
+ const tmp = `${path}.tmp-${process.pid}-${this.writeSeq++}`;
6635
+ try {
6636
+ writeFileSync(tmp, content, "utf-8");
6637
+ renameSync(tmp, path);
6638
+ } catch (err) {
6639
+ try {
6640
+ if (existsSync(tmp))
6641
+ unlinkSync(tmp);
6642
+ } catch {}
6643
+ throw err;
6644
+ }
6632
6645
  }
6633
6646
  remove(path) {
6634
6647
  if (!existsSync(path))
@@ -24050,6 +24063,7 @@ var HistoryEntrySchema = exports_external.object({
24050
24063
  engine: exports_external.string(),
24051
24064
  model: exports_external.string(),
24052
24065
  result: exports_external.string(),
24066
+ appVersion: exports_external.string().optional(),
24053
24067
  usage: IterationUsageSchema.partial().optional()
24054
24068
  });
24055
24069
  var StateSchema = exports_external.object({
@@ -18928,8 +18928,8 @@ import { readFileSync } from "fs";
18928
18928
  import { resolve } from "path";
18929
18929
  function getVersion() {
18930
18930
  try {
18931
- if ("3.10.0")
18932
- return "3.10.0";
18931
+ if ("3.10.2")
18932
+ return "3.10.2";
18933
18933
  } catch {}
18934
18934
  const dirsToTry = [];
18935
18935
  try {
@@ -81370,8 +81370,7 @@ var init_fields = __esm(() => {
81370
81370
  id: "linear.syncSpecsAsAttachments",
81371
81371
  label: "Upload proposal.md / design.md as attachments?",
81372
81372
  description: "Upload the OpenSpec planning docs (proposal.md, design.md) to the issue as attachments. OpenSpec is Ralphy's spec-driven planning format.",
81373
- spec: yes(),
81374
- when: isOn("linear.syncTasksToComment")
81373
+ spec: yes()
81375
81374
  },
81376
81375
  {
81377
81376
  id: "linear.specAttachmentFormats",
@@ -82780,6 +82779,14 @@ function buildFromAnswers(mode, answers, build = buildWorkflowMarkdown) {
82780
82779
  else
82781
82780
  delete values2["linear.indicators"];
82782
82781
  }
82782
+ if (values2["linear.confirmationMode.enabled"] === true && values2["linear.indicators"] && typeof values2["linear.indicators"] === "object") {
82783
+ const map3 = { ...values2["linear.indicators"] };
82784
+ if (!("getApproved" in map3)) {
82785
+ map3.getApproved = { filter: [{ type: "label", value: "approved" }] };
82786
+ map3.clearApproved = { type: "label", value: "approved" };
82787
+ values2["linear.indicators"] = map3;
82788
+ }
82789
+ }
82783
82790
  let bodyOverride;
82784
82791
  if (PROMPT_BODY_FIELD_ID in values2) {
82785
82792
  const body = values2[PROMPT_BODY_FIELD_ID];
@@ -83785,12 +83792,6 @@ var init_SetupWizard = __esm(async () => {
83785
83792
  }
83786
83793
  ];
83787
83794
  CONFIRMATION_STATES = [
83788
- {
83789
- key: "confirmGate",
83790
- label: "Confirmation gate",
83791
- description: "Opt-in: only require human approval for issues that match this.",
83792
- slots: ["getConfirmGate"]
83793
- },
83794
83795
  {
83795
83796
  key: "autoApprove",
83796
83797
  label: "Auto-approve",
@@ -84079,20 +84080,33 @@ import {
84079
84080
  writeFileSync,
84080
84081
  existsSync as existsSync2,
84081
84082
  unlinkSync,
84083
+ renameSync,
84082
84084
  mkdirSync,
84083
84085
  readdirSync
84084
84086
  } from "fs";
84085
84087
  import { dirname as dirname2 } from "path";
84086
84088
 
84087
84089
  class FileSystemProvider {
84090
+ writeSeq = 0;
84088
84091
  read(path) {
84089
84092
  if (!existsSync2(path))
84090
84093
  return null;
84091
84094
  return readFileSync3(path, "utf-8");
84092
84095
  }
84093
84096
  write(path, content) {
84094
- mkdirSync(dirname2(path), { recursive: true });
84095
- writeFileSync(path, content, "utf-8");
84097
+ const dir = dirname2(path);
84098
+ mkdirSync(dir, { recursive: true });
84099
+ const tmp = `${path}.tmp-${process.pid}-${this.writeSeq++}`;
84100
+ try {
84101
+ writeFileSync(tmp, content, "utf-8");
84102
+ renameSync(tmp, path);
84103
+ } catch (err) {
84104
+ try {
84105
+ if (existsSync2(tmp))
84106
+ unlinkSync(tmp);
84107
+ } catch {}
84108
+ throw err;
84109
+ }
84096
84110
  }
84097
84111
  remove(path) {
84098
84112
  if (!existsSync2(path))
@@ -84149,7 +84163,7 @@ function projectLayout(root) {
84149
84163
  stateFile: (name) => join6(statesDir, name, STATE_FILE)
84150
84164
  };
84151
84165
  }
84152
- var STATE_FILE = ".ralph-state.json";
84166
+ var STATE_FILE = ".ralph-state.json", GAVEUP_COUNT_FILE = ".ralph-gaveup-count";
84153
84167
  var init_layout = __esm(() => {
84154
84168
  init_context();
84155
84169
  });
@@ -88848,6 +88862,7 @@ var init_types2 = __esm(() => {
88848
88862
  engine: exports_external2.string(),
88849
88863
  model: exports_external2.string(),
88850
88864
  result: exports_external2.string(),
88865
+ appVersion: exports_external2.string().optional(),
88851
88866
  usage: IterationUsageSchema.partial().optional()
88852
88867
  });
88853
88868
  StateSchema = exports_external2.object({
@@ -99615,6 +99630,7 @@ function updateStateIteration(stateDir, result2, startedAt, engine, model, usage
99615
99630
  engine,
99616
99631
  model,
99617
99632
  result: result2,
99633
+ appVersion: VERSION,
99618
99634
  usage: usage ? {
99619
99635
  cost_usd: usage.cost_usd,
99620
99636
  duration_ms: usage.duration_ms,
@@ -99676,6 +99692,7 @@ function mergeUsage(base2, resumed) {
99676
99692
  }
99677
99693
  var STEERING_MAX_LINES = 20;
99678
99694
  var init_loop = __esm(() => {
99695
+ init_version();
99679
99696
  init_state();
99680
99697
  init_context();
99681
99698
  init_tasks_md();
@@ -99807,7 +99824,9 @@ function useLoop(opts) {
99807
99824
  startingStatus: currentState.status
99808
99825
  });
99809
99826
  while (!cancelled) {
99810
- currentState = readState(stateDir);
99827
+ const { state: polled } = tryReadStateRaw(stateDir);
99828
+ if (polled !== null)
99829
+ currentState = polled;
99811
99830
  setState(currentState);
99812
99831
  if (!actor.getSnapshot().matches("running"))
99813
99832
  break;
@@ -100411,7 +100430,7 @@ import { join as join16, dirname as dirname7 } from "path";
100411
100430
  import { homedir as homedir4 } from "os";
100412
100431
  import { mkdir as mkdir5 } from "fs/promises";
100413
100432
  function fmt(type, text) {
100414
- return `[${new Date().toISOString()}] [${type}] ${text}
100433
+ return `[${new Date().toISOString()}] [v${VERSION}] [${type}] ${text}
100415
100434
  `;
100416
100435
  }
100417
100436
  function write(path, line) {
@@ -100453,6 +100472,7 @@ async function initWorkerLog(logFile) {
100453
100472
  }
100454
100473
  var jsonLogChains, ANSI_RE, AGENT_LOG_PATH;
100455
100474
  var init_log = __esm(() => {
100475
+ init_version();
100456
100476
  jsonLogChains = new Map;
100457
100477
  ANSI_RE = /\x1b(?:\[[0-9;]*[A-Za-z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|.)/g;
100458
100478
  AGENT_LOG_PATH = join16(homedir4(), ".ralph", "agent-mode.log");
@@ -101475,7 +101495,16 @@ function branchForChange(changeName) {
101475
101495
  function worktreeDirNameForIssue(issue2) {
101476
101496
  return issue2.identifier.toLowerCase();
101477
101497
  }
101478
- async function createWorktree(projectRoot, changeName, baseBranch, runner) {
101498
+ function withRepoLock(projectRoot, fn) {
101499
+ const prev = repoWorktreeLocks.get(projectRoot) ?? Promise.resolve();
101500
+ const result2 = prev.then(fn, fn);
101501
+ repoWorktreeLocks.set(projectRoot, result2.then(() => {}, () => {}));
101502
+ return result2;
101503
+ }
101504
+ function createWorktree(projectRoot, changeName, baseBranch, runner) {
101505
+ return withRepoLock(projectRoot, () => provisionWorktree(projectRoot, changeName, baseBranch, runner));
101506
+ }
101507
+ async function provisionWorktree(projectRoot, changeName, baseBranch, runner) {
101479
101508
  const dir = worktreesDir2(projectRoot);
101480
101509
  const cwd2 = join20(dir, changeName);
101481
101510
  const branch = branchForChange(changeName);
@@ -101570,7 +101599,7 @@ async function seedWorktreeMcpConfig(projectRoot, worktreeCwd) {
101570
101599
  await Bun.write(dst, JSON.stringify(parsed, null, 2) + `
101571
101600
  `);
101572
101601
  }
101573
- var PRE_PUSH_HOOK_SCRIPT = `#!/usr/bin/env bash
101602
+ var repoWorktreeLocks, PRE_PUSH_HOOK_SCRIPT = `#!/usr/bin/env bash
101574
101603
  # Installed by ralphy createWorktree (RLF-107).
101575
101604
  # Rejects any push whose remote ref is not refs/heads/ralph/*,
101576
101605
  # and rejects force pushes unless RALPH_ALLOW_FORCE_PUSH=1.
@@ -101590,7 +101619,9 @@ while read local_ref local_sha remote_ref remote_sha; do
101590
101619
  done
101591
101620
  exit 0
101592
101621
  `;
101593
- var init_worktree = () => {};
101622
+ var init_worktree = __esm(() => {
101623
+ repoWorktreeLocks = new Map;
101624
+ });
101594
101625
 
101595
101626
  // apps/agent/src/shared/capabilities/git.ts
101596
101627
  var createWorktree2, removeWorktree2, seedWorktreeMcpConfig2, git;
@@ -103276,7 +103307,7 @@ function emitFeatureSkipped(bus, id, reason) {
103276
103307
  var init_run_feature = () => {};
103277
103308
 
103278
103309
  // apps/agent/src/agent/post-task.ts
103279
- import { join as join21 } from "path";
103310
+ import { join as join21, dirname as dirname9 } from "path";
103280
103311
  function summarizeUncommittedStatus(stdout) {
103281
103312
  const lines = stdout.split(`
103282
103313
  `).filter((line) => line.length > 0);
@@ -103918,6 +103949,17 @@ async function runValidateOnlyPhase(input, deps) {
103918
103949
  await reactivateState(stateFilePath, log3, changeName);
103919
103950
  return respawnWorker();
103920
103951
  }
103952
+ async function recordGaveUp(stateFilePath, log3, changeName) {
103953
+ const path = join21(dirname9(stateFilePath), GAVEUP_COUNT_FILE);
103954
+ try {
103955
+ const file2 = Bun.file(path);
103956
+ const current = await file2.exists() ? Number.parseInt(await file2.text(), 10) || 0 : 0;
103957
+ await Bun.write(path, String(current + 1) + `
103958
+ `);
103959
+ } catch (err) {
103960
+ log3(`! could not record gave-up for ${changeName}: ${err.message}`, "yellow");
103961
+ }
103962
+ }
103921
103963
  async function runPostTask(input, deps) {
103922
103964
  const { log: log3, cmd, git: git2, runScript } = deps;
103923
103965
  const emit3 = (phase, detail) => deps.onPhase?.(phase, detail);
@@ -103961,6 +104003,8 @@ async function runPostTask(input, deps) {
103961
104003
  respawnWorker
103962
104004
  });
103963
104005
  emit3(effectiveCode === 0 ? "done" : "gave-up", effectiveCode !== 0 ? `exit ${effectiveCode}` : undefined);
104006
+ if (effectiveCode !== 0)
104007
+ await recordGaveUp(stateFilePath, log3, changeName);
103964
104008
  await runWorktreeCleanupPhase({ changeName, cwd: cwd2, projectRoot, useWorktree, effectiveCode, cfg }, { git: git2, log: log3, emit: emit3 });
103965
104009
  await runTeardownPhase({ cwd: cwd2, teardownScript: cfg.teardownScript }, { runScript, log: log3, emit: emit3 });
103966
104010
  return effectiveCode;
@@ -104035,6 +104079,8 @@ async function runPostTask(input, deps) {
104035
104079
  }
104036
104080
  const succeeded = effectiveCode === 0 || effectiveCode === NO_CHANGES_EXIT;
104037
104081
  emit3(succeeded ? "done" : "gave-up", succeeded ? undefined : `exit ${effectiveCode}`);
104082
+ if (!succeeded)
104083
+ await recordGaveUp(stateFilePath, log3, changeName);
104038
104084
  await runWorktreeCleanupPhase({ changeName, cwd: cwd2, projectRoot, useWorktree, effectiveCode, cfg }, { git: git2, log: log3, emit: emit3 });
104039
104085
  await runTeardownPhase({ cwd: cwd2, teardownScript: cfg.teardownScript }, { runScript, log: log3, emit: emit3 });
104040
104086
  return effectiveCode;
@@ -104052,6 +104098,7 @@ var CI_FAILED_EXIT = 70, PR_FAILED_EXIT = 71, NO_CHANGES_EXIT = 72, repoAutoMerg
104052
104098
  return { exitCode: proc.exitCode ?? 1, output };
104053
104099
  };
104054
104100
  var init_post_task = __esm(() => {
104101
+ init_layout();
104055
104102
  init_tasks_md();
104056
104103
  init_fs_change();
104057
104104
  init_git2();
@@ -105160,7 +105207,7 @@ var init_detections = __esm(() => {
105160
105207
  });
105161
105208
 
105162
105209
  // apps/agent/src/features/confirmation/state.ts
105163
- import { dirname as dirname9, join as join23 } from "path";
105210
+ import { dirname as dirname10, join as join23 } from "path";
105164
105211
  import { mkdir as mkdir8 } from "fs/promises";
105165
105212
  async function readConfirmationState(statePath) {
105166
105213
  const f2 = Bun.file(statePath);
@@ -105185,7 +105232,7 @@ async function readConfirmationState(statePath) {
105185
105232
  return { stateObj, confirmation };
105186
105233
  }
105187
105234
  async function writeConfirmationState(statePath, stateObj, confirmation) {
105188
- await mkdir8(dirname9(statePath), { recursive: true });
105235
+ await mkdir8(dirname10(statePath), { recursive: true });
105189
105236
  await Bun.write(statePath, JSON.stringify({ ...stateObj, confirmation }, null, 2) + `
105190
105237
  `);
105191
105238
  }
@@ -105427,7 +105474,7 @@ var init_inspect = __esm(() => {
105427
105474
  });
105428
105475
 
105429
105476
  // apps/agent/src/features/confirmation/awaiting.ts
105430
- import { join as join24, dirname as dirname10 } from "path";
105477
+ import { join as join24, dirname as dirname11 } from "path";
105431
105478
  import { mkdir as mkdir9 } from "fs/promises";
105432
105479
  async function resolveChangeCwdForIssue(issue2, changeName, deps) {
105433
105480
  const tracked = deps.cwdOf(changeName);
@@ -105489,7 +105536,7 @@ async function postPlanReadyCommentOnce(issue2, statePath, changeName, deps) {
105489
105536
  rounds: confirmation?.rounds ?? 0
105490
105537
  };
105491
105538
  try {
105492
- await mkdir9(dirname10(statePath), { recursive: true });
105539
+ await mkdir9(dirname11(statePath), { recursive: true });
105493
105540
  await Bun.write(statePath, JSON.stringify({ ...stateObj, confirmation: nextConfirmation }, null, 2) + `
105494
105541
  `);
105495
105542
  } catch (err) {
@@ -105811,7 +105858,10 @@ var init_runners = __esm(() => {
105811
105858
  const stderr = await new Response(proc.stderr).text();
105812
105859
  const code = await proc.exited;
105813
105860
  if (code !== 0) {
105814
- const err = new Error("git command failed");
105861
+ const firstStderrLine = stderr.trim().split(`
105862
+ `)[0] ?? "";
105863
+ const summary = firstStderrLine ? `: ${firstStderrLine}` : "";
105864
+ const err = new Error(`git \`${args.join(" ")}\` failed${summary}`);
105815
105865
  err.stderr = stderr;
105816
105866
  err.code = code;
105817
105867
  throw err;
@@ -106493,17 +106543,17 @@ var init_pr_discovery = __esm(() => {
106493
106543
  });
106494
106544
 
106495
106545
  // apps/agent/src/features/review-followup/scan.ts
106496
- import { dirname as dirname11, join as join26 } from "path";
106546
+ import { dirname as dirname12, join as join26 } from "path";
106497
106547
  async function resolveReviewStateDir(changeName, deps) {
106498
106548
  const root = deps.cwdOf(changeName);
106499
106549
  if (root)
106500
- return dirname11(projectLayout(root).stateFile(changeName));
106550
+ return dirname12(projectLayout(root).stateFile(changeName));
106501
106551
  if (!deps.useWorktree)
106502
- return dirname11(projectLayout(deps.projectRoot).stateFile(changeName));
106552
+ return dirname12(projectLayout(deps.projectRoot).stateFile(changeName));
106503
106553
  const wtPath = join26(worktreesDir2(deps.projectRoot), changeName);
106504
106554
  const statePath = projectLayout(wtPath).stateFile(changeName);
106505
106555
  if (await Bun.file(statePath).exists())
106506
- return dirname11(statePath);
106556
+ return dirname12(statePath);
106507
106557
  return null;
106508
106558
  }
106509
106559
  async function readReviewWatermark(stateDir) {
@@ -107541,8 +107591,8 @@ var init_linear_sync = __esm(() => {
107541
107591
  });
107542
107592
 
107543
107593
  // apps/agent/src/agent/linear-sync/comment-sync.ts
107544
- import { dirname as dirname12, join as join30 } from "path";
107545
- import { mkdir as mkdir11 } from "fs/promises";
107594
+ import { dirname as dirname13, join as join30 } from "path";
107595
+ import { mkdir as mkdir11, rename, unlink as unlink2 } from "fs/promises";
107546
107596
  async function readStateJson(statePath) {
107547
107597
  const file2 = Bun.file(statePath);
107548
107598
  if (!await file2.exists())
@@ -107554,9 +107604,16 @@ async function readStateJson(statePath) {
107554
107604
  }
107555
107605
  }
107556
107606
  async function writeStateJson(statePath, state) {
107557
- await mkdir11(dirname12(statePath), { recursive: true });
107558
- await Bun.write(statePath, JSON.stringify(state, null, 2) + `
107607
+ await mkdir11(dirname13(statePath), { recursive: true });
107608
+ const tmp = `${statePath}.tmp-${process.pid}-${writeStateSeq++}`;
107609
+ try {
107610
+ await Bun.write(tmp, JSON.stringify(state, null, 2) + `
107559
107611
  `);
107612
+ await rename(tmp, statePath);
107613
+ } catch (err) {
107614
+ await unlink2(tmp).catch(() => {});
107615
+ throw err;
107616
+ }
107560
107617
  }
107561
107618
  function readComments(state) {
107562
107619
  const raw = state?.linearComments ?? {};
@@ -107763,7 +107820,7 @@ ${deps.message.trim()}`;
107763
107820
  iteration: deps.iteration
107764
107821
  });
107765
107822
  }
107766
- var PLAN_COMMENT_TITLE = "\uD83D\uDCCB Ralph plan", STEERING_COMMENT_TITLE = "\uD83E\uDDED Ralph steering";
107823
+ var PLAN_COMMENT_TITLE = "\uD83D\uDCCB Ralph plan", STEERING_COMMENT_TITLE = "\uD83E\uDDED Ralph steering", writeStateSeq = 0;
107767
107824
  var init_comment_sync = __esm(() => {
107768
107825
  init_linear_sync();
107769
107826
  });
@@ -259977,7 +260034,7 @@ var init_render_pdf = __esm(() => {
259977
260034
  });
259978
260035
 
259979
260036
  // apps/agent/src/agent/linear-sync/spec-attachments.ts
259980
- import { dirname as dirname13, join as join31 } from "path";
260037
+ import { dirname as dirname14, join as join31 } from "path";
259981
260038
  function describeLinearError(err) {
259982
260039
  const e = err;
259983
260040
  const parts = [e.message ?? String(err)];
@@ -259992,7 +260049,7 @@ function describeLinearError(err) {
259992
260049
  return parts.join(" ");
259993
260050
  }
259994
260051
  function stateDirOf(statePath) {
259995
- return dirname13(statePath);
260052
+ return dirname14(statePath);
259996
260053
  }
259997
260054
  async function readRawState(statePath) {
259998
260055
  const file2 = Bun.file(statePath);
@@ -260251,7 +260308,9 @@ var init_spec_attachments = __esm(() => {
260251
260308
  // apps/agent/src/agent/wire/comment-sync.ts
260252
260309
  function createCommentSyncHooks(input) {
260253
260310
  const { apiKey, cfg, projectRoot, onLog, diag, cwdByChange, issueByChange } = input;
260254
- const enabled2 = Boolean(cfg.linear.syncTasksToComment && apiKey);
260311
+ const commentsEnabled = Boolean(cfg.linear.syncTasksToComment && apiKey);
260312
+ const specAttachmentsEnabled = Boolean(cfg.linear.syncSpecsAsAttachments && apiKey);
260313
+ const enabled2 = commentsEnabled || specAttachmentsEnabled;
260255
260314
  if (!enabled2)
260256
260315
  return { enabled: false };
260257
260316
  const commentMutations = {
@@ -260265,7 +260324,6 @@ function createCommentSyncHooks(input) {
260265
260324
  deleteAttachment,
260266
260325
  findIssueAttachmentByTitle
260267
260326
  };
260268
- const specAttachmentsEnabled = Boolean(cfg.linear.syncSpecsAsAttachments);
260269
260327
  return {
260270
260328
  enabled: enabled2,
260271
260329
  syncTasks: async (worker, iteration) => {
@@ -260273,27 +260331,29 @@ function createCommentSyncHooks(input) {
260273
260331
  const layout = projectLayout(root);
260274
260332
  const changeDir = layout.changeDir(worker.changeName);
260275
260333
  const statePath = layout.stateFile(worker.changeName);
260276
- if (!specAttachmentsEnabled) {
260277
- await postPlanCommentOnce({
260334
+ if (commentsEnabled) {
260335
+ if (!specAttachmentsEnabled) {
260336
+ await postPlanCommentOnce({
260337
+ apiKey,
260338
+ issueId: worker.issueId,
260339
+ statePath,
260340
+ changeDir,
260341
+ changeName: worker.changeName,
260342
+ log: onLog,
260343
+ mutations: commentMutations
260344
+ });
260345
+ }
260346
+ await postOrUpdateTasksComment({
260278
260347
  apiKey,
260279
260348
  issueId: worker.issueId,
260280
260349
  statePath,
260281
260350
  changeDir,
260282
260351
  changeName: worker.changeName,
260352
+ iteration,
260283
260353
  log: onLog,
260284
260354
  mutations: commentMutations
260285
260355
  });
260286
260356
  }
260287
- await postOrUpdateTasksComment({
260288
- apiKey,
260289
- issueId: worker.issueId,
260290
- statePath,
260291
- changeDir,
260292
- changeName: worker.changeName,
260293
- iteration,
260294
- log: onLog,
260295
- mutations: commentMutations
260296
- });
260297
260357
  if (specAttachmentsEnabled) {
260298
260358
  await syncSpecAttachments({
260299
260359
  apiKey,
@@ -260307,37 +260367,39 @@ function createCommentSyncHooks(input) {
260307
260367
  });
260308
260368
  }
260309
260369
  },
260310
- onSteeringAppended: async (changeName, message) => {
260311
- const root = cwdByChange.get(changeName) ?? projectRoot;
260312
- const layout = projectLayout(root);
260313
- const changeDir = layout.changeDir(changeName);
260314
- const statePath = layout.stateFile(changeName);
260315
- const issue2 = issueByChange.get(changeName) ?? null;
260316
- const issueId = issue2?.id ?? null;
260317
- if (!issueId) {
260318
- diag("comment-sync", ` comment-sync: no Linear issue cached for ${changeName}; skipping steering refresh`, "gray");
260319
- return;
260320
- }
260321
- let iteration = 0;
260322
- try {
260323
- const f2 = Bun.file(statePath);
260324
- if (await f2.exists()) {
260325
- const json2 = await f2.json();
260326
- iteration = json2.iteration ?? 0;
260370
+ ...commentsEnabled ? {
260371
+ onSteeringAppended: async (changeName, message) => {
260372
+ const root = cwdByChange.get(changeName) ?? projectRoot;
260373
+ const layout = projectLayout(root);
260374
+ const changeDir = layout.changeDir(changeName);
260375
+ const statePath = layout.stateFile(changeName);
260376
+ const issue2 = issueByChange.get(changeName) ?? null;
260377
+ const issueId = issue2?.id ?? null;
260378
+ if (!issueId) {
260379
+ diag("comment-sync", ` comment-sync: no Linear issue cached for ${changeName}; skipping steering refresh`, "gray");
260380
+ return;
260327
260381
  }
260328
- } catch {}
260329
- await postSteeringAndRefreshTasks({
260330
- apiKey,
260331
- issueId,
260332
- statePath,
260333
- changeDir,
260334
- changeName,
260335
- iteration,
260336
- message,
260337
- log: onLog,
260338
- mutations: commentMutations
260339
- });
260340
- }
260382
+ let iteration = 0;
260383
+ try {
260384
+ const f2 = Bun.file(statePath);
260385
+ if (await f2.exists()) {
260386
+ const json2 = await f2.json();
260387
+ iteration = json2.iteration ?? 0;
260388
+ }
260389
+ } catch {}
260390
+ await postSteeringAndRefreshTasks({
260391
+ apiKey,
260392
+ issueId,
260393
+ statePath,
260394
+ changeDir,
260395
+ changeName,
260396
+ iteration,
260397
+ message,
260398
+ log: onLog,
260399
+ mutations: commentMutations
260400
+ });
260401
+ }
260402
+ } : {}
260341
260403
  };
260342
260404
  }
260343
260405
  var init_comment_sync2 = __esm(() => {
@@ -260697,7 +260759,19 @@ function buildAgentCoordinator(input) {
260697
260759
  pollInterval,
260698
260760
  getWorkerCwd: (changeName) => cwdByChange.get(changeName),
260699
260761
  syncTasksEnabled: commentSync.enabled,
260700
- runBaselineGate: runBaselineGateOnce
260762
+ runBaselineGate: runBaselineGateOnce,
260763
+ getGaveUpTotal: async () => {
260764
+ let total = 0;
260765
+ for (const [changeName, root] of cwdByChange) {
260766
+ const file2 = Bun.file(join33(projectLayout(root).taskStateDir(changeName), GAVEUP_COUNT_FILE));
260767
+ if (!await file2.exists())
260768
+ continue;
260769
+ try {
260770
+ total += Number.parseInt(await file2.text(), 10) || 0;
260771
+ } catch {}
260772
+ }
260773
+ return total;
260774
+ }
260701
260775
  };
260702
260776
  }
260703
260777
  var init_wire = __esm(() => {
@@ -260723,19 +260797,19 @@ var init_wire = __esm(() => {
260723
260797
 
260724
260798
  // apps/agent/src/agent/json-log/json-log-file.ts
260725
260799
  import { mkdir as mkdir12, appendFile as appendFile2 } from "fs/promises";
260726
- import { dirname as dirname14 } from "path";
260800
+ import { dirname as dirname15 } from "path";
260727
260801
  function createJsonLogFileSink(path) {
260728
260802
  if (!path)
260729
260803
  return { emit: () => {} };
260730
260804
  let chain2 = (async () => {
260731
260805
  try {
260732
- await mkdir12(dirname14(path), { recursive: true });
260806
+ await mkdir12(dirname15(path), { recursive: true });
260733
260807
  await Bun.write(path, "");
260734
260808
  } catch {}
260735
260809
  })();
260736
260810
  return {
260737
260811
  emit(event) {
260738
- const line = JSON.stringify({ ts: Date.now(), ...event }) + `
260812
+ const line = JSON.stringify({ ts: Date.now(), v: VERSION, ...event }) + `
260739
260813
  `;
260740
260814
  chain2 = chain2.then(async () => {
260741
260815
  try {
@@ -260745,7 +260819,9 @@ function createJsonLogFileSink(path) {
260745
260819
  }
260746
260820
  };
260747
260821
  }
260748
- var init_json_log_file = () => {};
260822
+ var init_json_log_file = __esm(() => {
260823
+ init_version();
260824
+ });
260749
260825
 
260750
260826
  // apps/agent/src/runtime/shutdown.ts
260751
260827
  async function waitForActiveWorkers(deps) {
@@ -261323,6 +261399,7 @@ function AgentMode({
261323
261399
  const [focusedIdx, setFocusedIdx] = import_react62.useState(0);
261324
261400
  const [showPendingTasks, setShowPendingTasks] = import_react62.useState(false);
261325
261401
  const [showAllSubtasks, setShowAllSubtasks] = import_react62.useState(false);
261402
+ const [gaveUpCount, setGaveUpCount] = import_react62.useState(0);
261326
261403
  const coordRef = import_react62.useRef(null);
261327
261404
  const workerMetaRef = import_react62.useRef(new Map);
261328
261405
  const gatedTicketsRef = import_react62.useRef(new Map);
@@ -261373,7 +261450,7 @@ function AgentMode({
261373
261450
  }, 100);
261374
261451
  return;
261375
261452
  }
261376
- const { coord: coord2, filterDesc, concurrency, pollInterval, runBaselineGate: runBaselineGate2 } = buildCoordinator({
261453
+ const { coord: coord2, filterDesc, concurrency, pollInterval, runBaselineGate: runBaselineGate2, getGaveUpTotal } = buildCoordinator({
261377
261454
  args,
261378
261455
  cfg: cfg2,
261379
261456
  projectRoot,
@@ -261519,6 +261596,12 @@ function AgentMode({
261519
261596
  if (cancelled)
261520
261597
  return;
261521
261598
  fileEmit({ type: "poll_done", found, added, buckets, prStatus });
261599
+ getGaveUpTotal().then((total) => {
261600
+ if (!cancelled)
261601
+ setGaveUpCount(total);
261602
+ }).catch(() => {
261603
+ return;
261604
+ });
261522
261605
  if (added > 0) {
261523
261606
  appendLog(` ${added} new issue${added === 1 ? "" : "s"} queued (found ${found} open)`);
261524
261607
  }
@@ -261830,7 +261913,14 @@ function AgentMode({
261830
261913
  cfg.useWorktree && /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
261831
261914
  color: "green",
261832
261915
  children: " \u25CF worktree"
261833
- }, undefined, false, undefined, this)
261916
+ }, undefined, false, undefined, this),
261917
+ gaveUpCount > 0 && /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
261918
+ color: "red",
261919
+ children: [
261920
+ " \u2502 gave-up \xD7",
261921
+ gaveUpCount
261922
+ ]
261923
+ }, undefined, true, undefined, this)
261834
261924
  ]
261835
261925
  }, undefined, true, undefined, this)
261836
261926
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neriros/ralphy",
3
- "version": "3.10.0",
3
+ "version": "3.10.2",
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",