@neriros/ralphy 2.7.9 → 2.8.0
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 +267 -60
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -50549,7 +50549,7 @@ var require_axios = __commonJS((exports, module) => {
|
|
|
50549
50549
|
|
|
50550
50550
|
// apps/cli/src/index.ts
|
|
50551
50551
|
import { resolve, join as join17, dirname as dirname4 } from "path";
|
|
50552
|
-
import { exists, mkdir as mkdir3 } from "fs/promises";
|
|
50552
|
+
import { exists, mkdir as mkdir3, rm } from "fs/promises";
|
|
50553
50553
|
|
|
50554
50554
|
// node_modules/.bun/ink@5.2.1+1f88f629f0141b18/node_modules/ink/build/render.js
|
|
50555
50555
|
import { Stream } from "stream";
|
|
@@ -56117,7 +56117,7 @@ function log(msg) {
|
|
|
56117
56117
|
}
|
|
56118
56118
|
|
|
56119
56119
|
// apps/cli/src/cli.ts
|
|
56120
|
-
var VALID_MODES = new Set(["task", "list", "status", "init", "agent"]);
|
|
56120
|
+
var VALID_MODES = new Set(["task", "list", "status", "init", "agent", "clean"]);
|
|
56121
56121
|
var VALID_MODELS = new Set(["haiku", "sonnet", "opus"]);
|
|
56122
56122
|
var HELP_TEXT = [
|
|
56123
56123
|
"Usage: ralph <command> [options]",
|
|
@@ -56128,6 +56128,7 @@ var HELP_TEXT = [
|
|
|
56128
56128
|
" status Show detailed change status",
|
|
56129
56129
|
" init Initialize OpenSpec in current directory",
|
|
56130
56130
|
" agent Poll Linear for new tasks and run loops concurrently",
|
|
56131
|
+
" clean Remove worktree, branch, openspec change, and task state for --name",
|
|
56131
56132
|
"",
|
|
56132
56133
|
"Options:",
|
|
56133
56134
|
" --name <name> Change name (required for most commands)",
|
|
@@ -60611,54 +60612,70 @@ function countTaskItems(content) {
|
|
|
60611
60612
|
const unchecked = (content.match(/^- \[ \]/gm) ?? []).length;
|
|
60612
60613
|
return { checked, unchecked };
|
|
60613
60614
|
}
|
|
60614
|
-
function buildRows(statesDir) {
|
|
60615
|
+
function buildRows(statesDir, projectRoot) {
|
|
60615
60616
|
const storage = getStorage();
|
|
60616
|
-
const entries = storage.list(statesDir);
|
|
60617
60617
|
const rows = [];
|
|
60618
|
-
|
|
60619
|
-
|
|
60620
|
-
|
|
60621
|
-
|
|
60622
|
-
|
|
60623
|
-
|
|
60624
|
-
|
|
60625
|
-
|
|
60626
|
-
|
|
60618
|
+
const seenNames = new Set;
|
|
60619
|
+
const sources = [{ dir: statesDir, label: "main" }];
|
|
60620
|
+
if (projectRoot) {
|
|
60621
|
+
const worktreesRoot = join3(projectRoot, ".ralph", "worktrees");
|
|
60622
|
+
for (const wt of storage.list(worktreesRoot)) {
|
|
60623
|
+
sources.push({
|
|
60624
|
+
dir: join3(worktreesRoot, wt, ".ralph", "tasks"),
|
|
60625
|
+
label: `wt:${wt}`
|
|
60626
|
+
});
|
|
60627
60627
|
}
|
|
60628
|
-
|
|
60629
|
-
|
|
60630
|
-
const
|
|
60631
|
-
|
|
60628
|
+
}
|
|
60629
|
+
for (const { dir, label } of sources) {
|
|
60630
|
+
for (const entry of storage.list(dir)) {
|
|
60631
|
+
if (seenNames.has(entry))
|
|
60632
|
+
continue;
|
|
60633
|
+
const raw = storage.read(join3(dir, entry, ".ralph-state.json"));
|
|
60634
|
+
if (raw === null)
|
|
60635
|
+
continue;
|
|
60636
|
+
let state;
|
|
60637
|
+
try {
|
|
60638
|
+
state = JSON.parse(raw);
|
|
60639
|
+
} catch {
|
|
60640
|
+
continue;
|
|
60641
|
+
}
|
|
60642
|
+
if (String(state.status ?? "") === "completed")
|
|
60643
|
+
continue;
|
|
60644
|
+
const promptRaw = String(state.prompt ?? "");
|
|
60645
|
+
const firstLine = promptRaw.split(`
|
|
60632
60646
|
`).find((l) => l.trim() !== "") ?? "";
|
|
60633
|
-
|
|
60634
|
-
|
|
60635
|
-
|
|
60636
|
-
|
|
60637
|
-
|
|
60638
|
-
|
|
60639
|
-
|
|
60640
|
-
|
|
60641
|
-
|
|
60642
|
-
|
|
60643
|
-
|
|
60644
|
-
|
|
60645
|
-
|
|
60646
|
-
|
|
60647
|
-
|
|
60648
|
-
|
|
60649
|
-
|
|
60650
|
-
|
|
60651
|
-
|
|
60652
|
-
|
|
60647
|
+
let progress = "\u2014";
|
|
60648
|
+
let progressStyled = true;
|
|
60649
|
+
const tasksContent = storage.read(join3(dir, entry, "tasks.md"));
|
|
60650
|
+
if (tasksContent !== null) {
|
|
60651
|
+
const { checked, unchecked } = countTaskItems(tasksContent);
|
|
60652
|
+
const total = checked + unchecked;
|
|
60653
|
+
if (total > 0) {
|
|
60654
|
+
progress = `${checked}/${total}`;
|
|
60655
|
+
progressStyled = false;
|
|
60656
|
+
}
|
|
60657
|
+
}
|
|
60658
|
+
seenNames.add(entry);
|
|
60659
|
+
rows.push({
|
|
60660
|
+
name: String(state.name ?? entry),
|
|
60661
|
+
phase: String(state.status ?? "active"),
|
|
60662
|
+
status: String(state.status ?? "unknown"),
|
|
60663
|
+
iters: String(state.iteration ?? 0),
|
|
60664
|
+
progress,
|
|
60665
|
+
progressStyled,
|
|
60666
|
+
prompt: firstLine.replace(/^#+\s*/, "").trim().slice(0, 60),
|
|
60667
|
+
source: label
|
|
60668
|
+
});
|
|
60669
|
+
}
|
|
60653
60670
|
}
|
|
60654
60671
|
return rows;
|
|
60655
60672
|
}
|
|
60656
|
-
function TaskList({ statesDir }) {
|
|
60673
|
+
function TaskList({ statesDir, projectRoot }) {
|
|
60657
60674
|
const { exit } = use_app_default();
|
|
60658
60675
|
import_react22.useEffect(() => {
|
|
60659
60676
|
exit();
|
|
60660
60677
|
}, [exit]);
|
|
60661
|
-
const rows = buildRows(statesDir);
|
|
60678
|
+
const rows = buildRows(statesDir, projectRoot);
|
|
60662
60679
|
if (rows.length === 0) {
|
|
60663
60680
|
return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
|
|
60664
60681
|
flexDirection: "column",
|
|
@@ -60681,9 +60698,10 @@ function TaskList({ statesDir }) {
|
|
|
60681
60698
|
phase: Math.max(5, ...rows.map((r) => r.phase.length)),
|
|
60682
60699
|
status: Math.max(6, ...rows.map((r) => r.status.length)),
|
|
60683
60700
|
iters: 5,
|
|
60684
|
-
progress: 8
|
|
60701
|
+
progress: 8,
|
|
60702
|
+
source: Math.max(6, ...rows.map((r) => r.source.length))
|
|
60685
60703
|
};
|
|
60686
|
-
const ruleWidth = cols.name + cols.phase + cols.status + cols.iters + cols.progress + 60 +
|
|
60704
|
+
const ruleWidth = cols.name + cols.phase + cols.status + cols.iters + cols.progress + cols.source + 60 + 12;
|
|
60687
60705
|
return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
|
|
60688
60706
|
flexDirection: "column",
|
|
60689
60707
|
children: [
|
|
@@ -60717,6 +60735,11 @@ function TaskList({ statesDir }) {
|
|
|
60717
60735
|
children: "Progress".padEnd(cols.progress)
|
|
60718
60736
|
}, undefined, false, undefined, this),
|
|
60719
60737
|
" ",
|
|
60738
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
60739
|
+
bold: true,
|
|
60740
|
+
children: "Source".padEnd(cols.source)
|
|
60741
|
+
}, undefined, false, undefined, this),
|
|
60742
|
+
" ",
|
|
60720
60743
|
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
60721
60744
|
bold: true,
|
|
60722
60745
|
children: "Description"
|
|
@@ -60745,6 +60768,11 @@ function TaskList({ statesDir }) {
|
|
|
60745
60768
|
children: row.progress.padStart(cols.progress)
|
|
60746
60769
|
}, undefined, false, undefined, this) : row.progress.padStart(cols.progress),
|
|
60747
60770
|
" ",
|
|
60771
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
60772
|
+
dimColor: true,
|
|
60773
|
+
children: row.source.padEnd(cols.source)
|
|
60774
|
+
}, undefined, false, undefined, this),
|
|
60775
|
+
" ",
|
|
60748
60776
|
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
60749
60777
|
dimColor: true,
|
|
60750
60778
|
children: row.prompt
|
|
@@ -69758,7 +69786,12 @@ async function addLabelToIssue(apiKey, issueId, labelId) {
|
|
|
69758
69786
|
import { join as join10 } from "path";
|
|
69759
69787
|
var AgentStateSchema = exports_external.object({
|
|
69760
69788
|
processedIssueIds: exports_external.array(exports_external.string()).default([]),
|
|
69761
|
-
|
|
69789
|
+
startedIssueIds: exports_external.array(exports_external.string()).default([]),
|
|
69790
|
+
lastPollAt: exports_external.string().nullable().default(null),
|
|
69791
|
+
changeMeta: exports_external.record(exports_external.string(), exports_external.object({
|
|
69792
|
+
issueId: exports_external.string(),
|
|
69793
|
+
identifier: exports_external.string()
|
|
69794
|
+
})).default({})
|
|
69762
69795
|
});
|
|
69763
69796
|
function statePath(projectRoot) {
|
|
69764
69797
|
return join10(projectRoot, ".ralph", "agent-state.json");
|
|
@@ -70049,9 +70082,14 @@ class AgentCoordinator {
|
|
|
70049
70082
|
const updater = this.deps.updater;
|
|
70050
70083
|
if (!updater)
|
|
70051
70084
|
return;
|
|
70052
|
-
|
|
70085
|
+
const alreadyStarted = this.state?.startedIssueIds.includes(issue.id) ?? false;
|
|
70086
|
+
if (this.opts.postComments !== false && !alreadyStarted) {
|
|
70053
70087
|
try {
|
|
70054
70088
|
await updater.postComment(issue, `\uD83E\uDD16 Ralph started working on this issue. Tracking change: \`${changeName}\``);
|
|
70089
|
+
if (this.state && !this.state.startedIssueIds.includes(issue.id)) {
|
|
70090
|
+
this.state.startedIssueIds.push(issue.id);
|
|
70091
|
+
await this.deps.saveState(this.state);
|
|
70092
|
+
}
|
|
70055
70093
|
} catch (err) {
|
|
70056
70094
|
this.deps.onLog(`! Linear comment failed for ${issue.identifier}: ${err.message}`, "red");
|
|
70057
70095
|
}
|
|
@@ -70299,7 +70337,10 @@ var bunCmdRunner = {
|
|
|
70299
70337
|
const stderr = await new Response(proc.stderr).text();
|
|
70300
70338
|
const code = await proc.exited;
|
|
70301
70339
|
if (code !== 0) {
|
|
70302
|
-
const
|
|
70340
|
+
const firstStderrLine = stderr.trim().split(`
|
|
70341
|
+
`)[0] ?? "";
|
|
70342
|
+
const summary = firstStderrLine ? `: ${firstStderrLine}` : "";
|
|
70343
|
+
const err = new Error(`\`${cmd.join(" ")}\` exited ${code}${summary}`);
|
|
70303
70344
|
err.stderr = stderr;
|
|
70304
70345
|
err.code = code;
|
|
70305
70346
|
throw err;
|
|
@@ -70312,11 +70353,28 @@ function nextId() {
|
|
|
70312
70353
|
lineCounter += 1;
|
|
70313
70354
|
return `${Date.now()}-${lineCounter}`;
|
|
70314
70355
|
}
|
|
70356
|
+
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
70357
|
+
function fmtElapsed(ms) {
|
|
70358
|
+
const s = Math.floor(ms / 1000);
|
|
70359
|
+
if (s < 60)
|
|
70360
|
+
return `${s}s`;
|
|
70361
|
+
const m = Math.floor(s / 60);
|
|
70362
|
+
const rem = s % 60;
|
|
70363
|
+
if (m < 60)
|
|
70364
|
+
return `${m}m${rem.toString().padStart(2, "0")}s`;
|
|
70365
|
+
const h = Math.floor(m / 60);
|
|
70366
|
+
return `${h}h${(m % 60).toString().padStart(2, "0")}m`;
|
|
70367
|
+
}
|
|
70315
70368
|
function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
70316
70369
|
const { exit } = use_app_default();
|
|
70317
70370
|
const [logs, setLogs] = import_react57.useState([]);
|
|
70318
70371
|
const [, setTick] = import_react57.useState(0);
|
|
70372
|
+
const [clock, setClock] = import_react57.useState(0);
|
|
70319
70373
|
const coordRef = import_react57.useRef(null);
|
|
70374
|
+
const workerMetaRef = import_react57.useRef(new Map);
|
|
70375
|
+
const nextPollAtRef = import_react57.useRef(0);
|
|
70376
|
+
const pollIntervalRef = import_react57.useRef(0);
|
|
70377
|
+
const [pollStatus, setPollStatus] = import_react57.useState({ state: "idle", lastFound: null, lastAdded: null, lastAt: null, filterDesc: "" });
|
|
70320
70378
|
function appendLog(text, color) {
|
|
70321
70379
|
setLogs((prev) => [...prev, { id: nextId(), text, color }]);
|
|
70322
70380
|
}
|
|
@@ -70329,6 +70387,7 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
70329
70387
|
appendLog(`agent mode \u2014 config: ${cfgPath}`, "gray");
|
|
70330
70388
|
const concurrency = args.concurrency || cfg.concurrency;
|
|
70331
70389
|
const pollInterval = args.pollInterval || cfg.pollIntervalSeconds;
|
|
70390
|
+
pollIntervalRef.current = pollInterval;
|
|
70332
70391
|
appendLog(`concurrency=${concurrency} pollInterval=${pollInterval}s`, "gray");
|
|
70333
70392
|
const apiKey = process.env["LINEAR_API_KEY"];
|
|
70334
70393
|
if (!apiKey) {
|
|
@@ -70402,6 +70461,13 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
70402
70461
|
issueByChange.set(changeName, issue);
|
|
70403
70462
|
if (workerBranch)
|
|
70404
70463
|
branchByChange.set(changeName, workerBranch);
|
|
70464
|
+
try {
|
|
70465
|
+
const s = await readAgentState(projectRoot);
|
|
70466
|
+
s.changeMeta[changeName] = { issueId: issue.id, identifier: issue.identifier };
|
|
70467
|
+
await writeAgentState(projectRoot, s);
|
|
70468
|
+
} catch (err) {
|
|
70469
|
+
appendLog(`! failed to record agent meta for ${changeName}: ${err.message}`, "yellow");
|
|
70470
|
+
}
|
|
70405
70471
|
if (cfg.setupScript) {
|
|
70406
70472
|
await runScript("setup", cfg.setupScript, workerCwd);
|
|
70407
70473
|
}
|
|
@@ -70447,8 +70513,14 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
70447
70513
|
stderr: "ignore",
|
|
70448
70514
|
stdin: "ignore"
|
|
70449
70515
|
});
|
|
70516
|
+
workerMetaRef.current.set(changeName, {
|
|
70517
|
+
startedAt: Date.now(),
|
|
70518
|
+
statesDir: statesDirByChange.get(changeName) ?? statesDir,
|
|
70519
|
+
iter: 0
|
|
70520
|
+
});
|
|
70450
70521
|
const wantPr = args.createPr || cfg.createPrOnSuccess;
|
|
70451
70522
|
const CI_FAILED_EXIT = 70;
|
|
70523
|
+
const PR_FAILED_EXIT = 71;
|
|
70452
70524
|
const wrapped = proc.exited.then(async (code) => {
|
|
70453
70525
|
if (cfg.teardownScript) {
|
|
70454
70526
|
try {
|
|
@@ -70462,6 +70534,7 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
70462
70534
|
const prIssue = issueByChange.get(changeName);
|
|
70463
70535
|
if (!branch || !prIssue) {
|
|
70464
70536
|
appendLog(`! createPr requested but no worktree branch is tracked for ${changeName} (use --worktree)`, "yellow");
|
|
70537
|
+
effectiveCode = PR_FAILED_EXIT;
|
|
70465
70538
|
} else {
|
|
70466
70539
|
try {
|
|
70467
70540
|
const pr = await createPullRequest({ cwd: cwd2, branch, issue: prIssue, base: cfg.prBaseBranch }, bunCmdRunner);
|
|
@@ -70521,7 +70594,10 @@ ${stamped}
|
|
|
70521
70594
|
}
|
|
70522
70595
|
}
|
|
70523
70596
|
} catch (err) {
|
|
70524
|
-
|
|
70597
|
+
const e = err;
|
|
70598
|
+
const detail = e.stderr?.trim() || e.message;
|
|
70599
|
+
appendLog(`! PR create failed for ${changeName}: ${detail}`, "red");
|
|
70600
|
+
effectiveCode = PR_FAILED_EXIT;
|
|
70525
70601
|
}
|
|
70526
70602
|
}
|
|
70527
70603
|
}
|
|
@@ -70539,6 +70615,7 @@ ${stamped}
|
|
|
70539
70615
|
statesDirByChange.delete(changeName);
|
|
70540
70616
|
branchByChange.delete(changeName);
|
|
70541
70617
|
issueByChange.delete(changeName);
|
|
70618
|
+
workerMetaRef.current.delete(changeName);
|
|
70542
70619
|
return effectiveCode;
|
|
70543
70620
|
});
|
|
70544
70621
|
return { exited: wrapped, kill: () => proc.kill() };
|
|
@@ -70591,15 +70668,25 @@ ${stamped}
|
|
|
70591
70668
|
});
|
|
70592
70669
|
coordRef.current = coord2;
|
|
70593
70670
|
await coord2.init();
|
|
70671
|
+
const filterDesc = `team=${filter2.team ?? "*"}, assignee=${filter2.assignee ?? "*"}, statuses=${filter2.statuses?.length ? filter2.statuses.join(",") : "open"}${filter2.labels?.length ? `, labels=${filter2.labels.join(",")}` : ""}`;
|
|
70594
70672
|
const tick = async () => {
|
|
70595
70673
|
if (cancelled)
|
|
70596
70674
|
return;
|
|
70597
|
-
|
|
70598
|
-
appendLog(`\u2026 polling Linear (${filterDesc})`);
|
|
70675
|
+
setPollStatus((p) => ({ ...p, state: "polling", filterDesc }));
|
|
70599
70676
|
const { found, added } = await coord2.pollOnce();
|
|
70600
|
-
appendLog(` found ${found} open, ${added} new (queue=${coord2.queuedCount})`);
|
|
70601
70677
|
if (cancelled)
|
|
70602
70678
|
return;
|
|
70679
|
+
if (added > 0) {
|
|
70680
|
+
appendLog(` ${added} new issue${added === 1 ? "" : "s"} queued (found ${found} open)`);
|
|
70681
|
+
}
|
|
70682
|
+
setPollStatus({
|
|
70683
|
+
state: "idle",
|
|
70684
|
+
lastFound: found,
|
|
70685
|
+
lastAdded: added,
|
|
70686
|
+
lastAt: Date.now(),
|
|
70687
|
+
filterDesc
|
|
70688
|
+
});
|
|
70689
|
+
nextPollAtRef.current = Date.now() + pollInterval * 1000;
|
|
70603
70690
|
pollTimer = setTimeout(tick, pollInterval * 1000);
|
|
70604
70691
|
};
|
|
70605
70692
|
tick();
|
|
@@ -70624,7 +70711,34 @@ ${stamped}
|
|
|
70624
70711
|
process.off("SIGTERM", onSig);
|
|
70625
70712
|
};
|
|
70626
70713
|
}, []);
|
|
70714
|
+
import_react57.useEffect(() => {
|
|
70715
|
+
let cancelled = false;
|
|
70716
|
+
const interval = setInterval(() => {
|
|
70717
|
+
if (cancelled)
|
|
70718
|
+
return;
|
|
70719
|
+
(async () => {
|
|
70720
|
+
for (const [changeName, meta] of workerMetaRef.current) {
|
|
70721
|
+
try {
|
|
70722
|
+
const file = Bun.file(join14(meta.statesDir, changeName, ".ralph-state.json"));
|
|
70723
|
+
if (await file.exists()) {
|
|
70724
|
+
const json = await file.json();
|
|
70725
|
+
meta.iter = json.iteration ?? meta.iter;
|
|
70726
|
+
}
|
|
70727
|
+
} catch {}
|
|
70728
|
+
}
|
|
70729
|
+
if (!cancelled)
|
|
70730
|
+
setClock((c) => c + 1);
|
|
70731
|
+
})();
|
|
70732
|
+
}, 1000);
|
|
70733
|
+
return () => {
|
|
70734
|
+
cancelled = true;
|
|
70735
|
+
clearInterval(interval);
|
|
70736
|
+
};
|
|
70737
|
+
}, []);
|
|
70627
70738
|
const coord = coordRef.current;
|
|
70739
|
+
const spinnerFrame = SPINNER_FRAMES[clock % SPINNER_FRAMES.length];
|
|
70740
|
+
const now2 = Date.now();
|
|
70741
|
+
const secsToNextPoll = nextPollAtRef.current ? Math.max(0, Math.ceil((nextPollAtRef.current - now2) / 1000)) : null;
|
|
70628
70742
|
return /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
|
|
70629
70743
|
flexDirection: "column",
|
|
70630
70744
|
children: [
|
|
@@ -70644,23 +70758,41 @@ ${stamped}
|
|
|
70644
70758
|
/* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
|
|
70645
70759
|
dimColor: true,
|
|
70646
70760
|
children: [
|
|
70761
|
+
spinnerFrame,
|
|
70762
|
+
" ",
|
|
70763
|
+
pollStatus.state === "polling" ? `polling Linear (${pollStatus.filterDesc})` : pollStatus.lastAt !== null ? `last poll: ${pollStatus.lastFound} open, ${pollStatus.lastAdded} new${secsToNextPoll !== null ? ` \xB7 next in ${secsToNextPoll}s` : ""}` : "starting\u2026"
|
|
70764
|
+
]
|
|
70765
|
+
}, undefined, true, undefined, this),
|
|
70766
|
+
/* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
|
|
70767
|
+
dimColor: true,
|
|
70768
|
+
children: [
|
|
70769
|
+
" ",
|
|
70647
70770
|
"workers active: ",
|
|
70648
70771
|
coord?.activeCount ?? 0,
|
|
70649
70772
|
" \xB7 queued: ",
|
|
70650
70773
|
coord?.queuedCount ?? 0
|
|
70651
70774
|
]
|
|
70652
70775
|
}, undefined, true, undefined, this),
|
|
70653
|
-
coord?.activeWorkers.map((w) =>
|
|
70654
|
-
|
|
70655
|
-
|
|
70656
|
-
|
|
70657
|
-
|
|
70658
|
-
|
|
70659
|
-
|
|
70660
|
-
|
|
70661
|
-
|
|
70662
|
-
|
|
70663
|
-
|
|
70776
|
+
coord?.activeWorkers.map((w) => {
|
|
70777
|
+
const meta = workerMetaRef.current.get(w.changeName);
|
|
70778
|
+
const elapsed = meta ? fmtElapsed(now2 - meta.startedAt) : "\u2013";
|
|
70779
|
+
const iter = meta?.iter ?? 0;
|
|
70780
|
+
return /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
|
|
70781
|
+
color: "cyan",
|
|
70782
|
+
children: [
|
|
70783
|
+
" ",
|
|
70784
|
+
spinnerFrame,
|
|
70785
|
+
" ",
|
|
70786
|
+
w.issueIdentifier,
|
|
70787
|
+
" (",
|
|
70788
|
+
w.changeName,
|
|
70789
|
+
") \xB7 iter ",
|
|
70790
|
+
iter,
|
|
70791
|
+
" \xB7 ",
|
|
70792
|
+
elapsed
|
|
70793
|
+
]
|
|
70794
|
+
}, w.changeName, true, undefined, this);
|
|
70795
|
+
})
|
|
70664
70796
|
]
|
|
70665
70797
|
}, undefined, true, undefined, this)
|
|
70666
70798
|
]
|
|
@@ -70811,7 +70943,8 @@ function App2({ args, statesDir, tasksDir, projectRoot }) {
|
|
|
70811
70943
|
switch (args.mode) {
|
|
70812
70944
|
case "list":
|
|
70813
70945
|
return /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(TaskList, {
|
|
70814
|
-
statesDir
|
|
70946
|
+
statesDir,
|
|
70947
|
+
projectRoot
|
|
70815
70948
|
}, undefined, false, undefined, this);
|
|
70816
70949
|
case "agent":
|
|
70817
70950
|
return /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(AgentMode, {
|
|
@@ -70847,6 +70980,10 @@ function App2({ args, statesDir, tasksDir, projectRoot }) {
|
|
|
70847
70980
|
children: "Initialized openspec directory"
|
|
70848
70981
|
}, undefined, false, undefined, this)
|
|
70849
70982
|
}, undefined, false, undefined, this);
|
|
70983
|
+
case "clean":
|
|
70984
|
+
return /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(ExitAfterRender, {
|
|
70985
|
+
children: /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {}, undefined, false, undefined, this)
|
|
70986
|
+
}, undefined, false, undefined, this);
|
|
70850
70987
|
case "task": {
|
|
70851
70988
|
if (!args.name) {
|
|
70852
70989
|
return /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(ErrorMessage, {
|
|
@@ -70930,6 +71067,76 @@ try {
|
|
|
70930
71067
|
cwd: process.cwd()
|
|
70931
71068
|
});
|
|
70932
71069
|
}
|
|
71070
|
+
if (args.mode === "clean") {
|
|
71071
|
+
if (!args.name) {
|
|
71072
|
+
process.stderr.write(`Error: --name is required for clean mode
|
|
71073
|
+
`);
|
|
71074
|
+
process.exit(1);
|
|
71075
|
+
}
|
|
71076
|
+
const worktreeDir = join17(projectRoot, ".ralph", "worktrees", args.name);
|
|
71077
|
+
const changeDir = join17(tasksDir, args.name);
|
|
71078
|
+
const stateDir = join17(statesDir, args.name);
|
|
71079
|
+
const branch = `ralph/${args.name}`;
|
|
71080
|
+
const removed = [];
|
|
71081
|
+
if (await exists(worktreeDir)) {
|
|
71082
|
+
const proc = Bun.spawn({
|
|
71083
|
+
cmd: ["git", "worktree", "remove", "--force", worktreeDir],
|
|
71084
|
+
cwd: projectRoot,
|
|
71085
|
+
stdout: "pipe",
|
|
71086
|
+
stderr: "pipe"
|
|
71087
|
+
});
|
|
71088
|
+
const code = await proc.exited;
|
|
71089
|
+
if (code !== 0) {
|
|
71090
|
+
await rm(worktreeDir, { recursive: true, force: true });
|
|
71091
|
+
await Bun.spawn({
|
|
71092
|
+
cmd: ["git", "worktree", "prune"],
|
|
71093
|
+
cwd: projectRoot,
|
|
71094
|
+
stdout: "ignore",
|
|
71095
|
+
stderr: "ignore"
|
|
71096
|
+
}).exited;
|
|
71097
|
+
}
|
|
71098
|
+
removed.push(`worktree ${worktreeDir}`);
|
|
71099
|
+
}
|
|
71100
|
+
const branchProc = Bun.spawn({
|
|
71101
|
+
cmd: ["git", "branch", "-D", branch],
|
|
71102
|
+
cwd: projectRoot,
|
|
71103
|
+
stdout: "ignore",
|
|
71104
|
+
stderr: "ignore"
|
|
71105
|
+
});
|
|
71106
|
+
if (await branchProc.exited === 0)
|
|
71107
|
+
removed.push(`branch ${branch}`);
|
|
71108
|
+
if (await exists(changeDir)) {
|
|
71109
|
+
await rm(changeDir, { recursive: true, force: true });
|
|
71110
|
+
removed.push(`openspec change ${changeDir}`);
|
|
71111
|
+
}
|
|
71112
|
+
if (await exists(stateDir)) {
|
|
71113
|
+
await rm(stateDir, { recursive: true, force: true });
|
|
71114
|
+
removed.push(`task state ${stateDir}`);
|
|
71115
|
+
}
|
|
71116
|
+
try {
|
|
71117
|
+
const agentState = await readAgentState(projectRoot);
|
|
71118
|
+
const meta = agentState.changeMeta[args.name];
|
|
71119
|
+
if (meta) {
|
|
71120
|
+
agentState.processedIssueIds = agentState.processedIssueIds.filter((id) => id !== meta.issueId);
|
|
71121
|
+
agentState.startedIssueIds = agentState.startedIssueIds.filter((id) => id !== meta.issueId);
|
|
71122
|
+
delete agentState.changeMeta[args.name];
|
|
71123
|
+
await writeAgentState(projectRoot, agentState);
|
|
71124
|
+
removed.push(`agent-state entry for ${meta.identifier} (${meta.issueId})`);
|
|
71125
|
+
}
|
|
71126
|
+
} catch {}
|
|
71127
|
+
if (removed.length === 0) {
|
|
71128
|
+
process.stdout.write(`Nothing to clean for '${args.name}'
|
|
71129
|
+
`);
|
|
71130
|
+
} else {
|
|
71131
|
+
process.stdout.write(`Cleaned '${args.name}':
|
|
71132
|
+
`);
|
|
71133
|
+
for (const r of removed)
|
|
71134
|
+
process.stdout.write(` \u2713 removed ${r}
|
|
71135
|
+
`);
|
|
71136
|
+
}
|
|
71137
|
+
await shutdown();
|
|
71138
|
+
process.exit(0);
|
|
71139
|
+
}
|
|
70933
71140
|
if (args.mode === "task" && args.name) {
|
|
70934
71141
|
await mkdir3(join17(statesDir, args.name), { recursive: true });
|
|
70935
71142
|
await mkdir3(join17(tasksDir, args.name), { recursive: true });
|