@neriros/ralphy 2.17.1 → 2.17.3
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/cli/index.js +403 -185
- package/dist/mcp/index.js +131 -44
- package/package.json +18 -16
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.17.
|
|
35033
|
-
return "2.17.
|
|
35032
|
+
if ("2.17.3")
|
|
35033
|
+
return "2.17.3";
|
|
35034
35034
|
} catch {}
|
|
35035
35035
|
const dirsToTry = [];
|
|
35036
35036
|
try {
|
|
@@ -35131,6 +35131,7 @@ async function parseArgs(argv) {
|
|
|
35131
35131
|
createPr: false,
|
|
35132
35132
|
fixCi: false,
|
|
35133
35133
|
maxTickets: 0,
|
|
35134
|
+
projectRoot: undefined,
|
|
35134
35135
|
jsonOutput: false
|
|
35135
35136
|
};
|
|
35136
35137
|
let expectModel = false;
|
|
@@ -35149,6 +35150,7 @@ async function parseArgs(argv) {
|
|
|
35149
35150
|
let expectConcurrency = false;
|
|
35150
35151
|
let expectMaxTickets = false;
|
|
35151
35152
|
let expectIndicator = false;
|
|
35153
|
+
let expectProjectRoot = false;
|
|
35152
35154
|
for (const arg of argv) {
|
|
35153
35155
|
if (expectModel) {
|
|
35154
35156
|
if (VALID_MODELS.has(arg)) {
|
|
@@ -35237,6 +35239,11 @@ async function parseArgs(argv) {
|
|
|
35237
35239
|
expectIndicator = false;
|
|
35238
35240
|
continue;
|
|
35239
35241
|
}
|
|
35242
|
+
if (expectProjectRoot) {
|
|
35243
|
+
result2.projectRoot = arg;
|
|
35244
|
+
expectProjectRoot = false;
|
|
35245
|
+
continue;
|
|
35246
|
+
}
|
|
35240
35247
|
switch (arg) {
|
|
35241
35248
|
case "--claude":
|
|
35242
35249
|
if (result2.engineSet && result2.engine !== "claude") {
|
|
@@ -35325,6 +35332,9 @@ async function parseArgs(argv) {
|
|
|
35325
35332
|
case "--from-agent":
|
|
35326
35333
|
result2.fromAgent = true;
|
|
35327
35334
|
break;
|
|
35335
|
+
case "--project-root":
|
|
35336
|
+
expectProjectRoot = true;
|
|
35337
|
+
break;
|
|
35328
35338
|
default:
|
|
35329
35339
|
if (VALID_MODES.has(arg)) {
|
|
35330
35340
|
result2.mode = arg;
|
|
@@ -39458,6 +39468,99 @@ var init_types2 = __esm(() => {
|
|
|
39458
39468
|
});
|
|
39459
39469
|
});
|
|
39460
39470
|
|
|
39471
|
+
// apps/cli/src/agent/worktree.ts
|
|
39472
|
+
import { basename, join as join3 } from "path";
|
|
39473
|
+
import { homedir } from "os";
|
|
39474
|
+
import { exists } from "fs/promises";
|
|
39475
|
+
function worktreesDir(projectRoot) {
|
|
39476
|
+
return join3(homedir(), ".ralph", basename(projectRoot), "worktrees");
|
|
39477
|
+
}
|
|
39478
|
+
function branchForChange(changeName) {
|
|
39479
|
+
return `ralph/${changeName}`;
|
|
39480
|
+
}
|
|
39481
|
+
async function createWorktree(projectRoot, changeName, runner) {
|
|
39482
|
+
const dir = worktreesDir(projectRoot);
|
|
39483
|
+
const cwd2 = join3(dir, changeName);
|
|
39484
|
+
const branch = branchForChange(changeName);
|
|
39485
|
+
const list = await runner.run(["worktree", "list", "--porcelain"], projectRoot);
|
|
39486
|
+
if (list.stdout.includes(`worktree ${cwd2}
|
|
39487
|
+
`)) {
|
|
39488
|
+
return { cwd: cwd2, branch };
|
|
39489
|
+
}
|
|
39490
|
+
let branchExists = true;
|
|
39491
|
+
try {
|
|
39492
|
+
await runner.run(["rev-parse", "--verify", "--quiet", `refs/heads/${branch}`], projectRoot);
|
|
39493
|
+
} catch {
|
|
39494
|
+
branchExists = false;
|
|
39495
|
+
}
|
|
39496
|
+
const cmd = branchExists ? ["worktree", "add", cwd2, branch] : ["worktree", "add", "-b", branch, cwd2];
|
|
39497
|
+
await runner.run(cmd, projectRoot);
|
|
39498
|
+
return { cwd: cwd2, branch };
|
|
39499
|
+
}
|
|
39500
|
+
async function removeWorktree(projectRoot, cwd2, runner) {
|
|
39501
|
+
await runner.run(["worktree", "remove", "--force", cwd2], projectRoot);
|
|
39502
|
+
}
|
|
39503
|
+
async function isWorktreeSafeToRemove(cwd2, base2, runner) {
|
|
39504
|
+
const status = await runner.run(["status", "--porcelain"], cwd2);
|
|
39505
|
+
const dirty = status.stdout.trim();
|
|
39506
|
+
let unpushedCommits = "";
|
|
39507
|
+
try {
|
|
39508
|
+
const log2 = await runner.run(["log", "--oneline", `${base2}..HEAD`, "--no-merges"], cwd2);
|
|
39509
|
+
unpushedCommits = log2.stdout.trim();
|
|
39510
|
+
} catch {
|
|
39511
|
+
unpushedCommits = "<unknown: failed to compare against base>";
|
|
39512
|
+
}
|
|
39513
|
+
if (dirty && unpushedCommits) {
|
|
39514
|
+
return {
|
|
39515
|
+
safe: false,
|
|
39516
|
+
reason: "uncommitted changes AND unpushed commits present",
|
|
39517
|
+
dirty,
|
|
39518
|
+
unpushedCommits
|
|
39519
|
+
};
|
|
39520
|
+
}
|
|
39521
|
+
if (dirty) {
|
|
39522
|
+
return {
|
|
39523
|
+
safe: false,
|
|
39524
|
+
reason: "uncommitted or untracked files present",
|
|
39525
|
+
dirty,
|
|
39526
|
+
unpushedCommits
|
|
39527
|
+
};
|
|
39528
|
+
}
|
|
39529
|
+
if (unpushedCommits) {
|
|
39530
|
+
return {
|
|
39531
|
+
safe: false,
|
|
39532
|
+
reason: `commits ahead of ${base2} were not pushed/PR'd`,
|
|
39533
|
+
dirty,
|
|
39534
|
+
unpushedCommits
|
|
39535
|
+
};
|
|
39536
|
+
}
|
|
39537
|
+
return { safe: true, dirty, unpushedCommits };
|
|
39538
|
+
}
|
|
39539
|
+
async function seedWorktreeMcpConfig(projectRoot, worktreeCwd) {
|
|
39540
|
+
const dst = join3(worktreeCwd, ".mcp.json");
|
|
39541
|
+
const src = join3(projectRoot, ".mcp.json");
|
|
39542
|
+
const source = await exists(dst) ? dst : await exists(src) ? src : null;
|
|
39543
|
+
if (!source)
|
|
39544
|
+
return;
|
|
39545
|
+
let parsed;
|
|
39546
|
+
try {
|
|
39547
|
+
parsed = await Bun.file(source).json();
|
|
39548
|
+
} catch {
|
|
39549
|
+
return;
|
|
39550
|
+
}
|
|
39551
|
+
const servers = parsed.mcpServers;
|
|
39552
|
+
if (servers && typeof servers === "object") {
|
|
39553
|
+
for (const cfg of Object.values(servers)) {
|
|
39554
|
+
if (Array.isArray(cfg.args)) {
|
|
39555
|
+
cfg.args = cfg.args.map((a) => typeof a === "string" && a.startsWith(".ralph/") ? join3(projectRoot, a) : a);
|
|
39556
|
+
}
|
|
39557
|
+
}
|
|
39558
|
+
}
|
|
39559
|
+
await Bun.write(dst, JSON.stringify(parsed, null, 2) + `
|
|
39560
|
+
`);
|
|
39561
|
+
}
|
|
39562
|
+
var init_worktree = () => {};
|
|
39563
|
+
|
|
39461
39564
|
// node_modules/.bun/react@18.3.1/node_modules/react/cjs/react-jsx-dev-runtime.development.js
|
|
39462
39565
|
var require_react_jsx_dev_runtime_development = __commonJS((exports) => {
|
|
39463
39566
|
var React10 = __toESM(require_react());
|
|
@@ -59323,8 +59426,8 @@ var init_node = __esm(() => {
|
|
|
59323
59426
|
});
|
|
59324
59427
|
|
|
59325
59428
|
// packages/telemetry/src/index.ts
|
|
59326
|
-
import { homedir } from "os";
|
|
59327
|
-
import { join as
|
|
59429
|
+
import { homedir as homedir2 } from "os";
|
|
59430
|
+
import { join as join7 } from "path";
|
|
59328
59431
|
import { randomUUID } from "crypto";
|
|
59329
59432
|
function setDefaultProperties(props) {
|
|
59330
59433
|
defaultProps = { ...defaultProps, ...props };
|
|
@@ -59332,7 +59435,7 @@ function setDefaultProperties(props) {
|
|
|
59332
59435
|
async function init() {
|
|
59333
59436
|
if (!enabled)
|
|
59334
59437
|
return;
|
|
59335
|
-
const idPath =
|
|
59438
|
+
const idPath = join7(homedir2(), ".ralph", ".telemetry-id");
|
|
59336
59439
|
const idFile = Bun.file(idPath);
|
|
59337
59440
|
if (await idFile.exists()) {
|
|
59338
59441
|
distinctId = (await idFile.text()).trim();
|
|
@@ -59407,12 +59510,12 @@ ${fence}`;
|
|
|
59407
59510
|
}
|
|
59408
59511
|
|
|
59409
59512
|
// apps/cli/src/agent/config.ts
|
|
59410
|
-
import { join as
|
|
59513
|
+
import { join as join11 } from "path";
|
|
59411
59514
|
function stripJsonComments(text) {
|
|
59412
59515
|
return text.replace(/\/\/[^\n]*/g, "");
|
|
59413
59516
|
}
|
|
59414
59517
|
async function loadRalphyConfig(projectRoot) {
|
|
59415
|
-
const path =
|
|
59518
|
+
const path = join11(projectRoot, "ralphy.config.json");
|
|
59416
59519
|
const file = Bun.file(path);
|
|
59417
59520
|
if (!await file.exists()) {
|
|
59418
59521
|
return RalphyConfigSchema.parse({});
|
|
@@ -59422,7 +59525,7 @@ async function loadRalphyConfig(projectRoot) {
|
|
|
59422
59525
|
return RalphyConfigSchema.parse(raw);
|
|
59423
59526
|
}
|
|
59424
59527
|
async function ensureRalphyConfig(projectRoot) {
|
|
59425
|
-
const path =
|
|
59528
|
+
const path = join11(projectRoot, "ralphy.config.json");
|
|
59426
59529
|
const file = Bun.file(path);
|
|
59427
59530
|
if (await file.exists())
|
|
59428
59531
|
return path;
|
|
@@ -59624,8 +59727,8 @@ var init_config = __esm(() => {
|
|
|
59624
59727
|
|
|
59625
59728
|
// packages/log/src/log.ts
|
|
59626
59729
|
import { appendFile } from "fs/promises";
|
|
59627
|
-
import { join as
|
|
59628
|
-
import { homedir as
|
|
59730
|
+
import { join as join12, dirname as dirname4 } from "path";
|
|
59731
|
+
import { homedir as homedir3 } from "os";
|
|
59629
59732
|
import { mkdir as mkdir2 } from "fs/promises";
|
|
59630
59733
|
function fmt(type, text) {
|
|
59631
59734
|
return `[${new Date().toISOString()}] [${type}] ${text}
|
|
@@ -59670,25 +59773,25 @@ async function initWorkerLog(logFile) {
|
|
|
59670
59773
|
var ANSI_RE, AGENT_LOG_PATH;
|
|
59671
59774
|
var init_log = __esm(() => {
|
|
59672
59775
|
ANSI_RE = /\x1b(?:\[[0-9;]*[A-Za-z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|.)/g;
|
|
59673
|
-
AGENT_LOG_PATH =
|
|
59776
|
+
AGENT_LOG_PATH = join12(homedir3(), ".ralph", "agent-mode.log");
|
|
59674
59777
|
mkdir2(dirname4(AGENT_LOG_PATH), { recursive: true }).catch(() => {
|
|
59675
59778
|
return;
|
|
59676
59779
|
});
|
|
59677
59780
|
});
|
|
59678
59781
|
|
|
59679
59782
|
// packages/core/src/layout.ts
|
|
59680
|
-
import { join as
|
|
59783
|
+
import { join as join13 } from "path";
|
|
59681
59784
|
function projectLayout(root) {
|
|
59682
|
-
const statesDir =
|
|
59683
|
-
const tasksDir =
|
|
59785
|
+
const statesDir = join13(root, ".ralph", "tasks");
|
|
59786
|
+
const tasksDir = join13(root, "openspec", "changes");
|
|
59684
59787
|
return {
|
|
59685
59788
|
root,
|
|
59686
59789
|
statesDir,
|
|
59687
59790
|
tasksDir,
|
|
59688
|
-
agentStateFile:
|
|
59689
|
-
changeDir: (name) =>
|
|
59690
|
-
taskStateDir: (name) =>
|
|
59691
|
-
stateFile: (name) =>
|
|
59791
|
+
agentStateFile: join13(root, ".ralph", "agent-state.json"),
|
|
59792
|
+
changeDir: (name) => join13(tasksDir, name),
|
|
59793
|
+
taskStateDir: (name) => join13(statesDir, name),
|
|
59794
|
+
stateFile: (name) => join13(statesDir, name, STATE_FILE2)
|
|
59692
59795
|
};
|
|
59693
59796
|
}
|
|
59694
59797
|
var STATE_FILE2 = ".ralph-state.json";
|
|
@@ -59743,7 +59846,15 @@ function buildIssueFilter(spec) {
|
|
|
59743
59846
|
where.and = [{ state: current }, noStatus];
|
|
59744
59847
|
}
|
|
59745
59848
|
if (labels.length > 0) {
|
|
59746
|
-
|
|
59849
|
+
const includeLabels = where.labels;
|
|
59850
|
+
const excludeLabels = { every: { name: { nin: labels } } };
|
|
59851
|
+
if (includeLabels === undefined) {
|
|
59852
|
+
where.labels = excludeLabels;
|
|
59853
|
+
} else {
|
|
59854
|
+
const existingAnd = where.and ?? [];
|
|
59855
|
+
where.and = [...existingAnd, { labels: includeLabels }, { labels: excludeLabels }];
|
|
59856
|
+
delete where.labels;
|
|
59857
|
+
}
|
|
59747
59858
|
}
|
|
59748
59859
|
}
|
|
59749
59860
|
return where;
|
|
@@ -60298,7 +60409,7 @@ var init_coordinator = __esm(() => {
|
|
|
60298
60409
|
});
|
|
60299
60410
|
|
|
60300
60411
|
// apps/cli/src/agent/scaffold.ts
|
|
60301
|
-
import { join as
|
|
60412
|
+
import { join as join14 } from "path";
|
|
60302
60413
|
import { mkdir as mkdir3 } from "fs/promises";
|
|
60303
60414
|
function changeNameForIssue(issue) {
|
|
60304
60415
|
const slug = issue.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
|
|
@@ -60306,10 +60417,10 @@ function changeNameForIssue(issue) {
|
|
|
60306
60417
|
}
|
|
60307
60418
|
async function scaffoldChangeForIssue(tasksDir, statesDir, issue, comments = [], appendPrompt = "") {
|
|
60308
60419
|
const name = changeNameForIssue(issue);
|
|
60309
|
-
const changeDir =
|
|
60310
|
-
const stateDir =
|
|
60420
|
+
const changeDir = join14(tasksDir, name);
|
|
60421
|
+
const stateDir = join14(statesDir, name);
|
|
60311
60422
|
await mkdir3(changeDir, { recursive: true });
|
|
60312
|
-
await mkdir3(
|
|
60423
|
+
await mkdir3(join14(changeDir, "specs"), { recursive: true });
|
|
60313
60424
|
await mkdir3(stateDir, { recursive: true });
|
|
60314
60425
|
const commentsBlock = comments.length > 0 ? [
|
|
60315
60426
|
"",
|
|
@@ -60361,106 +60472,13 @@ async function scaffoldChangeForIssue(tasksDir, statesDir, issue, comments = [],
|
|
|
60361
60472
|
""
|
|
60362
60473
|
].join(`
|
|
60363
60474
|
`);
|
|
60364
|
-
await Bun.write(
|
|
60365
|
-
await Bun.write(
|
|
60366
|
-
await Bun.write(
|
|
60475
|
+
await Bun.write(join14(changeDir, "proposal.md"), proposal);
|
|
60476
|
+
await Bun.write(join14(changeDir, "tasks.md"), tasks);
|
|
60477
|
+
await Bun.write(join14(changeDir, "design.md"), design);
|
|
60367
60478
|
return name;
|
|
60368
60479
|
}
|
|
60369
60480
|
var init_scaffold = () => {};
|
|
60370
60481
|
|
|
60371
|
-
// apps/cli/src/agent/worktree.ts
|
|
60372
|
-
import { basename, join as join14 } from "path";
|
|
60373
|
-
import { homedir as homedir3 } from "os";
|
|
60374
|
-
import { exists } from "fs/promises";
|
|
60375
|
-
function worktreesDir(projectRoot) {
|
|
60376
|
-
return join14(homedir3(), ".ralph", basename(projectRoot), "worktrees");
|
|
60377
|
-
}
|
|
60378
|
-
function branchForChange(changeName) {
|
|
60379
|
-
return `ralph/${changeName}`;
|
|
60380
|
-
}
|
|
60381
|
-
async function createWorktree(projectRoot, changeName, runner) {
|
|
60382
|
-
const dir = worktreesDir(projectRoot);
|
|
60383
|
-
const cwd2 = join14(dir, changeName);
|
|
60384
|
-
const branch = branchForChange(changeName);
|
|
60385
|
-
const list = await runner.run(["worktree", "list", "--porcelain"], projectRoot);
|
|
60386
|
-
if (list.stdout.includes(`worktree ${cwd2}
|
|
60387
|
-
`)) {
|
|
60388
|
-
return { cwd: cwd2, branch };
|
|
60389
|
-
}
|
|
60390
|
-
let branchExists = true;
|
|
60391
|
-
try {
|
|
60392
|
-
await runner.run(["rev-parse", "--verify", "--quiet", `refs/heads/${branch}`], projectRoot);
|
|
60393
|
-
} catch {
|
|
60394
|
-
branchExists = false;
|
|
60395
|
-
}
|
|
60396
|
-
const cmd = branchExists ? ["worktree", "add", cwd2, branch] : ["worktree", "add", "-b", branch, cwd2];
|
|
60397
|
-
await runner.run(cmd, projectRoot);
|
|
60398
|
-
return { cwd: cwd2, branch };
|
|
60399
|
-
}
|
|
60400
|
-
async function removeWorktree(projectRoot, cwd2, runner) {
|
|
60401
|
-
await runner.run(["worktree", "remove", "--force", cwd2], projectRoot);
|
|
60402
|
-
}
|
|
60403
|
-
async function isWorktreeSafeToRemove(cwd2, base2, runner) {
|
|
60404
|
-
const status = await runner.run(["status", "--porcelain"], cwd2);
|
|
60405
|
-
const dirty = status.stdout.trim();
|
|
60406
|
-
let unpushedCommits = "";
|
|
60407
|
-
try {
|
|
60408
|
-
const log2 = await runner.run(["log", "--oneline", `${base2}..HEAD`, "--no-merges"], cwd2);
|
|
60409
|
-
unpushedCommits = log2.stdout.trim();
|
|
60410
|
-
} catch {
|
|
60411
|
-
unpushedCommits = "<unknown: failed to compare against base>";
|
|
60412
|
-
}
|
|
60413
|
-
if (dirty && unpushedCommits) {
|
|
60414
|
-
return {
|
|
60415
|
-
safe: false,
|
|
60416
|
-
reason: "uncommitted changes AND unpushed commits present",
|
|
60417
|
-
dirty,
|
|
60418
|
-
unpushedCommits
|
|
60419
|
-
};
|
|
60420
|
-
}
|
|
60421
|
-
if (dirty) {
|
|
60422
|
-
return {
|
|
60423
|
-
safe: false,
|
|
60424
|
-
reason: "uncommitted or untracked files present",
|
|
60425
|
-
dirty,
|
|
60426
|
-
unpushedCommits
|
|
60427
|
-
};
|
|
60428
|
-
}
|
|
60429
|
-
if (unpushedCommits) {
|
|
60430
|
-
return {
|
|
60431
|
-
safe: false,
|
|
60432
|
-
reason: `commits ahead of ${base2} were not pushed/PR'd`,
|
|
60433
|
-
dirty,
|
|
60434
|
-
unpushedCommits
|
|
60435
|
-
};
|
|
60436
|
-
}
|
|
60437
|
-
return { safe: true, dirty, unpushedCommits };
|
|
60438
|
-
}
|
|
60439
|
-
async function seedWorktreeMcpConfig(projectRoot, worktreeCwd) {
|
|
60440
|
-
const dst = join14(worktreeCwd, ".mcp.json");
|
|
60441
|
-
const src = join14(projectRoot, ".mcp.json");
|
|
60442
|
-
const source = await exists(dst) ? dst : await exists(src) ? src : null;
|
|
60443
|
-
if (!source)
|
|
60444
|
-
return;
|
|
60445
|
-
let parsed;
|
|
60446
|
-
try {
|
|
60447
|
-
parsed = await Bun.file(source).json();
|
|
60448
|
-
} catch {
|
|
60449
|
-
return;
|
|
60450
|
-
}
|
|
60451
|
-
const servers = parsed.mcpServers;
|
|
60452
|
-
if (servers && typeof servers === "object") {
|
|
60453
|
-
for (const cfg of Object.values(servers)) {
|
|
60454
|
-
if (Array.isArray(cfg.args)) {
|
|
60455
|
-
cfg.args = cfg.args.map((a) => typeof a === "string" && a.startsWith(".ralph/") ? join14(projectRoot, a) : a);
|
|
60456
|
-
}
|
|
60457
|
-
}
|
|
60458
|
-
}
|
|
60459
|
-
await Bun.write(dst, JSON.stringify(parsed, null, 2) + `
|
|
60460
|
-
`);
|
|
60461
|
-
}
|
|
60462
|
-
var init_worktree = () => {};
|
|
60463
|
-
|
|
60464
60482
|
// apps/cli/src/agent/pr.ts
|
|
60465
60483
|
function defaultTitle(issue) {
|
|
60466
60484
|
return `${issue.identifier}: ${issue.title}`;
|
|
@@ -66953,7 +66971,8 @@ function ensureState(changeDir) {
|
|
|
66953
66971
|
|
|
66954
66972
|
// apps/cli/src/components/TaskList.tsx
|
|
66955
66973
|
var import_react22 = __toESM(require_react(), 1);
|
|
66956
|
-
import { join as
|
|
66974
|
+
import { join as join4 } from "path";
|
|
66975
|
+
init_worktree();
|
|
66957
66976
|
var jsx_dev_runtime = __toESM(require_jsx_dev_runtime(), 1);
|
|
66958
66977
|
function countTaskItems(content) {
|
|
66959
66978
|
const checked = (content.match(/^- \[x\]/gm) ?? []).length;
|
|
@@ -66966,10 +66985,10 @@ function buildRows(statesDir, projectRoot) {
|
|
|
66966
66985
|
const seenNames = new Set;
|
|
66967
66986
|
const sources = [{ dir: statesDir, label: "main" }];
|
|
66968
66987
|
if (projectRoot) {
|
|
66969
|
-
const worktreesRoot =
|
|
66988
|
+
const worktreesRoot = worktreesDir(projectRoot);
|
|
66970
66989
|
for (const wt of storage.list(worktreesRoot)) {
|
|
66971
66990
|
sources.push({
|
|
66972
|
-
dir:
|
|
66991
|
+
dir: join4(worktreesRoot, wt, ".ralph", "tasks"),
|
|
66973
66992
|
label: `wt:${wt}`
|
|
66974
66993
|
});
|
|
66975
66994
|
}
|
|
@@ -66978,7 +66997,7 @@ function buildRows(statesDir, projectRoot) {
|
|
|
66978
66997
|
for (const entry of storage.list(dir)) {
|
|
66979
66998
|
if (seenNames.has(entry))
|
|
66980
66999
|
continue;
|
|
66981
|
-
const raw = storage.read(
|
|
67000
|
+
const raw = storage.read(join4(dir, entry, ".ralph-state.json"));
|
|
66982
67001
|
if (raw === null)
|
|
66983
67002
|
continue;
|
|
66984
67003
|
let state;
|
|
@@ -66994,7 +67013,7 @@ function buildRows(statesDir, projectRoot) {
|
|
|
66994
67013
|
`).find((l) => l.trim() !== "") ?? "";
|
|
66995
67014
|
let progress = "\u2014";
|
|
66996
67015
|
let progressStyled = true;
|
|
66997
|
-
const tasksContent = storage.read(
|
|
67016
|
+
const tasksContent = storage.read(join4(dir, entry, "tasks.md"));
|
|
66998
67017
|
if (tasksContent !== null) {
|
|
66999
67018
|
const { checked, unchecked } = countTaskItems(tasksContent);
|
|
67000
67019
|
const total = checked + unchecked;
|
|
@@ -67135,7 +67154,7 @@ function TaskList({ statesDir, projectRoot }) {
|
|
|
67135
67154
|
}
|
|
67136
67155
|
|
|
67137
67156
|
// apps/cli/src/components/TaskStatus.tsx
|
|
67138
|
-
import { join as
|
|
67157
|
+
import { join as join5 } from "path";
|
|
67139
67158
|
var jsx_dev_runtime2 = __toESM(require_jsx_dev_runtime(), 1);
|
|
67140
67159
|
var HEAVY_RULE = "============================================";
|
|
67141
67160
|
var LIGHT_RULE = "--------------------------------------------";
|
|
@@ -67146,7 +67165,7 @@ function TaskStatus({ state, stateDir }) {
|
|
|
67146
67165
|
const time = Math.round(state.usage.total_duration_ms / 1000 * 10) / 10 + "s";
|
|
67147
67166
|
const artifacts = OPENSPEC_ARTIFACTS.map((name) => ({
|
|
67148
67167
|
name,
|
|
67149
|
-
exists: storage.read(
|
|
67168
|
+
exists: storage.read(join5(stateDir, name)) !== null
|
|
67150
67169
|
}));
|
|
67151
67170
|
const recent = state.history.slice(-10);
|
|
67152
67171
|
return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
|
|
@@ -67289,7 +67308,7 @@ function TaskStatus({ state, stateDir }) {
|
|
|
67289
67308
|
|
|
67290
67309
|
// apps/cli/src/components/TaskLoop.tsx
|
|
67291
67310
|
var import_react56 = __toESM(require_react(), 1);
|
|
67292
|
-
import { join as
|
|
67311
|
+
import { join as join10 } from "path";
|
|
67293
67312
|
|
|
67294
67313
|
// node_modules/.bun/@inkjs+ui@2.0.0+5b84dde3d6cd3930/node_modules/@inkjs/ui/build/components/badge/badge.js
|
|
67295
67314
|
var import_react24 = __toESM(require_react(), 1);
|
|
@@ -70853,7 +70872,7 @@ function StopMessage({
|
|
|
70853
70872
|
|
|
70854
70873
|
// apps/cli/src/hooks/useLoop.ts
|
|
70855
70874
|
var import_react55 = __toESM(require_react(), 1);
|
|
70856
|
-
import { join as
|
|
70875
|
+
import { join as join9 } from "path";
|
|
70857
70876
|
|
|
70858
70877
|
// packages/engine/src/spawn.ts
|
|
70859
70878
|
var {spawn: bunSpawn } = globalThis.Bun;
|
|
@@ -70863,7 +70882,7 @@ var spawn = bunSpawn;
|
|
|
70863
70882
|
import { createWriteStream } from "fs";
|
|
70864
70883
|
import { mkdtemp, unlink, mkdir } from "fs/promises";
|
|
70865
70884
|
import { dirname as dirname2 } from "path";
|
|
70866
|
-
import { join as
|
|
70885
|
+
import { join as join6 } from "path";
|
|
70867
70886
|
import { tmpdir } from "os";
|
|
70868
70887
|
|
|
70869
70888
|
// packages/engine/src/feed-events.ts
|
|
@@ -71596,7 +71615,7 @@ function buildCodexArgs() {
|
|
|
71596
71615
|
return ["exec", "--json", "--color", "never", "--dangerously-bypass-approvals-and-sandbox", "-"];
|
|
71597
71616
|
}
|
|
71598
71617
|
async function runInteractive(model, prompt, taskDir) {
|
|
71599
|
-
const promptFile = taskDir ?
|
|
71618
|
+
const promptFile = taskDir ? join6(taskDir, "_interactive_prompt.md") : join6(await mkdtemp(join6(tmpdir(), "ralph-")), "prompt.md");
|
|
71600
71619
|
await Bun.write(promptFile, prompt);
|
|
71601
71620
|
try {
|
|
71602
71621
|
const cmd = [
|
|
@@ -71622,7 +71641,7 @@ async function runInteractive(model, prompt, taskDir) {
|
|
|
71622
71641
|
stderr: "inherit"
|
|
71623
71642
|
});
|
|
71624
71643
|
const exitCode = await proc.exited;
|
|
71625
|
-
const doneFile = taskDir ?
|
|
71644
|
+
const doneFile = taskDir ? join6(taskDir, "_interactive_done") : null;
|
|
71626
71645
|
if (doneFile && await Bun.file(doneFile).exists()) {
|
|
71627
71646
|
return { exitCode: 0, usage: null, sessionId: null, rateLimited: false };
|
|
71628
71647
|
}
|
|
@@ -71839,12 +71858,12 @@ function commitTaskDir(taskDir, message) {
|
|
|
71839
71858
|
init_src();
|
|
71840
71859
|
|
|
71841
71860
|
// packages/core/src/loop.ts
|
|
71842
|
-
import { join as
|
|
71861
|
+
import { join as join8 } from "path";
|
|
71843
71862
|
var STEERING_MAX_LINES = 20;
|
|
71844
71863
|
function buildTaskPrompt(state, taskDir) {
|
|
71845
71864
|
const storage = getStorage();
|
|
71846
71865
|
let prompt = "";
|
|
71847
|
-
const steeringContent = storage.read(
|
|
71866
|
+
const steeringContent = storage.read(join8(taskDir, "steering.md"));
|
|
71848
71867
|
if (steeringContent !== null) {
|
|
71849
71868
|
const steeringLines = steeringContent.split(`
|
|
71850
71869
|
`).filter((line) => !line.startsWith("#")).filter((line) => line.trim()).slice(0, STEERING_MAX_LINES);
|
|
@@ -71863,7 +71882,7 @@ function buildTaskPrompt(state, taskDir) {
|
|
|
71863
71882
|
`;
|
|
71864
71883
|
}
|
|
71865
71884
|
}
|
|
71866
|
-
const tasksContent = storage.read(
|
|
71885
|
+
const tasksContent = storage.read(join8(taskDir, "tasks.md"));
|
|
71867
71886
|
if (tasksContent !== null) {
|
|
71868
71887
|
const section = firstUnchecked(tasksContent);
|
|
71869
71888
|
if (section) {
|
|
@@ -71878,7 +71897,7 @@ function buildTaskPrompt(state, taskDir) {
|
|
|
71878
71897
|
prompt += `---
|
|
71879
71898
|
|
|
71880
71899
|
`;
|
|
71881
|
-
prompt += `**Tracking progress**: as you finish each item above, edit ` + `\`${
|
|
71900
|
+
prompt += `**Tracking progress**: as you finish each item above, edit ` + `\`${join8(taskDir, "tasks.md")}\` and change its \`- [ ]\` to ` + `\`- [x]\` in the same commit. The loop reads this file between ` + `iterations and stops when no \`- [ ]\` items remain \u2014 if you do ` + `not tick the box, the next iteration will repeat this task.
|
|
71882
71901
|
|
|
71883
71902
|
`;
|
|
71884
71903
|
}
|
|
@@ -71899,7 +71918,7 @@ function buildTaskPrompt(state, taskDir) {
|
|
|
71899
71918
|
`;
|
|
71900
71919
|
}
|
|
71901
71920
|
if (state.manualTest) {
|
|
71902
|
-
const tasksContent2 = storage.read(
|
|
71921
|
+
const tasksContent2 = storage.read(join8(taskDir, "tasks.md"));
|
|
71903
71922
|
const hasUncheckedTasks = tasksContent2 !== null && /^- \[ \]/m.test(tasksContent2);
|
|
71904
71923
|
if (!hasUncheckedTasks) {
|
|
71905
71924
|
const hasManualTestSection = tasksContent2 !== null && /^## Manual Testing/m.test(tasksContent2);
|
|
@@ -71948,7 +71967,7 @@ When all tasks are complete and all files are committed, push your branch and op
|
|
|
71948
71967
|
}
|
|
71949
71968
|
function checkStopSignal(taskDir, stateDir) {
|
|
71950
71969
|
const storage = getStorage();
|
|
71951
|
-
const stopFile =
|
|
71970
|
+
const stopFile = join8(taskDir, "STOP");
|
|
71952
71971
|
const reason = storage.read(stopFile);
|
|
71953
71972
|
if (reason === null)
|
|
71954
71973
|
return null;
|
|
@@ -72023,7 +72042,7 @@ function updateStateIteration(stateDir, result2, startedAt, engine, model, usage
|
|
|
72023
72042
|
}
|
|
72024
72043
|
function appendSteeringMessage(taskDir, message) {
|
|
72025
72044
|
const storage = getStorage();
|
|
72026
|
-
const steeringPath =
|
|
72045
|
+
const steeringPath = join8(taskDir, "steering.md");
|
|
72027
72046
|
const existing = storage.read(steeringPath);
|
|
72028
72047
|
const updated = existing ? `${message}
|
|
72029
72048
|
|
|
@@ -72090,11 +72109,11 @@ function useLoop(opts) {
|
|
|
72090
72109
|
setLogLines((prev) => [...prev, { id: nextId(), kind: "feed", event }]);
|
|
72091
72110
|
};
|
|
72092
72111
|
runWithContext(createDefaultContext(), async () => {
|
|
72093
|
-
const stateDir =
|
|
72094
|
-
const tasksDir =
|
|
72112
|
+
const stateDir = join9(opts.statesDir, opts.name);
|
|
72113
|
+
const tasksDir = join9(opts.tasksDir, opts.name);
|
|
72095
72114
|
const storage = getStorage();
|
|
72096
72115
|
let currentState;
|
|
72097
|
-
const existingStateRaw = storage.read(
|
|
72116
|
+
const existingStateRaw = storage.read(join9(stateDir, ".ralph-state.json"));
|
|
72098
72117
|
if (existingStateRaw !== null) {
|
|
72099
72118
|
currentState = readState(stateDir);
|
|
72100
72119
|
if (currentState.engine !== opts.engine || currentState.model !== opts.model) {
|
|
@@ -72141,7 +72160,7 @@ function useLoop(opts) {
|
|
|
72141
72160
|
setStopReason(stop);
|
|
72142
72161
|
break;
|
|
72143
72162
|
}
|
|
72144
|
-
const tasksContent = storage.read(
|
|
72163
|
+
const tasksContent = storage.read(join9(tasksDir, "tasks.md"));
|
|
72145
72164
|
if (tasksContent !== null) {
|
|
72146
72165
|
const remaining = countUnchecked(tasksContent);
|
|
72147
72166
|
addInfo(`tasks.md: ${remaining} unchecked item${remaining === 1 ? "" : "s"} remaining`);
|
|
@@ -72181,7 +72200,7 @@ function useLoop(opts) {
|
|
|
72181
72200
|
model: opts.model,
|
|
72182
72201
|
prompt,
|
|
72183
72202
|
logFlag: opts.log,
|
|
72184
|
-
logFile:
|
|
72203
|
+
logFile: join9(stateDir, "log.json"),
|
|
72185
72204
|
taskDir: tasksDir,
|
|
72186
72205
|
interactive: false,
|
|
72187
72206
|
onFeedEvent: addFeedEvent,
|
|
@@ -72204,7 +72223,7 @@ function useLoop(opts) {
|
|
|
72204
72223
|
model: opts.model,
|
|
72205
72224
|
prompt: buildSteeringPrompt(steerMessage),
|
|
72206
72225
|
logFlag: opts.log,
|
|
72207
|
-
logFile:
|
|
72226
|
+
logFile: join9(stateDir, "log.json"),
|
|
72208
72227
|
taskDir: tasksDir,
|
|
72209
72228
|
onFeedEvent: addResumeFeedEvent,
|
|
72210
72229
|
signal: resumeController.signal,
|
|
@@ -72399,7 +72418,7 @@ function TaskLoop({ opts }) {
|
|
|
72399
72418
|
}, [loop.isRunning, exit]);
|
|
72400
72419
|
if (!loop.state)
|
|
72401
72420
|
return null;
|
|
72402
|
-
const stateDir =
|
|
72421
|
+
const stateDir = join10(opts.statesDir, opts.name);
|
|
72403
72422
|
return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
|
|
72404
72423
|
flexDirection: "column",
|
|
72405
72424
|
children: [
|
|
@@ -73753,8 +73772,11 @@ init_worktree();
|
|
|
73753
73772
|
// apps/cli/src/debug.ts
|
|
73754
73773
|
init_log();
|
|
73755
73774
|
import { join as join20 } from "path";
|
|
73775
|
+
function fmtTs(d) {
|
|
73776
|
+
return d.toISOString().replace("T", " ").slice(0, 23);
|
|
73777
|
+
}
|
|
73756
73778
|
var LOG_LINE_RE = /^\[(.+?)\] \[(.+?)\] (.+)$/;
|
|
73757
|
-
function
|
|
73779
|
+
function parseTextLog(content) {
|
|
73758
73780
|
return content.split(`
|
|
73759
73781
|
`).filter(Boolean).flatMap((line) => {
|
|
73760
73782
|
const m = LOG_LINE_RE.exec(line);
|
|
@@ -73766,23 +73788,154 @@ function parseLog(content) {
|
|
|
73766
73788
|
return [{ ts, type: m[2], text: m[3] }];
|
|
73767
73789
|
});
|
|
73768
73790
|
}
|
|
73769
|
-
function
|
|
73770
|
-
return
|
|
73791
|
+
function parseJsonlLog(content, filterChangeName) {
|
|
73792
|
+
return content.split(`
|
|
73793
|
+
`).filter(Boolean).flatMap((line) => {
|
|
73794
|
+
let entry;
|
|
73795
|
+
try {
|
|
73796
|
+
entry = JSON.parse(line);
|
|
73797
|
+
} catch {
|
|
73798
|
+
return [];
|
|
73799
|
+
}
|
|
73800
|
+
const ts = new Date(entry.ts);
|
|
73801
|
+
if (isNaN(ts.getTime()))
|
|
73802
|
+
return [];
|
|
73803
|
+
if (filterChangeName && entry.changeName && entry.changeName !== filterChangeName) {
|
|
73804
|
+
return [];
|
|
73805
|
+
}
|
|
73806
|
+
const cn = entry.changeName ?? "";
|
|
73807
|
+
switch (entry.type) {
|
|
73808
|
+
case "started":
|
|
73809
|
+
return [{ ts, type: "agent", text: `agent started v${entry.version ?? "?"}` }];
|
|
73810
|
+
case "stopped":
|
|
73811
|
+
return [{ ts, type: "agent", text: "agent stopped" }];
|
|
73812
|
+
case "worker_started":
|
|
73813
|
+
return [{ ts, type: "spawn", text: `${cn}: worker spawned` }];
|
|
73814
|
+
case "worker_phase": {
|
|
73815
|
+
const detail = entry.detail ? ` (${entry.detail})` : "";
|
|
73816
|
+
return [{ ts, type: "phase", text: `${cn}: ${entry.phase}${detail}` }];
|
|
73817
|
+
}
|
|
73818
|
+
case "worker_cmd_start":
|
|
73819
|
+
return [
|
|
73820
|
+
{
|
|
73821
|
+
ts,
|
|
73822
|
+
type: "cmd",
|
|
73823
|
+
text: `${cn}: \u2192 ${(entry.cmd ?? []).slice(0, 4).join(" ")}`
|
|
73824
|
+
}
|
|
73825
|
+
];
|
|
73826
|
+
case "worker_cmd_end":
|
|
73827
|
+
return [
|
|
73828
|
+
{
|
|
73829
|
+
ts,
|
|
73830
|
+
type: "cmd",
|
|
73831
|
+
text: `${cn}: \u2190 ${(entry.cmd ?? []).slice(0, 2).join(" ")} (${entry.durationMs}ms, ${entry.ok ? "ok" : "err"})`
|
|
73832
|
+
}
|
|
73833
|
+
];
|
|
73834
|
+
case "worker_pr":
|
|
73835
|
+
return [{ ts, type: "pr", text: `${cn}: PR \u2192 ${entry.prUrl}` }];
|
|
73836
|
+
case "worker_exited":
|
|
73837
|
+
return [
|
|
73838
|
+
{
|
|
73839
|
+
ts,
|
|
73840
|
+
type: "exit",
|
|
73841
|
+
text: `${cn}: exited (code ${entry.exitCode ?? "?"})`
|
|
73842
|
+
}
|
|
73843
|
+
];
|
|
73844
|
+
case "log":
|
|
73845
|
+
return [{ ts, type: "coord", text: entry.text ?? "" }];
|
|
73846
|
+
case "poll_done":
|
|
73847
|
+
return [
|
|
73848
|
+
{
|
|
73849
|
+
ts,
|
|
73850
|
+
type: "poll",
|
|
73851
|
+
text: `poll: found=${entry.found} added=${entry.added}`
|
|
73852
|
+
}
|
|
73853
|
+
];
|
|
73854
|
+
default:
|
|
73855
|
+
return [];
|
|
73856
|
+
}
|
|
73857
|
+
});
|
|
73858
|
+
}
|
|
73859
|
+
function detectStuck(lines) {
|
|
73860
|
+
if (lines.length < 10)
|
|
73861
|
+
return null;
|
|
73862
|
+
const recent = lines.slice(-20).filter((l) => l.type === "phase");
|
|
73863
|
+
if (recent.length < 5)
|
|
73864
|
+
return null;
|
|
73865
|
+
const phaseNames = recent.map((l) => l.text.split(": ").slice(1).join(": "));
|
|
73866
|
+
const unique = new Set(phaseNames);
|
|
73867
|
+
if (unique.size !== 1)
|
|
73868
|
+
return null;
|
|
73869
|
+
const phase = phaseNames[0];
|
|
73870
|
+
const all = lines.filter((l) => l.type === "phase" && l.text.includes(phase));
|
|
73871
|
+
const first = all[0];
|
|
73872
|
+
const last2 = all[all.length - 1];
|
|
73873
|
+
const minutesStuck = (last2.ts.getTime() - first.ts.getTime()) / 60000;
|
|
73874
|
+
const prEntry = lines.find((l) => l.type === "pr");
|
|
73875
|
+
const cmdEntry = lines.find((l) => l.type === "cmd" && l.text.includes("gh") && l.text.includes("mergeable"));
|
|
73876
|
+
const watchingPrUrl = prEntry?.text.split("PR \u2192 ")[1] ?? cmdEntry?.text.match(/https:\/\/github\.com\/[^\s)]+/)?.[0];
|
|
73877
|
+
return {
|
|
73878
|
+
phase,
|
|
73879
|
+
count: all.length,
|
|
73880
|
+
firstSeen: first.ts,
|
|
73881
|
+
lastSeen: last2.ts,
|
|
73882
|
+
minutesStuck,
|
|
73883
|
+
watchingPrUrl
|
|
73884
|
+
};
|
|
73885
|
+
}
|
|
73886
|
+
async function inspectBinary(projectRoot) {
|
|
73887
|
+
const binPath = join20(projectRoot, ".ralph", "bin", "cli.js");
|
|
73888
|
+
const file = Bun.file(binPath);
|
|
73889
|
+
if (!await file.exists())
|
|
73890
|
+
return null;
|
|
73891
|
+
let embeddedVersion;
|
|
73892
|
+
try {
|
|
73893
|
+
const slice2 = await file.slice(0, 50000).text();
|
|
73894
|
+
const m = /"(\d+\.\d+\.\d+)"/.exec(slice2);
|
|
73895
|
+
if (m)
|
|
73896
|
+
embeddedVersion = m[1];
|
|
73897
|
+
} catch {}
|
|
73898
|
+
let builtAt;
|
|
73899
|
+
try {
|
|
73900
|
+
const r = Bun.spawnSync(["stat", "-f", "%Sm", "-t", "%Y-%m-%dT%H:%M:%S", binPath], {
|
|
73901
|
+
stderr: "ignore"
|
|
73902
|
+
});
|
|
73903
|
+
const s = r.stdout.toString().trim();
|
|
73904
|
+
if (s)
|
|
73905
|
+
builtAt = new Date(s);
|
|
73906
|
+
} catch {}
|
|
73907
|
+
return { path: binPath, embeddedVersion, builtAt };
|
|
73771
73908
|
}
|
|
73772
73909
|
var SPAWN_RE = /\u25B6 (\S+) \u2192 (\S+)/;
|
|
73773
|
-
async function resolveDebugTarget(opts) {
|
|
73910
|
+
async function resolveDebugTarget(projectRoot, opts) {
|
|
73774
73911
|
const agentLogFile = Bun.file(AGENT_LOG_PATH);
|
|
73775
|
-
const
|
|
73912
|
+
const textLines = await agentLogFile.exists() ? parseTextLog(await agentLogFile.text()) : [];
|
|
73913
|
+
const jsonlLogFile = Bun.file(join20(projectRoot, ".ralph", "agent.log"));
|
|
73914
|
+
const jsonlLines = await jsonlLogFile.exists() ? parseJsonlLog(await jsonlLogFile.text()) : [];
|
|
73915
|
+
const allLines = [...textLines, ...jsonlLines];
|
|
73776
73916
|
if (opts.name && !opts.issue) {
|
|
73777
|
-
for (const line of
|
|
73917
|
+
for (const line of allLines) {
|
|
73778
73918
|
const m = SPAWN_RE.exec(line.text);
|
|
73779
73919
|
if (m && m[2] === opts.name)
|
|
73780
73920
|
return { changeName: opts.name, identifier: m[1] };
|
|
73781
73921
|
}
|
|
73922
|
+
for (const line of allLines) {
|
|
73923
|
+
if (line.text.includes(opts.name) && line.text.includes("COD-")) {
|
|
73924
|
+
const id = /COD-\d+/.exec(line.text)?.[0];
|
|
73925
|
+
if (id)
|
|
73926
|
+
return { changeName: opts.name, identifier: id };
|
|
73927
|
+
}
|
|
73928
|
+
}
|
|
73782
73929
|
return { changeName: opts.name, identifier: undefined };
|
|
73783
73930
|
}
|
|
73784
73931
|
if (opts.issue && !opts.name) {
|
|
73785
|
-
|
|
73932
|
+
const pattern = new RegExp(`(cod-${opts.issue.toLowerCase().replace("cod-", "")}[\\w-]+)`);
|
|
73933
|
+
for (const line of allLines) {
|
|
73934
|
+
const m = pattern.exec(line.text);
|
|
73935
|
+
if (m)
|
|
73936
|
+
return { changeName: m[1], identifier: opts.issue };
|
|
73937
|
+
}
|
|
73938
|
+
for (const line of allLines) {
|
|
73786
73939
|
const m = SPAWN_RE.exec(line.text);
|
|
73787
73940
|
if (m && m[1] === opts.issue)
|
|
73788
73941
|
return { changeName: m[2], identifier: opts.issue };
|
|
@@ -73852,34 +74005,33 @@ async function fetchGithubPr(changeName) {
|
|
|
73852
74005
|
]) ?? [];
|
|
73853
74006
|
return { ...pr, checks };
|
|
73854
74007
|
}
|
|
74008
|
+
function fetchMergeableNow(prUrl) {
|
|
74009
|
+
const result2 = Bun.spawnSync(["gh", "pr", "view", prUrl, "--json", "mergeable", "--jq", ".mergeable"], { stderr: "ignore" });
|
|
74010
|
+
return result2.exitCode === 0 ? result2.stdout.toString().trim() : null;
|
|
74011
|
+
}
|
|
73855
74012
|
async function runDebug(opts) {
|
|
73856
74013
|
const { projectRoot } = opts;
|
|
74014
|
+
const out = (s) => process.stdout.write(s + `
|
|
74015
|
+
`);
|
|
73857
74016
|
const agentLogFile = Bun.file(AGENT_LOG_PATH);
|
|
73858
|
-
const
|
|
73859
|
-
const
|
|
73860
|
-
|
|
74017
|
+
const textLines = await agentLogFile.exists() ? parseTextLog(await agentLogFile.text()) : [];
|
|
74018
|
+
const jsonlLogPath = join20(projectRoot, ".ralph", "agent.log");
|
|
74019
|
+
const jsonlLogFile = Bun.file(jsonlLogPath);
|
|
74020
|
+
const hasJsonlLog = await jsonlLogFile.exists();
|
|
74021
|
+
let { changeName, identifier: issueIdentifier } = await resolveDebugTarget(projectRoot, {
|
|
73861
74022
|
...opts.name !== undefined ? { name: opts.name } : {},
|
|
73862
74023
|
...opts.issue !== undefined ? { issue: opts.issue } : {}
|
|
73863
74024
|
});
|
|
73864
74025
|
if (!changeName) {
|
|
73865
|
-
process.stderr.write(`! Could not resolve
|
|
74026
|
+
process.stderr.write(`! Could not resolve change name for ${opts.issue ?? opts.name}.
|
|
73866
74027
|
`);
|
|
73867
74028
|
process.exit(1);
|
|
73868
74029
|
}
|
|
73869
|
-
const
|
|
73870
|
-
|
|
73871
|
-
|
|
73872
|
-
|
|
73873
|
-
|
|
73874
|
-
issueIdentifier = m[1];
|
|
73875
|
-
break;
|
|
73876
|
-
}
|
|
73877
|
-
}
|
|
73878
|
-
}
|
|
73879
|
-
const workerLogPath = join20(projectRoot, ".ralph", "logs", `${changeName}.log`);
|
|
73880
|
-
const workerLogFile = Bun.file(workerLogPath);
|
|
73881
|
-
const workerLines = await workerLogFile.exists() ? parseLog(await workerLogFile.text()) : [];
|
|
73882
|
-
const merged = [...relevant, ...workerLines].sort((a, b) => +a.ts - +b.ts);
|
|
74030
|
+
const jsonlLines = hasJsonlLog ? parseJsonlLog(await jsonlLogFile.text(), changeName) : [];
|
|
74031
|
+
const relevantText = textLines.filter((l) => l.text.includes(changeName) || issueIdentifier !== undefined && l.text.includes(issueIdentifier));
|
|
74032
|
+
const workerLogFile = Bun.file(join20(projectRoot, ".ralph", "logs", `${changeName}.log`));
|
|
74033
|
+
const workerLines = await workerLogFile.exists() ? parseTextLog(await workerLogFile.text()) : [];
|
|
74034
|
+
const merged = [...relevantText, ...jsonlLines, ...workerLines].sort((a, b) => +a.ts - +b.ts);
|
|
73883
74035
|
const seen = new Set;
|
|
73884
74036
|
const timeline = merged.filter((l) => {
|
|
73885
74037
|
const key = `${l.ts.getTime()}:${l.type}:${l.text}`;
|
|
@@ -73888,8 +74040,24 @@ async function runDebug(opts) {
|
|
|
73888
74040
|
seen.add(key);
|
|
73889
74041
|
return true;
|
|
73890
74042
|
});
|
|
73891
|
-
|
|
73892
|
-
|
|
74043
|
+
if (!issueIdentifier) {
|
|
74044
|
+
for (const line of timeline) {
|
|
74045
|
+
const m = SPAWN_RE.exec(line.text);
|
|
74046
|
+
if (m && m[2] === changeName) {
|
|
74047
|
+
issueIdentifier = m[1];
|
|
74048
|
+
break;
|
|
74049
|
+
}
|
|
74050
|
+
if (line.text.includes(changeName)) {
|
|
74051
|
+
const id = /(COD|ENG|DEV)-\d+/.exec(line.text)?.[0];
|
|
74052
|
+
if (id) {
|
|
74053
|
+
issueIdentifier = id;
|
|
74054
|
+
break;
|
|
74055
|
+
}
|
|
74056
|
+
}
|
|
74057
|
+
}
|
|
74058
|
+
}
|
|
74059
|
+
const stuck = detectStuck(timeline);
|
|
74060
|
+
const binary = await inspectBinary(projectRoot);
|
|
73893
74061
|
out(`
|
|
73894
74062
|
=== Ralph Debug: ${changeName}${issueIdentifier ? ` (${issueIdentifier})` : ""} ===
|
|
73895
74063
|
`);
|
|
@@ -73897,12 +74065,30 @@ async function runDebug(opts) {
|
|
|
73897
74065
|
if (!timeline.length) {
|
|
73898
74066
|
out(" (no log entries found)");
|
|
73899
74067
|
} else {
|
|
73900
|
-
|
|
73901
|
-
const
|
|
73902
|
-
|
|
74068
|
+
if (stuck && timeline.length > 20) {
|
|
74069
|
+
const phaseLines = timeline.filter((l) => l.type === "phase" && l.text.includes(stuck.phase));
|
|
74070
|
+
const nonPhase = timeline.filter((l) => !(l.type === "phase" && l.text.includes(stuck.phase)));
|
|
74071
|
+
for (const line of nonPhase) {
|
|
74072
|
+
const prefix = line.type === "output" ? " \u2502" : " \xB7";
|
|
74073
|
+
out(`${prefix} ${fmtTs(line.ts)} [${line.type.padEnd(7)}] ${line.text}`);
|
|
74074
|
+
}
|
|
74075
|
+
out(` \u21BA ... ${phaseLines.length}\xD7 ${stuck.phase} (${stuck.minutesStuck.toFixed(1)} min) ...`);
|
|
74076
|
+
} else {
|
|
74077
|
+
for (const line of timeline) {
|
|
74078
|
+
const prefix = line.type === "output" ? " \u2502" : " \xB7";
|
|
74079
|
+
out(`${prefix} ${fmtTs(line.ts)} [${line.type.padEnd(7)}] ${line.text}`);
|
|
74080
|
+
}
|
|
73903
74081
|
}
|
|
73904
74082
|
}
|
|
73905
74083
|
out("");
|
|
74084
|
+
if (binary) {
|
|
74085
|
+
out("\u2500\u2500 Installed binary \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
74086
|
+
out(` Path : ${binary.path}`);
|
|
74087
|
+
out(` Embedded version : ${binary.embeddedVersion ?? "(unknown)"}`);
|
|
74088
|
+
if (binary.builtAt)
|
|
74089
|
+
out(` Built at : ${fmtTs(binary.builtAt)}`);
|
|
74090
|
+
out("");
|
|
74091
|
+
}
|
|
73906
74092
|
out("\u2500\u2500 Current Linear state \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
73907
74093
|
if (!issueIdentifier) {
|
|
73908
74094
|
out(" (unknown identifier \u2014 pass --issue to query Linear directly)");
|
|
@@ -73924,7 +74110,13 @@ async function runDebug(opts) {
|
|
|
73924
74110
|
out("\u2500\u2500 Current GitHub PR \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
73925
74111
|
const pr = await fetchGithubPr(changeName);
|
|
73926
74112
|
if (!pr) {
|
|
73927
|
-
|
|
74113
|
+
if (stuck?.watchingPrUrl) {
|
|
74114
|
+
const m = fetchMergeableNow(stuck.watchingPrUrl);
|
|
74115
|
+
out(` Watching : ${stuck.watchingPrUrl}`);
|
|
74116
|
+
out(` Mergeable: ${m ?? "(error fetching)"}`);
|
|
74117
|
+
} else {
|
|
74118
|
+
out(` (no PR found for branch ralph/${changeName})`);
|
|
74119
|
+
}
|
|
73928
74120
|
} else {
|
|
73929
74121
|
const failing = pr.checks.filter((c) => c.conclusion === "FAILURE" || c.conclusion === "failure");
|
|
73930
74122
|
const pending = pr.checks.filter((c) => c.state === "PENDING" || c.state === "IN_PROGRESS");
|
|
@@ -73946,32 +74138,58 @@ async function runDebug(opts) {
|
|
|
73946
74138
|
const lastEvent = timeline.at(-1);
|
|
73947
74139
|
if (lastEvent)
|
|
73948
74140
|
out(` Last event : ${fmtTs(lastEvent.ts)} ${lastEvent.text}`);
|
|
73949
|
-
const exitLine =
|
|
74141
|
+
const exitLine = timeline.find((l) => /exited \(code \d+\)/.test(l.text));
|
|
73950
74142
|
if (exitLine) {
|
|
73951
74143
|
const code = Number(/code (\d+)/.exec(exitLine.text)?.[1]);
|
|
73952
|
-
const meaning = code === 0 ? "success" : code === 70 ? "CI fix loop exhausted its attempt budget" : code === 71 ? "push or PR creation failed
|
|
74144
|
+
const meaning = code === 0 ? "success" : code === 70 ? "CI fix loop exhausted its attempt budget" : code === 71 ? "push or PR creation failed" : "worker subprocess failed";
|
|
73953
74145
|
out(` Exit code : ${code} \u2014 ${meaning}`);
|
|
73954
74146
|
}
|
|
73955
|
-
|
|
74147
|
+
if (stuck) {
|
|
74148
|
+
out(` \u26A0 STUCK in ${stuck.phase} \u2014 ${stuck.count} iterations over ${stuck.minutesStuck.toFixed(1)} min`);
|
|
74149
|
+
if (stuck.watchingPrUrl) {
|
|
74150
|
+
const mergeable = fetchMergeableNow(stuck.watchingPrUrl);
|
|
74151
|
+
out(` Watching : ${stuck.watchingPrUrl}`);
|
|
74152
|
+
out(` Mergeable : ${mergeable ?? "(error)"} (live fetch)`);
|
|
74153
|
+
if (mergeable === "MERGEABLE") {
|
|
74154
|
+
out(` \u2192 PR is MERGEABLE \u2014 loop should have exited. Likely cause:`);
|
|
74155
|
+
if (binary?.embeddedVersion && binary.embeddedVersion < "2.17.1") {
|
|
74156
|
+
out(` Local binary is v${binary.embeddedVersion} (fix shipped in v2.17.1). Update with:`);
|
|
74157
|
+
out(` cd ${projectRoot} && bunx @neriros/ralphy@latest make-install`);
|
|
74158
|
+
} else {
|
|
74159
|
+
out(` This is the conflict-check infinite loop bug (fixed in v2.17.1).`);
|
|
74160
|
+
out(` Restart the agent after updating to v2.17.1.`);
|
|
74161
|
+
}
|
|
74162
|
+
}
|
|
74163
|
+
}
|
|
74164
|
+
}
|
|
74165
|
+
if (binary) {
|
|
74166
|
+
const embV = binary.embeddedVersion ?? "?";
|
|
74167
|
+
const logV = timeline.find((l) => l.text.includes("agent started"))?.text.match(/v([\d.]+)/)?.[1];
|
|
74168
|
+
if (logV && embV !== logV) {
|
|
74169
|
+
out(` \u26A0 Version mismatch: binary says v${embV}, agent reported v${logV}`);
|
|
74170
|
+
out(` The binary reads version from a package.json at runtime \u2014 the actual`);
|
|
74171
|
+
out(` running code is v${embV}, not v${logV}. Update the local install.`);
|
|
74172
|
+
}
|
|
74173
|
+
}
|
|
74174
|
+
const logHas = (s) => timeline.some((l) => l.text.includes(s));
|
|
73956
74175
|
if (logHas("setError applied"))
|
|
73957
74176
|
out(" \u26A0 setError applied \u2014 issue is quarantined in Linear");
|
|
73958
74177
|
if (logHas("setDone applied"))
|
|
73959
74178
|
out(" \u2713 setDone applied \u2014 issue marked done in Linear");
|
|
73960
74179
|
if (logHas("clearConflicted applied"))
|
|
73961
|
-
out(" \u2713 clearConflicted applied
|
|
74180
|
+
out(" \u2713 clearConflicted applied");
|
|
73962
74181
|
if (logHas("setConflicted applied"))
|
|
73963
74182
|
out(" \u26A0 setConflicted applied \u2014 merge conflicts detected");
|
|
73964
74183
|
if (logHas("skipping PR phase"))
|
|
73965
74184
|
out(" \u21A9 PR phase skipped \u2014 worker exited non-zero");
|
|
73966
74185
|
if (pr?.mergeable === "CONFLICTING")
|
|
73967
74186
|
out(" \u26A0 PR currently has merge conflicts");
|
|
73968
|
-
if (pr?.checks.some((c) => c.conclusion === "FAILURE"
|
|
74187
|
+
if (pr?.checks.some((c) => c.conclusion === "FAILURE"))
|
|
73969
74188
|
out(" \u26A0 PR has failing CI checks");
|
|
73970
|
-
}
|
|
73971
74189
|
const worktreePath = join20(projectRoot, ".ralph", "worktrees", changeName);
|
|
73972
|
-
|
|
73973
|
-
if (worktreeExists)
|
|
74190
|
+
if (await Bun.file(join20(worktreePath, ".git")).exists()) {
|
|
73974
74191
|
out(` Worktree : ${worktreePath}`);
|
|
74192
|
+
}
|
|
73975
74193
|
if (!timeline.length)
|
|
73976
74194
|
out(" (no log entries \u2014 has this change been started yet?)");
|
|
73977
74195
|
out("");
|
|
@@ -74041,7 +74259,7 @@ try {
|
|
|
74041
74259
|
`);
|
|
74042
74260
|
process.exit(1);
|
|
74043
74261
|
}
|
|
74044
|
-
await runDebug({ name: args.name, projectRoot });
|
|
74262
|
+
await runDebug({ name: args.name, projectRoot: args.projectRoot ?? projectRoot });
|
|
74045
74263
|
await shutdown();
|
|
74046
74264
|
process.exit(0);
|
|
74047
74265
|
}
|
package/dist/mcp/index.js
CHANGED
|
@@ -3047,10 +3047,13 @@ var require_data = __commonJS((exports, module) => {
|
|
|
3047
3047
|
};
|
|
3048
3048
|
});
|
|
3049
3049
|
|
|
3050
|
-
// node_modules/.bun/fast-uri@3.1.
|
|
3050
|
+
// node_modules/.bun/fast-uri@3.1.2/node_modules/fast-uri/lib/utils.js
|
|
3051
3051
|
var require_utils = __commonJS((exports, module) => {
|
|
3052
3052
|
var isUUID = RegExp.prototype.test.bind(/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iu);
|
|
3053
3053
|
var isIPv4 = RegExp.prototype.test.bind(/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u);
|
|
3054
|
+
var isHexPair = RegExp.prototype.test.bind(/^[\da-f]{2}$/iu);
|
|
3055
|
+
var isUnreserved = RegExp.prototype.test.bind(/^[\da-z\-._~]$/iu);
|
|
3056
|
+
var isPathCharacter = RegExp.prototype.test.bind(/^[\da-z\-._~!$&'()*+,;=:@/]$/iu);
|
|
3054
3057
|
function stringArrayToHexStripped(input) {
|
|
3055
3058
|
let acc = "";
|
|
3056
3059
|
let code = 0;
|
|
@@ -3244,27 +3247,77 @@ var require_utils = __commonJS((exports, module) => {
|
|
|
3244
3247
|
}
|
|
3245
3248
|
return output.join("");
|
|
3246
3249
|
}
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3250
|
+
var HOST_DELIMS = { "@": "%40", "/": "%2F", "?": "%3F", "#": "%23", ":": "%3A" };
|
|
3251
|
+
var HOST_DELIM_RE = /[@/?#:]/g;
|
|
3252
|
+
var HOST_DELIM_NO_COLON_RE = /[@/?#]/g;
|
|
3253
|
+
function reescapeHostDelimiters(host, isIP) {
|
|
3254
|
+
const re = isIP ? HOST_DELIM_NO_COLON_RE : HOST_DELIM_RE;
|
|
3255
|
+
re.lastIndex = 0;
|
|
3256
|
+
return host.replace(re, (ch) => HOST_DELIMS[ch]);
|
|
3257
|
+
}
|
|
3258
|
+
function normalizePercentEncoding(input, decodeUnreserved = false) {
|
|
3259
|
+
if (input.indexOf("%") === -1) {
|
|
3260
|
+
return input;
|
|
3257
3261
|
}
|
|
3258
|
-
|
|
3259
|
-
|
|
3262
|
+
let output = "";
|
|
3263
|
+
for (let i = 0;i < input.length; i++) {
|
|
3264
|
+
if (input[i] === "%" && i + 2 < input.length) {
|
|
3265
|
+
const hex = input.slice(i + 1, i + 3);
|
|
3266
|
+
if (isHexPair(hex)) {
|
|
3267
|
+
const normalizedHex = hex.toUpperCase();
|
|
3268
|
+
const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
|
|
3269
|
+
if (decodeUnreserved && isUnreserved(decoded)) {
|
|
3270
|
+
output += decoded;
|
|
3271
|
+
} else {
|
|
3272
|
+
output += "%" + normalizedHex;
|
|
3273
|
+
}
|
|
3274
|
+
i += 2;
|
|
3275
|
+
continue;
|
|
3276
|
+
}
|
|
3277
|
+
}
|
|
3278
|
+
output += input[i];
|
|
3260
3279
|
}
|
|
3261
|
-
|
|
3262
|
-
|
|
3280
|
+
return output;
|
|
3281
|
+
}
|
|
3282
|
+
function normalizePathEncoding(input) {
|
|
3283
|
+
let output = "";
|
|
3284
|
+
for (let i = 0;i < input.length; i++) {
|
|
3285
|
+
if (input[i] === "%" && i + 2 < input.length) {
|
|
3286
|
+
const hex = input.slice(i + 1, i + 3);
|
|
3287
|
+
if (isHexPair(hex)) {
|
|
3288
|
+
const normalizedHex = hex.toUpperCase();
|
|
3289
|
+
const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
|
|
3290
|
+
if (decoded !== "." && isUnreserved(decoded)) {
|
|
3291
|
+
output += decoded;
|
|
3292
|
+
} else {
|
|
3293
|
+
output += "%" + normalizedHex;
|
|
3294
|
+
}
|
|
3295
|
+
i += 2;
|
|
3296
|
+
continue;
|
|
3297
|
+
}
|
|
3298
|
+
}
|
|
3299
|
+
if (isPathCharacter(input[i])) {
|
|
3300
|
+
output += input[i];
|
|
3301
|
+
} else {
|
|
3302
|
+
output += escape(input[i]);
|
|
3303
|
+
}
|
|
3263
3304
|
}
|
|
3264
|
-
|
|
3265
|
-
|
|
3305
|
+
return output;
|
|
3306
|
+
}
|
|
3307
|
+
function escapePreservingEscapes(input) {
|
|
3308
|
+
let output = "";
|
|
3309
|
+
for (let i = 0;i < input.length; i++) {
|
|
3310
|
+
if (input[i] === "%" && i + 2 < input.length) {
|
|
3311
|
+
const hex = input.slice(i + 1, i + 3);
|
|
3312
|
+
if (isHexPair(hex)) {
|
|
3313
|
+
output += "%" + hex.toUpperCase();
|
|
3314
|
+
i += 2;
|
|
3315
|
+
continue;
|
|
3316
|
+
}
|
|
3317
|
+
}
|
|
3318
|
+
output += escape(input[i]);
|
|
3266
3319
|
}
|
|
3267
|
-
return
|
|
3320
|
+
return output;
|
|
3268
3321
|
}
|
|
3269
3322
|
function recomposeAuthority(component) {
|
|
3270
3323
|
const uriTokens = [];
|
|
@@ -3279,7 +3332,7 @@ var require_utils = __commonJS((exports, module) => {
|
|
|
3279
3332
|
if (ipV6res.isIPV6 === true) {
|
|
3280
3333
|
host = `[${ipV6res.escapedHost}]`;
|
|
3281
3334
|
} else {
|
|
3282
|
-
host =
|
|
3335
|
+
host = reescapeHostDelimiters(host, false);
|
|
3283
3336
|
}
|
|
3284
3337
|
}
|
|
3285
3338
|
uriTokens.push(host);
|
|
@@ -3293,7 +3346,10 @@ var require_utils = __commonJS((exports, module) => {
|
|
|
3293
3346
|
module.exports = {
|
|
3294
3347
|
nonSimpleDomain,
|
|
3295
3348
|
recomposeAuthority,
|
|
3296
|
-
|
|
3349
|
+
reescapeHostDelimiters,
|
|
3350
|
+
normalizePercentEncoding,
|
|
3351
|
+
normalizePathEncoding,
|
|
3352
|
+
escapePreservingEscapes,
|
|
3297
3353
|
removeDotSegments,
|
|
3298
3354
|
isIPv4,
|
|
3299
3355
|
isUUID,
|
|
@@ -3302,7 +3358,7 @@ var require_utils = __commonJS((exports, module) => {
|
|
|
3302
3358
|
};
|
|
3303
3359
|
});
|
|
3304
3360
|
|
|
3305
|
-
// node_modules/.bun/fast-uri@3.1.
|
|
3361
|
+
// node_modules/.bun/fast-uri@3.1.2/node_modules/fast-uri/lib/schemes.js
|
|
3306
3362
|
var require_schemes = __commonJS((exports, module) => {
|
|
3307
3363
|
var { isUUID } = require_utils();
|
|
3308
3364
|
var URN_REG = /([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-.:;=@]|%[\da-f]{2})+)/iu;
|
|
@@ -3476,13 +3532,13 @@ var require_schemes = __commonJS((exports, module) => {
|
|
|
3476
3532
|
};
|
|
3477
3533
|
});
|
|
3478
3534
|
|
|
3479
|
-
// node_modules/.bun/fast-uri@3.1.
|
|
3535
|
+
// node_modules/.bun/fast-uri@3.1.2/node_modules/fast-uri/index.js
|
|
3480
3536
|
var require_fast_uri = __commonJS((exports, module) => {
|
|
3481
|
-
var { normalizeIPv6, removeDotSegments, recomposeAuthority,
|
|
3537
|
+
var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, escapePreservingEscapes, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
|
|
3482
3538
|
var { SCHEMES, getSchemeHandler } = require_schemes();
|
|
3483
3539
|
function normalize(uri, options) {
|
|
3484
3540
|
if (typeof uri === "string") {
|
|
3485
|
-
uri =
|
|
3541
|
+
uri = normalizeString(uri, options);
|
|
3486
3542
|
} else if (typeof uri === "object") {
|
|
3487
3543
|
uri = parse6(serialize(uri, options), options);
|
|
3488
3544
|
}
|
|
@@ -3548,19 +3604,9 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
3548
3604
|
return target;
|
|
3549
3605
|
}
|
|
3550
3606
|
function equal(uriA, uriB, options) {
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
} else if (typeof uriA === "object") {
|
|
3555
|
-
uriA = serialize(normalizeComponentEncoding(uriA, true), { ...options, skipEscape: true });
|
|
3556
|
-
}
|
|
3557
|
-
if (typeof uriB === "string") {
|
|
3558
|
-
uriB = unescape(uriB);
|
|
3559
|
-
uriB = serialize(normalizeComponentEncoding(parse6(uriB, options), true), { ...options, skipEscape: true });
|
|
3560
|
-
} else if (typeof uriB === "object") {
|
|
3561
|
-
uriB = serialize(normalizeComponentEncoding(uriB, true), { ...options, skipEscape: true });
|
|
3562
|
-
}
|
|
3563
|
-
return uriA.toLowerCase() === uriB.toLowerCase();
|
|
3607
|
+
const normalizedA = normalizeComparableURI(uriA, options);
|
|
3608
|
+
const normalizedB = normalizeComparableURI(uriB, options);
|
|
3609
|
+
return normalizedA !== undefined && normalizedB !== undefined && normalizedA.toLowerCase() === normalizedB.toLowerCase();
|
|
3564
3610
|
}
|
|
3565
3611
|
function serialize(cmpts, opts) {
|
|
3566
3612
|
const component = {
|
|
@@ -3586,12 +3632,12 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
3586
3632
|
schemeHandler.serialize(component, options);
|
|
3587
3633
|
if (component.path !== undefined) {
|
|
3588
3634
|
if (!options.skipEscape) {
|
|
3589
|
-
component.path =
|
|
3635
|
+
component.path = escapePreservingEscapes(component.path);
|
|
3590
3636
|
if (component.scheme !== undefined) {
|
|
3591
3637
|
component.path = component.path.split("%3A").join(":");
|
|
3592
3638
|
}
|
|
3593
3639
|
} else {
|
|
3594
|
-
component.path =
|
|
3640
|
+
component.path = normalizePercentEncoding(component.path);
|
|
3595
3641
|
}
|
|
3596
3642
|
}
|
|
3597
3643
|
if (options.reference !== "suffix" && component.scheme) {
|
|
@@ -3626,7 +3672,16 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
3626
3672
|
return uriTokens.join("");
|
|
3627
3673
|
}
|
|
3628
3674
|
var URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u;
|
|
3629
|
-
function
|
|
3675
|
+
function getParseError(parsed, matches) {
|
|
3676
|
+
if (matches[2] !== undefined && parsed.path && parsed.path[0] !== "/") {
|
|
3677
|
+
return 'URI path must start with "/" when authority is present.';
|
|
3678
|
+
}
|
|
3679
|
+
if (typeof parsed.port === "number" && (parsed.port < 0 || parsed.port > 65535)) {
|
|
3680
|
+
return "URI port is malformed.";
|
|
3681
|
+
}
|
|
3682
|
+
return;
|
|
3683
|
+
}
|
|
3684
|
+
function parseWithStatus(uri, opts) {
|
|
3630
3685
|
const options = Object.assign({}, opts);
|
|
3631
3686
|
const parsed = {
|
|
3632
3687
|
scheme: undefined,
|
|
@@ -3637,6 +3692,7 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
3637
3692
|
query: undefined,
|
|
3638
3693
|
fragment: undefined
|
|
3639
3694
|
};
|
|
3695
|
+
let malformedAuthorityOrPort = false;
|
|
3640
3696
|
let isIP = false;
|
|
3641
3697
|
if (options.reference === "suffix") {
|
|
3642
3698
|
if (options.scheme) {
|
|
@@ -3657,6 +3713,11 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
3657
3713
|
if (isNaN(parsed.port)) {
|
|
3658
3714
|
parsed.port = matches[5];
|
|
3659
3715
|
}
|
|
3716
|
+
const parseError = getParseError(parsed, matches);
|
|
3717
|
+
if (parseError !== undefined) {
|
|
3718
|
+
parsed.error = parsed.error || parseError;
|
|
3719
|
+
malformedAuthorityOrPort = true;
|
|
3720
|
+
}
|
|
3660
3721
|
if (parsed.host) {
|
|
3661
3722
|
const ipv4result = isIPv4(parsed.host);
|
|
3662
3723
|
if (ipv4result === false) {
|
|
@@ -3695,14 +3756,18 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
3695
3756
|
parsed.scheme = unescape(parsed.scheme);
|
|
3696
3757
|
}
|
|
3697
3758
|
if (parsed.host !== undefined) {
|
|
3698
|
-
parsed.host = unescape(parsed.host);
|
|
3759
|
+
parsed.host = reescapeHostDelimiters(unescape(parsed.host), isIP);
|
|
3699
3760
|
}
|
|
3700
3761
|
}
|
|
3701
3762
|
if (parsed.path) {
|
|
3702
|
-
parsed.path =
|
|
3763
|
+
parsed.path = normalizePathEncoding(parsed.path);
|
|
3703
3764
|
}
|
|
3704
3765
|
if (parsed.fragment) {
|
|
3705
|
-
|
|
3766
|
+
try {
|
|
3767
|
+
parsed.fragment = encodeURI(decodeURIComponent(parsed.fragment));
|
|
3768
|
+
} catch {
|
|
3769
|
+
parsed.error = parsed.error || "URI malformed";
|
|
3770
|
+
}
|
|
3706
3771
|
}
|
|
3707
3772
|
}
|
|
3708
3773
|
if (schemeHandler && schemeHandler.parse) {
|
|
@@ -3711,7 +3776,29 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
3711
3776
|
} else {
|
|
3712
3777
|
parsed.error = parsed.error || "URI can not be parsed.";
|
|
3713
3778
|
}
|
|
3714
|
-
return parsed;
|
|
3779
|
+
return { parsed, malformedAuthorityOrPort };
|
|
3780
|
+
}
|
|
3781
|
+
function parse6(uri, opts) {
|
|
3782
|
+
return parseWithStatus(uri, opts).parsed;
|
|
3783
|
+
}
|
|
3784
|
+
function normalizeString(uri, opts) {
|
|
3785
|
+
return normalizeStringWithStatus(uri, opts).normalized;
|
|
3786
|
+
}
|
|
3787
|
+
function normalizeStringWithStatus(uri, opts) {
|
|
3788
|
+
const { parsed, malformedAuthorityOrPort } = parseWithStatus(uri, opts);
|
|
3789
|
+
return {
|
|
3790
|
+
normalized: malformedAuthorityOrPort ? uri : serialize(parsed, opts),
|
|
3791
|
+
malformedAuthorityOrPort
|
|
3792
|
+
};
|
|
3793
|
+
}
|
|
3794
|
+
function normalizeComparableURI(uri, opts) {
|
|
3795
|
+
if (typeof uri === "string") {
|
|
3796
|
+
const { normalized, malformedAuthorityOrPort } = normalizeStringWithStatus(uri, opts);
|
|
3797
|
+
return malformedAuthorityOrPort ? undefined : normalized;
|
|
3798
|
+
}
|
|
3799
|
+
if (typeof uri === "object") {
|
|
3800
|
+
return serialize(uri, opts);
|
|
3801
|
+
}
|
|
3715
3802
|
}
|
|
3716
3803
|
var fastUri = {
|
|
3717
3804
|
SCHEMES,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neriros/ralphy",
|
|
3
|
-
"version": "2.17.
|
|
3
|
+
"version": "2.17.3",
|
|
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",
|
|
@@ -57,33 +57,35 @@
|
|
|
57
57
|
"prepublishOnly": "bun run build:publish"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
|
-
"@commitlint/cli": "^20.
|
|
61
|
-
"@commitlint/config-conventional": "^20.
|
|
60
|
+
"@commitlint/cli": "^20.5.3",
|
|
61
|
+
"@commitlint/config-conventional": "^20.5.3",
|
|
62
62
|
"@fission-ai/openspec": "latest",
|
|
63
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
63
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
64
64
|
"@nx/devkit": "^22.7.1",
|
|
65
65
|
"@nx/js": "^22.7.1",
|
|
66
|
-
"@secretlint/secretlint-rule-preset-recommend": "^11.
|
|
66
|
+
"@secretlint/secretlint-rule-preset-recommend": "^11.7.1",
|
|
67
67
|
"@swc-node/register": "^1.11.1",
|
|
68
|
-
"@swc/core": "^1.15.
|
|
68
|
+
"@swc/core": "^1.15.33",
|
|
69
69
|
"@total-typescript/ts-reset": "^0.6.1",
|
|
70
|
-
"@types/node": "^22.
|
|
71
|
-
"bun-types": "^1.3.
|
|
72
|
-
"chalk": "^5.
|
|
73
|
-
"dependency-cruiser": "^17.
|
|
70
|
+
"@types/node": "^22.19.18",
|
|
71
|
+
"bun-types": "^1.3.13",
|
|
72
|
+
"chalk": "^5.6.2",
|
|
73
|
+
"dependency-cruiser": "^17.4.0",
|
|
74
74
|
"husky": "^9.1.7",
|
|
75
|
-
"knip": "^5.
|
|
76
|
-
"lint-staged": "^16.
|
|
75
|
+
"knip": "^5.88.1",
|
|
76
|
+
"lint-staged": "^16.4.0",
|
|
77
77
|
"nx": "22.5.3",
|
|
78
78
|
"oxc-parser": "^0.126.0",
|
|
79
79
|
"oxfmt": "^0.36.0",
|
|
80
|
-
"oxlint": "^1.
|
|
81
|
-
"secretlint": "^11.
|
|
82
|
-
"typescript": "^5.
|
|
83
|
-
"zod": "^3.
|
|
80
|
+
"oxlint": "^1.63.0",
|
|
81
|
+
"secretlint": "^11.7.1",
|
|
82
|
+
"typescript": "^5.9.3",
|
|
83
|
+
"zod": "^3.25.76"
|
|
84
84
|
},
|
|
85
85
|
"overrides": {
|
|
86
|
+
"@babel/plugin-transform-modules-systemjs": "^7.29.4",
|
|
86
87
|
"axios": "^1.15.1",
|
|
88
|
+
"fast-uri": "^3.1.2",
|
|
87
89
|
"minimatch": "^10.2.3"
|
|
88
90
|
},
|
|
89
91
|
"engines": {
|