ralphctl 0.4.4 → 0.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-OFILN7QL.mjs → chunk-B3RCOHW3.mjs} +101 -30
- package/dist/{chunk-ACRMBVEE.mjs → chunk-O566EEDL.mjs} +17 -7
- package/dist/cli.mjs +5 -5
- package/dist/{mount-VEV3TESX.mjs → mount-B3MLHNVY.mjs} +59 -15
- package/dist/{start-2WH4BTDB.mjs → start-FP7MVN5P.mjs} +1 -1
- package/package.json +1 -1
|
@@ -1626,6 +1626,14 @@ function resolveCheckScriptForRepo(repo) {
|
|
|
1626
1626
|
}
|
|
1627
1627
|
|
|
1628
1628
|
// src/business/pipelines/steps/run-check-scripts.ts
|
|
1629
|
+
var ERROR_OUTPUT_TAIL_LINES = 100;
|
|
1630
|
+
function tailOutput(output, maxLines = ERROR_OUTPUT_TAIL_LINES) {
|
|
1631
|
+
const lines = output.split("\n");
|
|
1632
|
+
if (lines.length <= maxLines) return output;
|
|
1633
|
+
const hidden = lines.length - maxLines;
|
|
1634
|
+
return `[${String(hidden)} earlier line${hidden !== 1 ? "s" : ""} omitted]
|
|
1635
|
+
${lines.slice(-maxLines).join("\n")}`;
|
|
1636
|
+
}
|
|
1629
1637
|
function runCheckScriptsStep(external, persistence, mode, options) {
|
|
1630
1638
|
return step("run-check-scripts", async (ctx) => {
|
|
1631
1639
|
const sprint = ctx.sprint;
|
|
@@ -1646,15 +1654,17 @@ function runCheckScriptsStep(external, persistence, mode, options) {
|
|
|
1646
1654
|
const checkScript = resolveCheckScriptForRepo(resolved?.repo);
|
|
1647
1655
|
if (!resolved || !checkScript) continue;
|
|
1648
1656
|
const { repo } = resolved;
|
|
1649
|
-
const result = external.runCheckScript(repo.path, checkScript, "sprintStart", repo.checkTimeout);
|
|
1657
|
+
const result = await external.runCheckScript(repo.path, checkScript, "sprintStart", repo.checkTimeout);
|
|
1650
1658
|
if (!result.passed) {
|
|
1651
1659
|
checkResults[repoId] = {
|
|
1652
1660
|
projectPath: repo.path,
|
|
1653
1661
|
success: false,
|
|
1654
1662
|
output: result.output
|
|
1655
1663
|
};
|
|
1656
|
-
return Result.error(
|
|
1657
|
-
${
|
|
1664
|
+
return Result.error(
|
|
1665
|
+
new StorageError(`Check failed for ${repo.path}: ${checkScript}
|
|
1666
|
+
${tailOutput(result.output)}`)
|
|
1667
|
+
);
|
|
1658
1668
|
}
|
|
1659
1669
|
const ranAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1660
1670
|
sprint.checkRanAt[repoId] = ranAt;
|
|
@@ -1680,7 +1690,7 @@ ${result.output}`));
|
|
|
1680
1690
|
return Result.ok(partial2);
|
|
1681
1691
|
}
|
|
1682
1692
|
const { repo } = resolved;
|
|
1683
|
-
const result = external.runCheckScript(repo.path, checkScript, "taskComplete", repo.checkTimeout);
|
|
1693
|
+
const result = await external.runCheckScript(repo.path, checkScript, "taskComplete", repo.checkTimeout);
|
|
1684
1694
|
checkResults[targetRepoId] = {
|
|
1685
1695
|
projectPath: repo.path,
|
|
1686
1696
|
success: result.passed,
|
|
@@ -1689,7 +1699,7 @@ ${result.output}`));
|
|
|
1689
1699
|
if (!result.passed) {
|
|
1690
1700
|
return Result.error(
|
|
1691
1701
|
new StorageError(`Post-task check failed for ${repo.path}: ${checkScript}
|
|
1692
|
-
${result.output}`)
|
|
1702
|
+
${tailOutput(result.output)}`)
|
|
1693
1703
|
);
|
|
1694
1704
|
}
|
|
1695
1705
|
}
|
|
@@ -1892,7 +1902,7 @@ ${instructions}`;
|
|
|
1892
1902
|
if (!resolved || !checkScript) return true;
|
|
1893
1903
|
this.logger.info(`Running post-task check: ${checkScript}`);
|
|
1894
1904
|
const { repo } = resolved;
|
|
1895
|
-
const result = this.external.runCheckScript(repo.path, checkScript, "taskComplete", repo.checkTimeout);
|
|
1905
|
+
const result = await this.external.runCheckScript(repo.path, checkScript, "taskComplete", repo.checkTimeout);
|
|
1896
1906
|
if (result.passed) {
|
|
1897
1907
|
this.logger.success("Post-task check: passed");
|
|
1898
1908
|
}
|
|
@@ -4608,8 +4618,9 @@ function describeMcpHint(name) {
|
|
|
4608
4618
|
}
|
|
4609
4619
|
|
|
4610
4620
|
// src/integration/external/lifecycle.ts
|
|
4611
|
-
import {
|
|
4621
|
+
import { spawn } from "child_process";
|
|
4612
4622
|
var DEFAULT_HOOK_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
4623
|
+
var MAX_OUTPUT_BYTES = 50 * 1024 * 1024;
|
|
4613
4624
|
function getHookTimeoutMs() {
|
|
4614
4625
|
const envVal = process.env["RALPHCTL_SETUP_TIMEOUT_MS"];
|
|
4615
4626
|
if (envVal) {
|
|
@@ -4621,16 +4632,76 @@ function getHookTimeoutMs() {
|
|
|
4621
4632
|
function runLifecycleHook(projectPath, script, event, timeoutOverrideMs) {
|
|
4622
4633
|
assertSafeCwd(projectPath);
|
|
4623
4634
|
const timeoutMs = timeoutOverrideMs ?? getHookTimeoutMs();
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
4635
|
+
return new Promise((resolve) => {
|
|
4636
|
+
const child = spawn(script, {
|
|
4637
|
+
cwd: projectPath,
|
|
4638
|
+
shell: true,
|
|
4639
|
+
detached: process.platform !== "win32",
|
|
4640
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
4641
|
+
env: { ...process.env, RALPHCTL_LIFECYCLE_EVENT: event }
|
|
4642
|
+
});
|
|
4643
|
+
const killTree = () => {
|
|
4644
|
+
if (process.platform !== "win32" && typeof child.pid === "number") {
|
|
4645
|
+
try {
|
|
4646
|
+
process.kill(-child.pid, "SIGTERM");
|
|
4647
|
+
return;
|
|
4648
|
+
} catch {
|
|
4649
|
+
}
|
|
4650
|
+
}
|
|
4651
|
+
try {
|
|
4652
|
+
child.kill("SIGTERM");
|
|
4653
|
+
} catch {
|
|
4654
|
+
}
|
|
4655
|
+
};
|
|
4656
|
+
const chunks = [];
|
|
4657
|
+
let totalBytes = 0;
|
|
4658
|
+
let timedOut = false;
|
|
4659
|
+
let capExceeded = false;
|
|
4660
|
+
let settled = false;
|
|
4661
|
+
const appendChunk = (chunk) => {
|
|
4662
|
+
if (capExceeded) return;
|
|
4663
|
+
totalBytes += chunk.length;
|
|
4664
|
+
if (totalBytes > MAX_OUTPUT_BYTES) {
|
|
4665
|
+
capExceeded = true;
|
|
4666
|
+
killTree();
|
|
4667
|
+
return;
|
|
4668
|
+
}
|
|
4669
|
+
chunks.push(chunk);
|
|
4670
|
+
};
|
|
4671
|
+
child.stdout.on("data", (chunk) => {
|
|
4672
|
+
appendChunk(chunk);
|
|
4673
|
+
});
|
|
4674
|
+
child.stderr.on("data", (chunk) => {
|
|
4675
|
+
appendChunk(chunk);
|
|
4676
|
+
});
|
|
4677
|
+
const timer = setTimeout(() => {
|
|
4678
|
+
timedOut = true;
|
|
4679
|
+
killTree();
|
|
4680
|
+
}, timeoutMs);
|
|
4681
|
+
const finish = (passed, suffix) => {
|
|
4682
|
+
if (settled) return;
|
|
4683
|
+
settled = true;
|
|
4684
|
+
clearTimeout(timer);
|
|
4685
|
+
const base = Buffer.concat(chunks).toString("utf-8").trim();
|
|
4686
|
+
const output = suffix ? base ? `${base}
|
|
4687
|
+
${suffix}` : suffix : base;
|
|
4688
|
+
resolve({ passed, output });
|
|
4689
|
+
};
|
|
4690
|
+
child.on("error", (err) => {
|
|
4691
|
+
finish(false, `[spawn error: ${err.message}]`);
|
|
4692
|
+
});
|
|
4693
|
+
child.on("close", (code) => {
|
|
4694
|
+
if (timedOut) {
|
|
4695
|
+
finish(false, `[timeout exceeded after ${String(timeoutMs)}ms]`);
|
|
4696
|
+
return;
|
|
4697
|
+
}
|
|
4698
|
+
if (capExceeded) {
|
|
4699
|
+
finish(false, `[output exceeded ${String(MAX_OUTPUT_BYTES)} byte cap \u2014 truncated]`);
|
|
4700
|
+
return;
|
|
4701
|
+
}
|
|
4702
|
+
finish(code === 0);
|
|
4703
|
+
});
|
|
4631
4704
|
});
|
|
4632
|
-
const output = [result.stdout, result.stderr].filter(Boolean).join("\n").trim();
|
|
4633
|
-
return { passed: result.status === 0, output };
|
|
4634
4705
|
}
|
|
4635
4706
|
|
|
4636
4707
|
// src/integration/ai/task-context.ts
|
|
@@ -4650,7 +4721,7 @@ function getRecentGitHistory(projectPath, count = 20) {
|
|
|
4650
4721
|
}
|
|
4651
4722
|
|
|
4652
4723
|
// src/integration/external/git.ts
|
|
4653
|
-
import { spawnSync
|
|
4724
|
+
import { spawnSync } from "child_process";
|
|
4654
4725
|
var BRANCH_NAME_RE = /^[a-zA-Z0-9/_.-]+$/;
|
|
4655
4726
|
var BRANCH_NAME_INVALID_PATTERNS = [/\.\./, /\.$/, /\/$/, /\.lock$/, /^-/, /\/\//];
|
|
4656
4727
|
function isValidBranchName(name) {
|
|
@@ -4663,7 +4734,7 @@ function isValidBranchName(name) {
|
|
|
4663
4734
|
}
|
|
4664
4735
|
function getCurrentBranch(cwd) {
|
|
4665
4736
|
assertSafeCwd(cwd);
|
|
4666
|
-
const result =
|
|
4737
|
+
const result = spawnSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
4667
4738
|
cwd,
|
|
4668
4739
|
encoding: "utf-8",
|
|
4669
4740
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -4678,7 +4749,7 @@ function branchExists(cwd, name) {
|
|
|
4678
4749
|
if (!isValidBranchName(name)) {
|
|
4679
4750
|
throw new Error(`Invalid branch name: ${name}`);
|
|
4680
4751
|
}
|
|
4681
|
-
const result =
|
|
4752
|
+
const result = spawnSync("git", ["show-ref", "--verify", `refs/heads/${name}`], {
|
|
4682
4753
|
cwd,
|
|
4683
4754
|
encoding: "utf-8",
|
|
4684
4755
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -4695,7 +4766,7 @@ function createAndCheckoutBranch(cwd, name) {
|
|
|
4695
4766
|
return;
|
|
4696
4767
|
}
|
|
4697
4768
|
if (branchExists(cwd, name)) {
|
|
4698
|
-
const result =
|
|
4769
|
+
const result = spawnSync("git", ["checkout", name], {
|
|
4699
4770
|
cwd,
|
|
4700
4771
|
encoding: "utf-8",
|
|
4701
4772
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -4704,7 +4775,7 @@ function createAndCheckoutBranch(cwd, name) {
|
|
|
4704
4775
|
throw new Error(`Failed to checkout branch '${name}' in ${cwd}: ${result.stderr.trim()}`);
|
|
4705
4776
|
}
|
|
4706
4777
|
} else {
|
|
4707
|
-
const result =
|
|
4778
|
+
const result = spawnSync("git", ["checkout", "-b", name], {
|
|
4708
4779
|
cwd,
|
|
4709
4780
|
encoding: "utf-8",
|
|
4710
4781
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -4720,7 +4791,7 @@ function verifyCurrentBranch(cwd, expected) {
|
|
|
4720
4791
|
}
|
|
4721
4792
|
function getDefaultBranch(cwd) {
|
|
4722
4793
|
assertSafeCwd(cwd);
|
|
4723
|
-
const result =
|
|
4794
|
+
const result = spawnSync("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
|
|
4724
4795
|
cwd,
|
|
4725
4796
|
encoding: "utf-8",
|
|
4726
4797
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -4741,7 +4812,7 @@ function getDefaultBranch(cwd) {
|
|
|
4741
4812
|
function getHeadSha(cwd) {
|
|
4742
4813
|
try {
|
|
4743
4814
|
assertSafeCwd(cwd);
|
|
4744
|
-
const result =
|
|
4815
|
+
const result = spawnSync("git", ["rev-parse", "HEAD"], {
|
|
4745
4816
|
cwd,
|
|
4746
4817
|
encoding: "utf-8",
|
|
4747
4818
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -4754,7 +4825,7 @@ function getHeadSha(cwd) {
|
|
|
4754
4825
|
}
|
|
4755
4826
|
function hasUncommittedChanges(cwd) {
|
|
4756
4827
|
assertSafeCwd(cwd);
|
|
4757
|
-
const result =
|
|
4828
|
+
const result = spawnSync("git", ["status", "--porcelain"], {
|
|
4758
4829
|
cwd,
|
|
4759
4830
|
encoding: "utf-8",
|
|
4760
4831
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -4766,7 +4837,7 @@ function hasUncommittedChanges(cwd) {
|
|
|
4766
4837
|
}
|
|
4767
4838
|
function hardResetWorkingTree(cwd) {
|
|
4768
4839
|
assertSafeCwd(cwd);
|
|
4769
|
-
const reset =
|
|
4840
|
+
const reset = spawnSync("git", ["reset", "--hard", "HEAD"], {
|
|
4770
4841
|
cwd,
|
|
4771
4842
|
encoding: "utf-8",
|
|
4772
4843
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -4774,7 +4845,7 @@ function hardResetWorkingTree(cwd) {
|
|
|
4774
4845
|
if (reset.status !== 0) {
|
|
4775
4846
|
throw new StorageError(`Failed to reset working tree in ${cwd}: ${reset.stderr.trim() || reset.stdout.trim()}`);
|
|
4776
4847
|
}
|
|
4777
|
-
const clean =
|
|
4848
|
+
const clean = spawnSync("git", ["clean", "-fd"], {
|
|
4778
4849
|
cwd,
|
|
4779
4850
|
encoding: "utf-8",
|
|
4780
4851
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -4785,7 +4856,7 @@ function hardResetWorkingTree(cwd) {
|
|
|
4785
4856
|
}
|
|
4786
4857
|
function autoCommit(cwd, message) {
|
|
4787
4858
|
assertSafeCwd(cwd);
|
|
4788
|
-
const add =
|
|
4859
|
+
const add = spawnSync("git", ["add", "-A"], {
|
|
4789
4860
|
cwd,
|
|
4790
4861
|
encoding: "utf-8",
|
|
4791
4862
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -4793,7 +4864,7 @@ function autoCommit(cwd, message) {
|
|
|
4793
4864
|
if (add.status !== 0) {
|
|
4794
4865
|
throw new Error(`Failed to stage changes in ${cwd}: ${add.stderr.trim()}`);
|
|
4795
4866
|
}
|
|
4796
|
-
const commit =
|
|
4867
|
+
const commit = spawnSync("git", ["commit", "-m", message], {
|
|
4797
4868
|
cwd,
|
|
4798
4869
|
encoding: "utf-8",
|
|
4799
4870
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -4806,14 +4877,14 @@ function generateBranchName(sprintId) {
|
|
|
4806
4877
|
return `ralphctl/${sprintId}`;
|
|
4807
4878
|
}
|
|
4808
4879
|
function isGhAvailable() {
|
|
4809
|
-
const result =
|
|
4880
|
+
const result = spawnSync("gh", ["--version"], {
|
|
4810
4881
|
encoding: "utf-8",
|
|
4811
4882
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4812
4883
|
});
|
|
4813
4884
|
return result.status === 0;
|
|
4814
4885
|
}
|
|
4815
4886
|
function isGlabAvailable() {
|
|
4816
|
-
const result =
|
|
4887
|
+
const result = spawnSync("glab", ["--version"], {
|
|
4817
4888
|
encoding: "utf-8",
|
|
4818
4889
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4819
4890
|
});
|
|
@@ -41,7 +41,7 @@ import {
|
|
|
41
41
|
updateTask,
|
|
42
42
|
updateTaskStatus,
|
|
43
43
|
validateImportTasks
|
|
44
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-B3RCOHW3.mjs";
|
|
45
45
|
import {
|
|
46
46
|
SignalParser,
|
|
47
47
|
buildTicketRefinePrompt,
|
|
@@ -176,6 +176,7 @@ import {
|
|
|
176
176
|
ProjectNotFoundError,
|
|
177
177
|
SprintNotFoundError,
|
|
178
178
|
SprintStatusError,
|
|
179
|
+
StepError,
|
|
179
180
|
StorageError,
|
|
180
181
|
TaskNotFoundError,
|
|
181
182
|
TicketNotFoundError
|
|
@@ -184,7 +185,7 @@ import {
|
|
|
184
185
|
// package.json
|
|
185
186
|
var package_default = {
|
|
186
187
|
name: "ralphctl",
|
|
187
|
-
version: "0.4.
|
|
188
|
+
version: "0.4.6",
|
|
188
189
|
description: "Agent harness for long-running AI coding tasks \u2014 orchestrates Claude Code & GitHub Copilot across repositories",
|
|
189
190
|
homepage: "https://github.com/lukas-grigis/ralphctl",
|
|
190
191
|
type: "module",
|
|
@@ -2068,12 +2069,20 @@ var InMemoryExecutionRegistry = class {
|
|
|
2068
2069
|
this.transition(executionId, "completed", summary ?? void 0);
|
|
2069
2070
|
}
|
|
2070
2071
|
} catch (err) {
|
|
2071
|
-
void err;
|
|
2072
2072
|
if (abortSignal.aborted) {
|
|
2073
2073
|
this.transition(executionId, "cancelled");
|
|
2074
|
-
|
|
2075
|
-
this.transition(executionId, "failed");
|
|
2074
|
+
return;
|
|
2076
2075
|
}
|
|
2076
|
+
const errInfo = err instanceof StepError ? { message: err.message, stepName: err.stepName } : { message: err instanceof Error ? err.message : String(err) };
|
|
2077
|
+
const entry = this.entries.get(executionId);
|
|
2078
|
+
entry?.logEventBus.emit({
|
|
2079
|
+
kind: "log",
|
|
2080
|
+
level: "error",
|
|
2081
|
+
message: errInfo.stepName ? `[${errInfo.stepName}] ${errInfo.message}` : errInfo.message,
|
|
2082
|
+
context: {},
|
|
2083
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
2084
|
+
});
|
|
2085
|
+
this.transition(executionId, "failed", void 0, errInfo);
|
|
2077
2086
|
}
|
|
2078
2087
|
}
|
|
2079
2088
|
get(id) {
|
|
@@ -2100,7 +2109,7 @@ var InMemoryExecutionRegistry = class {
|
|
|
2100
2109
|
getLogEventBus(id) {
|
|
2101
2110
|
return this.entries.get(id)?.logEventBus ?? null;
|
|
2102
2111
|
}
|
|
2103
|
-
transition(executionId, status, summary) {
|
|
2112
|
+
transition(executionId, status, summary, error2) {
|
|
2104
2113
|
const entry = this.entries.get(executionId);
|
|
2105
2114
|
if (!entry) return;
|
|
2106
2115
|
if (entry.execution.status === status) return;
|
|
@@ -2108,7 +2117,8 @@ var InMemoryExecutionRegistry = class {
|
|
|
2108
2117
|
...entry.execution,
|
|
2109
2118
|
status,
|
|
2110
2119
|
endedAt: /* @__PURE__ */ new Date(),
|
|
2111
|
-
summary: summary ?? entry.execution.summary
|
|
2120
|
+
summary: summary ?? entry.execution.summary,
|
|
2121
|
+
error: error2 ?? entry.execution.error
|
|
2112
2122
|
};
|
|
2113
2123
|
entry.execution = next;
|
|
2114
2124
|
this.notify(next);
|
package/dist/cli.mjs
CHANGED
|
@@ -41,7 +41,7 @@ import {
|
|
|
41
41
|
ticketRefineCommand,
|
|
42
42
|
ticketRemoveCommand,
|
|
43
43
|
ticketShowCommand
|
|
44
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-O566EEDL.mjs";
|
|
45
45
|
import {
|
|
46
46
|
projectAddCommand
|
|
47
47
|
} from "./chunk-PYZEQ2VK.mjs";
|
|
@@ -56,7 +56,7 @@ import {
|
|
|
56
56
|
executePipeline,
|
|
57
57
|
getTasks,
|
|
58
58
|
sprintStartCommand
|
|
59
|
-
} from "./chunk-
|
|
59
|
+
} from "./chunk-B3RCOHW3.mjs";
|
|
60
60
|
import "./chunk-ZLWSPLWI.mjs";
|
|
61
61
|
import {
|
|
62
62
|
truncate
|
|
@@ -756,7 +756,7 @@ async function main() {
|
|
|
756
756
|
const isBare = argv.length <= 2;
|
|
757
757
|
const isInteractive = argv[2] === "interactive";
|
|
758
758
|
if (isBare || isInteractive) {
|
|
759
|
-
const { mountInkApp } = await import("./mount-
|
|
759
|
+
const { mountInkApp } = await import("./mount-B3MLHNVY.mjs");
|
|
760
760
|
const { fallback } = await mountInkApp({ initialView: "repl" });
|
|
761
761
|
if (!fallback) return;
|
|
762
762
|
printBanner();
|
|
@@ -767,10 +767,10 @@ async function main() {
|
|
|
767
767
|
return;
|
|
768
768
|
}
|
|
769
769
|
if (argv[2] === "sprint" && argv[3] === "start") {
|
|
770
|
-
const { parseSprintStartArgs } = await import("./start-
|
|
770
|
+
const { parseSprintStartArgs } = await import("./start-FP7MVN5P.mjs");
|
|
771
771
|
const parsed = parseSprintStartArgs(argv.slice(4));
|
|
772
772
|
if (parsed.ok) {
|
|
773
|
-
const { mountInkApp } = await import("./mount-
|
|
773
|
+
const { mountInkApp } = await import("./mount-B3MLHNVY.mjs");
|
|
774
774
|
const { getSharedDeps: getSharedDeps2 } = await import("./bootstrap-FMHG6DRY.mjs");
|
|
775
775
|
let sprintId;
|
|
776
776
|
try {
|
|
@@ -62,7 +62,7 @@ import {
|
|
|
62
62
|
ticketRemoveCommand,
|
|
63
63
|
ticketShowCommand,
|
|
64
64
|
useCurrentPrompt
|
|
65
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-O566EEDL.mjs";
|
|
66
66
|
import {
|
|
67
67
|
PromptCancelledError,
|
|
68
68
|
detectCheckScriptCandidates,
|
|
@@ -99,7 +99,7 @@ import {
|
|
|
99
99
|
reorderTask,
|
|
100
100
|
sprintStartCommand,
|
|
101
101
|
updateTaskStatus
|
|
102
|
-
} from "./chunk-
|
|
102
|
+
} from "./chunk-B3RCOHW3.mjs";
|
|
103
103
|
import {
|
|
104
104
|
ProviderAiSessionAdapter,
|
|
105
105
|
SignalParser,
|
|
@@ -189,7 +189,7 @@ import { render } from "ink";
|
|
|
189
189
|
|
|
190
190
|
// src/integration/ui/tui/views/app.tsx
|
|
191
191
|
import { useEffect as useEffect37, useState as useState38 } from "react";
|
|
192
|
-
import { Box as Box37, useStdout } from "ink";
|
|
192
|
+
import { Box as Box37, useStdout as useStdout2 } from "ink";
|
|
193
193
|
|
|
194
194
|
// src/integration/ui/tui/views/view-router.tsx
|
|
195
195
|
import { useCallback as useCallback12, useMemo as useMemo34, useState as useState37 } from "react";
|
|
@@ -1606,7 +1606,7 @@ function SettingsView() {
|
|
|
1606
1606
|
|
|
1607
1607
|
// src/integration/ui/tui/views/execute-view.tsx
|
|
1608
1608
|
import { useEffect as useEffect8, useMemo as useMemo6, useRef as useRef2, useState as useState8 } from "react";
|
|
1609
|
-
import { Box as Box19, Text as Text18, useInput as useInput6 } from "ink";
|
|
1609
|
+
import { Box as Box19, Text as Text18, useInput as useInput6, useStdout } from "ink";
|
|
1610
1610
|
|
|
1611
1611
|
// src/integration/ui/tui/runtime/hooks.ts
|
|
1612
1612
|
import { useCallback as useCallback4, useEffect as useEffect6, useRef, useState as useState6 } from "react";
|
|
@@ -1908,8 +1908,12 @@ function renderLine(event, index, isActiveSpinner) {
|
|
|
1908
1908
|
}
|
|
1909
1909
|
}
|
|
1910
1910
|
}
|
|
1911
|
-
function LogTail({ events,
|
|
1912
|
-
const
|
|
1911
|
+
function LogTail({ events, visibleLines = 8, scrollOffset = 0 }) {
|
|
1912
|
+
const maxOffset = Math.max(0, events.length - visibleLines);
|
|
1913
|
+
const clampedOffset = Math.min(Math.max(0, scrollOffset), maxOffset);
|
|
1914
|
+
const end = events.length - clampedOffset;
|
|
1915
|
+
const start = Math.max(0, end - visibleLines);
|
|
1916
|
+
const window = events.slice(start, end);
|
|
1913
1917
|
const resolvedIds = useMemo5(() => {
|
|
1914
1918
|
const ids = /* @__PURE__ */ new Set();
|
|
1915
1919
|
for (const ev of events) {
|
|
@@ -1919,9 +1923,29 @@ function LogTail({ events, limit = 8 }) {
|
|
|
1919
1923
|
}
|
|
1920
1924
|
return ids;
|
|
1921
1925
|
}, [events]);
|
|
1926
|
+
const scrolledUp = clampedOffset > 0;
|
|
1927
|
+
const hiddenAbove = start;
|
|
1928
|
+
const hiddenBelow = events.length - end;
|
|
1922
1929
|
return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", children: [
|
|
1923
|
-
/* @__PURE__ */
|
|
1924
|
-
|
|
1930
|
+
/* @__PURE__ */ jsxs14(Box15, { children: [
|
|
1931
|
+
/* @__PURE__ */ jsx17(Text14, { dimColor: true, children: "\u2500\u2500 Log \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" }),
|
|
1932
|
+
scrolledUp ? /* @__PURE__ */ jsxs14(Text14, { dimColor: true, children: [
|
|
1933
|
+
" ",
|
|
1934
|
+
glyphs.arrowRight,
|
|
1935
|
+
" ",
|
|
1936
|
+
hiddenAbove,
|
|
1937
|
+
" line",
|
|
1938
|
+
hiddenAbove !== 1 ? "s" : "",
|
|
1939
|
+
" above",
|
|
1940
|
+
hiddenBelow > 0 ? ` \xB7 ${String(hiddenBelow)} below` : ""
|
|
1941
|
+
] }) : events.length > visibleLines ? /* @__PURE__ */ jsxs14(Text14, { dimColor: true, children: [
|
|
1942
|
+
" ",
|
|
1943
|
+
"(",
|
|
1944
|
+
events.length - visibleLines,
|
|
1945
|
+
" hidden)"
|
|
1946
|
+
] }) : null
|
|
1947
|
+
] }),
|
|
1948
|
+
window.length === 0 ? /* @__PURE__ */ jsx17(Text14, { dimColor: true, children: "(no activity yet)" }) : window.map((event, i) => {
|
|
1925
1949
|
const active = event.kind === "spinner-start" && !resolvedIds.has(event.id);
|
|
1926
1950
|
return renderLine(event, i, active);
|
|
1927
1951
|
})
|
|
@@ -1998,9 +2022,13 @@ function ResultCard({ kind, title, fields, nextSteps, lines }) {
|
|
|
1998
2022
|
}
|
|
1999
2023
|
|
|
2000
2024
|
// src/integration/ui/tui/views/execute-view.tsx
|
|
2001
|
-
import { jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2025
|
+
import { Fragment, jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2002
2026
|
var EXECUTE_HINTS_RUNNING = [{ key: "c", action: "cancel" }];
|
|
2003
2027
|
var EXECUTE_HINTS_TERMINAL = [{ key: "Enter", action: "back" }];
|
|
2028
|
+
var LOG_TAIL_FIXED_OVERHEAD = 20;
|
|
2029
|
+
var LOG_TAIL_MIN_LINES = 3;
|
|
2030
|
+
var LOG_TAIL_DEFAULT_LINES = 8;
|
|
2031
|
+
var ERROR_MESSAGE_MAX_LINES = 20;
|
|
2004
2032
|
function initialState() {
|
|
2005
2033
|
return {
|
|
2006
2034
|
sprint: null,
|
|
@@ -2026,6 +2054,7 @@ function ExecuteView({ sprintId, executionId, executionOptions }) {
|
|
|
2026
2054
|
const registryEvents = useRegistryEvents(registry);
|
|
2027
2055
|
const [attach, setAttach] = useState8(initialAttach);
|
|
2028
2056
|
const [state, setState] = useState8(initialState);
|
|
2057
|
+
const { stdout } = useStdout();
|
|
2029
2058
|
const processedCountRef = useRef2(0);
|
|
2030
2059
|
const startedRef = useRef2(false);
|
|
2031
2060
|
const attachedId = attach.execution?.id ?? null;
|
|
@@ -2122,6 +2151,7 @@ function ExecuteView({ sprintId, executionId, executionOptions }) {
|
|
|
2122
2151
|
})();
|
|
2123
2152
|
}
|
|
2124
2153
|
}, [signalEvents, shared, sprintId]);
|
|
2154
|
+
const logVisibleLines = stdout.rows ? Math.max(LOG_TAIL_MIN_LINES, stdout.rows - LOG_TAIL_FIXED_OVERHEAD) : LOG_TAIL_DEFAULT_LINES;
|
|
2125
2155
|
const status = liveExecution?.status ?? "running";
|
|
2126
2156
|
const terminal = status === "completed" || status === "failed" || status === "cancelled";
|
|
2127
2157
|
const [closePromptRun, setClosePromptRun] = useState8(false);
|
|
@@ -2190,6 +2220,7 @@ function ExecuteView({ sprintId, executionId, executionOptions }) {
|
|
|
2190
2220
|
if (attach.kind === "attaching") {
|
|
2191
2221
|
return /* @__PURE__ */ jsx20(ViewShell, { title: "Execute", children: /* @__PURE__ */ jsx20(Spinner, { label: "Attaching to execution\u2026" }) });
|
|
2192
2222
|
}
|
|
2223
|
+
const errorCard = terminal && liveExecution?.status === "failed" && liveExecution.error ? buildErrorCard(liveExecution.error) : null;
|
|
2193
2224
|
return /* @__PURE__ */ jsxs18(ViewShell, { title: "Execute", children: [
|
|
2194
2225
|
/* @__PURE__ */ jsxs18(Box19, { children: [
|
|
2195
2226
|
/* @__PURE__ */ jsx20(Text18, { bold: true, color: inkColors.primary, children: state.sprint?.name ?? "Sprint" }),
|
|
@@ -2216,8 +2247,8 @@ function ExecuteView({ sprintId, executionId, executionOptions }) {
|
|
|
2216
2247
|
const taskName = task?.name ?? taskId.slice(0, 8);
|
|
2217
2248
|
return /* @__PURE__ */ jsx20(Spinner, { label: `${taskName} ${glyphs.emDash} ${label}` }, taskId);
|
|
2218
2249
|
}) }) : null,
|
|
2219
|
-
/* @__PURE__ */ jsx20(Box19, { marginTop: spacing.section, children: /* @__PURE__ */ jsx20(LogTail, { events: logEvents }) }),
|
|
2220
|
-
terminal && liveExecution ? /* @__PURE__ */
|
|
2250
|
+
/* @__PURE__ */ jsx20(Box19, { marginTop: spacing.section, children: /* @__PURE__ */ jsx20(LogTail, { events: logEvents, visibleLines: logVisibleLines, scrollOffset: 0 }) }),
|
|
2251
|
+
terminal && liveExecution ? /* @__PURE__ */ jsx20(Box19, { marginTop: spacing.section, flexDirection: "column", children: errorCard ? /* @__PURE__ */ jsx20(ResultCard, { kind: "error", title: "Execution failed", fields: errorCard.fields, lines: errorCard.lines }) : /* @__PURE__ */ jsxs18(Fragment, { children: [
|
|
2221
2252
|
/* @__PURE__ */ jsxs18(Text18, { color: terminalColor(liveExecution.status), bold: true, children: [
|
|
2222
2253
|
terminalGlyph(liveExecution.status),
|
|
2223
2254
|
" Execution ",
|
|
@@ -2229,8 +2260,8 @@ function ExecuteView({ sprintId, executionId, executionOptions }) {
|
|
|
2229
2260
|
glyphs.inlineDot,
|
|
2230
2261
|
" ",
|
|
2231
2262
|
liveExecution.summary.remaining,
|
|
2232
|
-
" remaining",
|
|
2233
2263
|
" ",
|
|
2264
|
+
"remaining ",
|
|
2234
2265
|
glyphs.inlineDot,
|
|
2235
2266
|
" ",
|
|
2236
2267
|
liveExecution.summary.blocked,
|
|
@@ -2239,7 +2270,7 @@ function ExecuteView({ sprintId, executionId, executionOptions }) {
|
|
|
2239
2270
|
liveExecution.summary.stopReason,
|
|
2240
2271
|
")"
|
|
2241
2272
|
] }) : null
|
|
2242
|
-
] }) : null
|
|
2273
|
+
] }) }) : null
|
|
2243
2274
|
] });
|
|
2244
2275
|
}
|
|
2245
2276
|
function terminalColor(status) {
|
|
@@ -2252,6 +2283,19 @@ function terminalGlyph(status) {
|
|
|
2252
2283
|
if (status === "failed") return glyphs.cross;
|
|
2253
2284
|
return glyphs.warningGlyph;
|
|
2254
2285
|
}
|
|
2286
|
+
function buildErrorCard(error) {
|
|
2287
|
+
const fields = error.stepName ? [["Step", error.stepName]] : void 0;
|
|
2288
|
+
const rawLines = error.message.split("\n");
|
|
2289
|
+
if (rawLines.length <= ERROR_MESSAGE_MAX_LINES) {
|
|
2290
|
+
return { fields, lines: rawLines };
|
|
2291
|
+
}
|
|
2292
|
+
const hidden = rawLines.length - ERROR_MESSAGE_MAX_LINES;
|
|
2293
|
+
const visibleLines = [
|
|
2294
|
+
`(${String(hidden)} earlier line${hidden !== 1 ? "s" : ""} omitted)`,
|
|
2295
|
+
...rawLines.slice(-ERROR_MESSAGE_MAX_LINES)
|
|
2296
|
+
];
|
|
2297
|
+
return { fields, lines: visibleLines };
|
|
2298
|
+
}
|
|
2255
2299
|
function CollisionRedirect({ registry, collisionId, fallbackSprintId }) {
|
|
2256
2300
|
const router = useRouter();
|
|
2257
2301
|
useInput6((_input, key) => {
|
|
@@ -6887,7 +6931,7 @@ import { Text as Text36 } from "ink";
|
|
|
6887
6931
|
// src/integration/external/version-check.ts
|
|
6888
6932
|
import { mkdir, readFile, rename, writeFile as writeFile2 } from "fs/promises";
|
|
6889
6933
|
import { dirname, join as join3 } from "path";
|
|
6890
|
-
var CACHE_TTL_MS =
|
|
6934
|
+
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
6891
6935
|
var FETCH_TIMEOUT_MS = 3e3;
|
|
6892
6936
|
var REGISTRY_URL = "https://registry.npmjs.org/ralphctl/latest";
|
|
6893
6937
|
function getCachePath() {
|
|
@@ -7311,7 +7355,7 @@ function buildInitialStack(initialView, mountOptions) {
|
|
|
7311
7355
|
return [{ id: "home" }];
|
|
7312
7356
|
}
|
|
7313
7357
|
function useTerminalWidth() {
|
|
7314
|
-
const { stdout } =
|
|
7358
|
+
const { stdout } = useStdout2();
|
|
7315
7359
|
const [width, setWidth] = useState38(stdout.columns);
|
|
7316
7360
|
useEffect37(() => {
|
|
7317
7361
|
const onResize = () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ralphctl",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.6",
|
|
4
4
|
"description": "Agent harness for long-running AI coding tasks — orchestrates Claude Code & GitHub Copilot across repositories",
|
|
5
5
|
"homepage": "https://github.com/lukas-grigis/ralphctl",
|
|
6
6
|
"type": "module",
|