ralphctl 0.7.1 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +265 -232
- package/dist/manifest.json +1 -1
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -2667,24 +2667,16 @@ var buildCodexArgs = (session, opts) => {
|
|
|
2667
2667
|
const perms = sandboxFor(session.permissions);
|
|
2668
2668
|
if (!perms.ok) return Result.error(perms.error);
|
|
2669
2669
|
const args = ["exec"];
|
|
2670
|
-
|
|
2670
|
+
const isResume = session.resume !== void 0;
|
|
2671
|
+
if (isResume) {
|
|
2671
2672
|
args.push("resume", String(session.resume));
|
|
2672
2673
|
}
|
|
2673
|
-
args.push(
|
|
2674
|
-
|
|
2675
|
-
"
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
"-m",
|
|
2680
|
-
session.model,
|
|
2681
|
-
"-C",
|
|
2682
|
-
String(session.cwd),
|
|
2683
|
-
"-s",
|
|
2684
|
-
perms.value.sandbox
|
|
2685
|
-
);
|
|
2686
|
-
for (const root of session.additionalRoots ?? []) {
|
|
2687
|
-
args.push("--add-dir", String(root));
|
|
2674
|
+
args.push("--ephemeral", "--skip-git-repo-check", "-o", opts.outputFile, "--json", "-m", session.model);
|
|
2675
|
+
if (!isResume) {
|
|
2676
|
+
args.push("-C", String(session.cwd), "-s", perms.value.sandbox);
|
|
2677
|
+
for (const root of session.additionalRoots ?? []) {
|
|
2678
|
+
args.push("--add-dir", String(root));
|
|
2679
|
+
}
|
|
2688
2680
|
}
|
|
2689
2681
|
if (opts.reasoningEffort !== void 0) {
|
|
2690
2682
|
args.push("-c", `model_reasoning_effort=${opts.reasoningEffort}`);
|
|
@@ -2862,6 +2854,18 @@ var spawnAttempt2 = async (input) => {
|
|
|
2862
2854
|
const signals = parseHarnessSignals(body, IsoTimestamp.now());
|
|
2863
2855
|
const wrote = await writeJsonAtomic(String(session.signalsFile), signals);
|
|
2864
2856
|
if (!wrote.ok) return { kind: "error", error: wrote.error };
|
|
2857
|
+
if (session.bodyFile !== void 0) {
|
|
2858
|
+
const bodyWrote = await writeTextAtomic(String(session.bodyFile), body);
|
|
2859
|
+
if (!bodyWrote.ok) {
|
|
2860
|
+
deps.eventBus.publish({
|
|
2861
|
+
type: "log",
|
|
2862
|
+
level: "warn",
|
|
2863
|
+
message: `codex-provider: failed to write body file \u2014 diagnostic capture skipped`,
|
|
2864
|
+
meta: { bodyFile: String(session.bodyFile), error: bodyWrote.error.message },
|
|
2865
|
+
at: IsoTimestamp.now()
|
|
2866
|
+
});
|
|
2867
|
+
}
|
|
2868
|
+
}
|
|
2865
2869
|
return {
|
|
2866
2870
|
kind: "success",
|
|
2867
2871
|
output: {
|
|
@@ -3958,11 +3962,7 @@ var defaultTemplatesDir = () => TEMPLATES_DIR;
|
|
|
3958
3962
|
var isNodeErrnoCode2 = (cause, code) => typeof cause === "object" && cause !== null && cause.code === code;
|
|
3959
3963
|
|
|
3960
3964
|
// src/integration/ai/readiness/claude/probe.ts
|
|
3961
|
-
import {
|
|
3962
|
-
import { basename, join as join7 } from "path";
|
|
3963
|
-
|
|
3964
|
-
// src/domain/value/kebab-case.ts
|
|
3965
|
-
var toKebabCase = (input) => input.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replaceAll(/^-+|-+$/g, "");
|
|
3965
|
+
import { join as join8 } from "path";
|
|
3966
3966
|
|
|
3967
3967
|
// src/domain/value/error/probe-error.ts
|
|
3968
3968
|
var ProbeError = class extends Error {
|
|
@@ -3983,66 +3983,22 @@ var ProbeError = class extends Error {
|
|
|
3983
3983
|
}
|
|
3984
3984
|
};
|
|
3985
3985
|
|
|
3986
|
-
// src/integration/ai/readiness/_engine/
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
var presentState = (evaluatedAt, artifacts) => ({
|
|
3990
|
-
kind: "present",
|
|
3991
|
-
evaluatedAt,
|
|
3992
|
-
artifacts
|
|
3993
|
-
});
|
|
3986
|
+
// src/integration/ai/readiness/_engine/probe-fs.ts
|
|
3987
|
+
import { promises as fs8 } from "fs";
|
|
3988
|
+
import { basename, join as join7 } from "path";
|
|
3994
3989
|
|
|
3995
|
-
// src/
|
|
3996
|
-
var
|
|
3997
|
-
var hasAnyClaudeArtifact = (a) => a.claudeMd !== void 0 || a.agentsMd !== void 0 || a.settings !== void 0 || a.settingsLocal !== void 0 || a.mcpConfig !== void 0 || a.skills.length > 0 || a.commands.length > 0 || a.agents.length > 0 || a.hooks.length > 0;
|
|
3998
|
-
var hasAnyCopilotArtifact = (a) => a.copilotInstructions !== void 0;
|
|
3990
|
+
// src/domain/value/kebab-case.ts
|
|
3991
|
+
var toKebabCase = (input) => input.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replaceAll(/^-+|-+$/g, "");
|
|
3999
3992
|
|
|
4000
|
-
// src/integration/ai/readiness/
|
|
4001
|
-
var claudeProbe = {
|
|
4002
|
-
tool: "claude-code",
|
|
4003
|
-
async evaluate(repository, now) {
|
|
4004
|
-
const root = repository.path;
|
|
4005
|
-
const claudeMd = await probeFile(join7(root, "CLAUDE.md"));
|
|
4006
|
-
if (!claudeMd.ok) return Result.error(claudeMd.error);
|
|
4007
|
-
const agentsMd = await probeFile(join7(root, "AGENTS.md"));
|
|
4008
|
-
if (!agentsMd.ok) return Result.error(agentsMd.error);
|
|
4009
|
-
const settings = await probeFile(join7(root, ".claude/settings.json"));
|
|
4010
|
-
if (!settings.ok) return Result.error(settings.error);
|
|
4011
|
-
const settingsLocal = await probeFile(join7(root, ".claude/settings.local.json"));
|
|
4012
|
-
if (!settingsLocal.ok) return Result.error(settingsLocal.error);
|
|
4013
|
-
const mcpConfig = await probeFile(join7(root, ".mcp.json"));
|
|
4014
|
-
if (!mcpConfig.ok) return Result.error(mcpConfig.error);
|
|
4015
|
-
const skills = await probeNamedDirCollection(join7(root, ".claude/skills"), "SKILL.md");
|
|
4016
|
-
if (!skills.ok) return Result.error(skills.error);
|
|
4017
|
-
const commands = await probeNamedFileCollection(join7(root, ".claude/commands"));
|
|
4018
|
-
if (!commands.ok) return Result.error(commands.error);
|
|
4019
|
-
const agents = await probeNamedFileCollection(join7(root, ".claude/agents"));
|
|
4020
|
-
if (!agents.ok) return Result.error(agents.error);
|
|
4021
|
-
const hooks = await readHooks([settings.value, settingsLocal.value]);
|
|
4022
|
-
if (!hooks.ok) return Result.error(hooks.error);
|
|
4023
|
-
const artifacts = {
|
|
4024
|
-
tool: "claude-code",
|
|
4025
|
-
...claudeMd.value !== void 0 ? { claudeMd: claudeMd.value } : {},
|
|
4026
|
-
...agentsMd.value !== void 0 ? { agentsMd: agentsMd.value } : {},
|
|
4027
|
-
...settings.value !== void 0 ? { settings: settings.value } : {},
|
|
4028
|
-
...settingsLocal.value !== void 0 ? { settingsLocal: settingsLocal.value } : {},
|
|
4029
|
-
...mcpConfig.value !== void 0 ? { mcpConfig: mcpConfig.value } : {},
|
|
4030
|
-
skills: skills.value,
|
|
4031
|
-
commands: commands.value,
|
|
4032
|
-
agents: agents.value,
|
|
4033
|
-
hooks: hooks.value
|
|
4034
|
-
};
|
|
4035
|
-
return Result.ok(hasAnyClaudeArtifact(artifacts) ? presentState(now, artifacts) : absentState(now));
|
|
4036
|
-
}
|
|
4037
|
-
};
|
|
3993
|
+
// src/integration/ai/readiness/_engine/probe-fs.ts
|
|
4038
3994
|
var probeFile = async (path) => {
|
|
4039
3995
|
try {
|
|
4040
3996
|
const stat = await fs8.stat(path);
|
|
4041
3997
|
if (!stat.isFile()) return Result.ok(void 0);
|
|
4042
3998
|
return Result.ok({ path });
|
|
4043
3999
|
} catch (cause) {
|
|
4044
|
-
if (
|
|
4045
|
-
if (
|
|
4000
|
+
if (isNodeErrnoCode(cause, "ENOENT")) return Result.ok(void 0);
|
|
4001
|
+
if (isNodeErrnoCode(cause, "EACCES")) {
|
|
4046
4002
|
return Result.error(
|
|
4047
4003
|
new ProbeError({ subCode: "fs-permission", message: `permission denied reading ${path}`, path, cause })
|
|
4048
4004
|
);
|
|
@@ -4050,23 +4006,6 @@ var probeFile = async (path) => {
|
|
|
4050
4006
|
return Result.error(new ProbeError({ subCode: "fs-read", message: `failed to stat ${path}`, path, cause }));
|
|
4051
4007
|
}
|
|
4052
4008
|
};
|
|
4053
|
-
var probeNamedFileCollection = async (dir) => {
|
|
4054
|
-
const entries = await listDir2(dir);
|
|
4055
|
-
if (!entries.ok) return Result.error(entries.error);
|
|
4056
|
-
const refs = [];
|
|
4057
|
-
for (const entry of entries.value) {
|
|
4058
|
-
if (!entry.endsWith(".md")) continue;
|
|
4059
|
-
const full = join7(dir, entry);
|
|
4060
|
-
const stat = await statSafely(full);
|
|
4061
|
-
if (!stat.ok) return Result.error(stat.error);
|
|
4062
|
-
if (stat.value === void 0 || !stat.value.isFile()) continue;
|
|
4063
|
-
const baseName = entry.slice(0, -".md".length);
|
|
4064
|
-
const slug = Slug.parse(toKebabCase(baseName));
|
|
4065
|
-
if (!slug.ok) continue;
|
|
4066
|
-
refs.push({ name: slug.value, path: full });
|
|
4067
|
-
}
|
|
4068
|
-
return Result.ok(refs);
|
|
4069
|
-
};
|
|
4070
4009
|
var probeNamedDirCollection = async (dir, childMarker) => {
|
|
4071
4010
|
const entries = await listDir2(dir);
|
|
4072
4011
|
if (!entries.ok) return Result.error(entries.error);
|
|
@@ -4086,12 +4025,29 @@ var probeNamedDirCollection = async (dir, childMarker) => {
|
|
|
4086
4025
|
}
|
|
4087
4026
|
return Result.ok(refs);
|
|
4088
4027
|
};
|
|
4028
|
+
var probeNamedFileCollection = async (dir) => {
|
|
4029
|
+
const entries = await listDir2(dir);
|
|
4030
|
+
if (!entries.ok) return Result.error(entries.error);
|
|
4031
|
+
const refs = [];
|
|
4032
|
+
for (const entry of entries.value) {
|
|
4033
|
+
if (!entry.endsWith(".md")) continue;
|
|
4034
|
+
const full = join7(dir, entry);
|
|
4035
|
+
const stat = await statSafely(full);
|
|
4036
|
+
if (!stat.ok) return Result.error(stat.error);
|
|
4037
|
+
if (stat.value === void 0 || !stat.value.isFile()) continue;
|
|
4038
|
+
const baseName = entry.slice(0, -".md".length);
|
|
4039
|
+
const slug = Slug.parse(toKebabCase(baseName));
|
|
4040
|
+
if (!slug.ok) continue;
|
|
4041
|
+
refs.push({ name: slug.value, path: full });
|
|
4042
|
+
}
|
|
4043
|
+
return Result.ok(refs);
|
|
4044
|
+
};
|
|
4089
4045
|
var listDir2 = async (dir) => {
|
|
4090
4046
|
try {
|
|
4091
4047
|
return Result.ok(await fs8.readdir(dir));
|
|
4092
4048
|
} catch (cause) {
|
|
4093
|
-
if (
|
|
4094
|
-
if (
|
|
4049
|
+
if (isNodeErrnoCode(cause, "ENOENT") || isNodeErrnoCode(cause, "ENOTDIR")) return Result.ok([]);
|
|
4050
|
+
if (isNodeErrnoCode(cause, "EACCES")) {
|
|
4095
4051
|
return Result.error(
|
|
4096
4052
|
new ProbeError({ subCode: "fs-permission", message: `permission denied listing ${dir}`, path: dir, cause })
|
|
4097
4053
|
);
|
|
@@ -4103,8 +4059,8 @@ var statSafely = async (path) => {
|
|
|
4103
4059
|
try {
|
|
4104
4060
|
return Result.ok(await fs8.stat(path));
|
|
4105
4061
|
} catch (cause) {
|
|
4106
|
-
if (
|
|
4107
|
-
if (
|
|
4062
|
+
if (isNodeErrnoCode(cause, "ENOENT")) return Result.ok(void 0);
|
|
4063
|
+
if (isNodeErrnoCode(cause, "EACCES")) {
|
|
4108
4064
|
return Result.error(
|
|
4109
4065
|
new ProbeError({ subCode: "fs-permission", message: `permission denied stat ${path}`, path, cause })
|
|
4110
4066
|
);
|
|
@@ -4112,6 +4068,73 @@ var statSafely = async (path) => {
|
|
|
4112
4068
|
return Result.error(new ProbeError({ subCode: "fs-read", message: `failed to stat ${path}`, path, cause }));
|
|
4113
4069
|
}
|
|
4114
4070
|
};
|
|
4071
|
+
var readFileSafely = async (path) => {
|
|
4072
|
+
try {
|
|
4073
|
+
return Result.ok(await fs8.readFile(path, "utf8"));
|
|
4074
|
+
} catch (cause) {
|
|
4075
|
+
if (isNodeErrnoCode(cause, "ENOENT")) return Result.ok(void 0);
|
|
4076
|
+
if (isNodeErrnoCode(cause, "EACCES")) {
|
|
4077
|
+
return Result.error(
|
|
4078
|
+
new ProbeError({ subCode: "fs-permission", message: `permission denied reading ${path}`, path, cause })
|
|
4079
|
+
);
|
|
4080
|
+
}
|
|
4081
|
+
return Result.error(new ProbeError({ subCode: "fs-read", message: `failed to read ${path}`, path, cause }));
|
|
4082
|
+
}
|
|
4083
|
+
};
|
|
4084
|
+
|
|
4085
|
+
// src/integration/ai/readiness/_engine/state.ts
|
|
4086
|
+
var unknownState = { kind: "unknown" };
|
|
4087
|
+
var absentState = (evaluatedAt) => ({ kind: "absent", evaluatedAt });
|
|
4088
|
+
var presentState = (evaluatedAt, artifacts) => ({
|
|
4089
|
+
kind: "present",
|
|
4090
|
+
evaluatedAt,
|
|
4091
|
+
artifacts
|
|
4092
|
+
});
|
|
4093
|
+
|
|
4094
|
+
// src/integration/ai/readiness/_engine/predicates.ts
|
|
4095
|
+
var isPresent = (state) => state.kind === "present";
|
|
4096
|
+
var hasAnyClaudeArtifact = (a) => a.claudeMd !== void 0 || a.agentsMd !== void 0 || a.settings !== void 0 || a.settingsLocal !== void 0 || a.mcpConfig !== void 0 || a.skills.length > 0 || a.commands.length > 0 || a.agents.length > 0 || a.hooks.length > 0;
|
|
4097
|
+
var hasAnyCopilotArtifact = (a) => a.copilotInstructions !== void 0;
|
|
4098
|
+
var hasAnyCodexArtifact = (a) => a.agentsMd !== void 0 || a.skills.length > 0;
|
|
4099
|
+
|
|
4100
|
+
// src/integration/ai/readiness/claude/probe.ts
|
|
4101
|
+
var claudeProbe = {
|
|
4102
|
+
tool: "claude-code",
|
|
4103
|
+
async evaluate(repository, now) {
|
|
4104
|
+
const root = repository.path;
|
|
4105
|
+
const claudeMd = await probeFile(join8(root, "CLAUDE.md"));
|
|
4106
|
+
if (!claudeMd.ok) return Result.error(claudeMd.error);
|
|
4107
|
+
const agentsMd = await probeFile(join8(root, "AGENTS.md"));
|
|
4108
|
+
if (!agentsMd.ok) return Result.error(agentsMd.error);
|
|
4109
|
+
const settings = await probeFile(join8(root, ".claude/settings.json"));
|
|
4110
|
+
if (!settings.ok) return Result.error(settings.error);
|
|
4111
|
+
const settingsLocal = await probeFile(join8(root, ".claude/settings.local.json"));
|
|
4112
|
+
if (!settingsLocal.ok) return Result.error(settingsLocal.error);
|
|
4113
|
+
const mcpConfig = await probeFile(join8(root, ".mcp.json"));
|
|
4114
|
+
if (!mcpConfig.ok) return Result.error(mcpConfig.error);
|
|
4115
|
+
const skills = await probeNamedDirCollection(join8(root, ".claude/skills"), "SKILL.md");
|
|
4116
|
+
if (!skills.ok) return Result.error(skills.error);
|
|
4117
|
+
const commands = await probeNamedFileCollection(join8(root, ".claude/commands"));
|
|
4118
|
+
if (!commands.ok) return Result.error(commands.error);
|
|
4119
|
+
const agents = await probeNamedFileCollection(join8(root, ".claude/agents"));
|
|
4120
|
+
if (!agents.ok) return Result.error(agents.error);
|
|
4121
|
+
const hooks = await readHooks([settings.value, settingsLocal.value]);
|
|
4122
|
+
if (!hooks.ok) return Result.error(hooks.error);
|
|
4123
|
+
const artifacts = {
|
|
4124
|
+
tool: "claude-code",
|
|
4125
|
+
...claudeMd.value !== void 0 ? { claudeMd: claudeMd.value } : {},
|
|
4126
|
+
...agentsMd.value !== void 0 ? { agentsMd: agentsMd.value } : {},
|
|
4127
|
+
...settings.value !== void 0 ? { settings: settings.value } : {},
|
|
4128
|
+
...settingsLocal.value !== void 0 ? { settingsLocal: settingsLocal.value } : {},
|
|
4129
|
+
...mcpConfig.value !== void 0 ? { mcpConfig: mcpConfig.value } : {},
|
|
4130
|
+
skills: skills.value,
|
|
4131
|
+
commands: commands.value,
|
|
4132
|
+
agents: agents.value,
|
|
4133
|
+
hooks: hooks.value
|
|
4134
|
+
};
|
|
4135
|
+
return Result.ok(hasAnyClaudeArtifact(artifacts) ? presentState(now, artifacts) : absentState(now));
|
|
4136
|
+
}
|
|
4137
|
+
};
|
|
4115
4138
|
var readHooks = async (settingsRefs) => {
|
|
4116
4139
|
const hooks = [];
|
|
4117
4140
|
for (const ref2 of settingsRefs) {
|
|
@@ -4131,19 +4154,6 @@ var readHooks = async (settingsRefs) => {
|
|
|
4131
4154
|
}
|
|
4132
4155
|
return Result.ok(hooks);
|
|
4133
4156
|
};
|
|
4134
|
-
var readFileSafely = async (path) => {
|
|
4135
|
-
try {
|
|
4136
|
-
return Result.ok(await fs8.readFile(path, "utf8"));
|
|
4137
|
-
} catch (cause) {
|
|
4138
|
-
if (isNodeErrnoCode3(cause, "ENOENT")) return Result.ok(void 0);
|
|
4139
|
-
if (isNodeErrnoCode3(cause, "EACCES")) {
|
|
4140
|
-
return Result.error(
|
|
4141
|
-
new ProbeError({ subCode: "fs-permission", message: `permission denied reading ${path}`, path, cause })
|
|
4142
|
-
);
|
|
4143
|
-
}
|
|
4144
|
-
return Result.error(new ProbeError({ subCode: "fs-read", message: `failed to read ${path}`, path, cause }));
|
|
4145
|
-
}
|
|
4146
|
-
};
|
|
4147
4157
|
var extractHooks = (settings, sink) => {
|
|
4148
4158
|
if (typeof settings !== "object" || settings === null) return;
|
|
4149
4159
|
const hooks = settings.hooks;
|
|
@@ -4164,33 +4174,41 @@ var extractHooks = (settings, sink) => {
|
|
|
4164
4174
|
}
|
|
4165
4175
|
}
|
|
4166
4176
|
};
|
|
4167
|
-
var isNodeErrnoCode3 = (cause, code) => typeof cause === "object" && cause !== null && cause.code === code;
|
|
4168
4177
|
|
|
4169
4178
|
// src/integration/ai/readiness/codex/probe.ts
|
|
4179
|
+
import { join as join9 } from "path";
|
|
4170
4180
|
var codexProbe = {
|
|
4171
4181
|
tool: "codex",
|
|
4172
|
-
evaluate(repository, now) {
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
return
|
|
4182
|
+
async evaluate(repository, now) {
|
|
4183
|
+
const root = repository.path;
|
|
4184
|
+
const agentsMd = await probeFile(join9(root, "AGENTS.md"));
|
|
4185
|
+
if (!agentsMd.ok) return Result.error(agentsMd.error);
|
|
4186
|
+
const skills = await probeNamedDirCollection(join9(root, ".agents/skills"), "SKILL.md");
|
|
4187
|
+
if (!skills.ok) return Result.error(skills.error);
|
|
4188
|
+
const artifacts = {
|
|
4189
|
+
tool: "codex",
|
|
4190
|
+
...agentsMd.value !== void 0 ? { agentsMd: agentsMd.value } : {},
|
|
4191
|
+
skills: skills.value
|
|
4192
|
+
};
|
|
4193
|
+
return Result.ok(hasAnyCodexArtifact(artifacts) ? presentState(now, artifacts) : absentState(now));
|
|
4176
4194
|
}
|
|
4177
4195
|
};
|
|
4178
4196
|
|
|
4179
4197
|
// src/integration/ai/readiness/copilot/probe.ts
|
|
4180
4198
|
import { promises as fs9 } from "fs";
|
|
4181
|
-
import { join as
|
|
4199
|
+
import { join as join10 } from "path";
|
|
4182
4200
|
var copilotProbe = {
|
|
4183
4201
|
tool: "copilot",
|
|
4184
4202
|
async evaluate(repository, now) {
|
|
4185
|
-
const path =
|
|
4203
|
+
const path = join10(repository.path, ".github/copilot-instructions.md");
|
|
4186
4204
|
try {
|
|
4187
4205
|
const stat = await fs9.stat(path);
|
|
4188
4206
|
if (!stat.isFile()) return Result.ok(absentState(now));
|
|
4189
4207
|
const artifacts = { tool: "copilot", copilotInstructions: { path } };
|
|
4190
4208
|
return Result.ok(hasAnyCopilotArtifact(artifacts) ? presentState(now, artifacts) : absentState(now));
|
|
4191
4209
|
} catch (cause) {
|
|
4192
|
-
if (
|
|
4193
|
-
if (
|
|
4210
|
+
if (isNodeErrnoCode(cause, "ENOENT")) return Result.ok(absentState(now));
|
|
4211
|
+
if (isNodeErrnoCode(cause, "EACCES")) {
|
|
4194
4212
|
return Result.error(
|
|
4195
4213
|
new ProbeError({ subCode: "fs-permission", message: `permission denied reading ${path}`, path, cause })
|
|
4196
4214
|
);
|
|
@@ -4199,7 +4217,6 @@ var copilotProbe = {
|
|
|
4199
4217
|
}
|
|
4200
4218
|
}
|
|
4201
4219
|
};
|
|
4202
|
-
var isNodeErrnoCode4 = (cause, code) => typeof cause === "object" && cause !== null && cause.code === code;
|
|
4203
4220
|
|
|
4204
4221
|
// src/integration/observability/in-memory-event-bus.ts
|
|
4205
4222
|
var createInMemoryEventBus = () => {
|
|
@@ -4247,7 +4264,7 @@ var loggerForScope = (deps, scope) => {
|
|
|
4247
4264
|
};
|
|
4248
4265
|
|
|
4249
4266
|
// src/integration/version/npm-version-checker.ts
|
|
4250
|
-
import { join as
|
|
4267
|
+
import { join as join11 } from "path";
|
|
4251
4268
|
import { z as z15 } from "zod";
|
|
4252
4269
|
|
|
4253
4270
|
// src/business/version/version-check.ts
|
|
@@ -4289,7 +4306,7 @@ var buildVersionCheck = (current, latest, now) => ({
|
|
|
4289
4306
|
var DEFAULT_CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
4290
4307
|
var DEFAULT_FETCH_TIMEOUT_MS = 3e3;
|
|
4291
4308
|
var RegistryPayloadSchema = z15.object({ version: z15.string() });
|
|
4292
|
-
var cachePath = (stateRoot) =>
|
|
4309
|
+
var cachePath = (stateRoot) => join11(String(stateRoot), "version-check.json");
|
|
4293
4310
|
var registryUrl = (packageName) => `https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`;
|
|
4294
4311
|
var shouldSkip = (env) => env["NO_NETWORK"] !== void 0 || env["VITEST"] !== void 0;
|
|
4295
4312
|
var readCache = async (path) => {
|
|
@@ -4343,7 +4360,7 @@ var createNpmVersionChecker = (deps) => {
|
|
|
4343
4360
|
// package.json
|
|
4344
4361
|
var package_default = {
|
|
4345
4362
|
name: "ralphctl",
|
|
4346
|
-
version: "0.7.
|
|
4363
|
+
version: "0.7.2",
|
|
4347
4364
|
description: "Agent harness for long-running AI coding tasks \u2014 orchestrates Claude Code & GitHub Copilot across repositories",
|
|
4348
4365
|
homepage: "https://github.com/lukas-grigis/ralphctl",
|
|
4349
4366
|
type: "module",
|
|
@@ -4445,11 +4462,11 @@ var CLI_METADATA = {
|
|
|
4445
4462
|
// src/integration/ai/skills/_engine/filesystem-skills-adapter.ts
|
|
4446
4463
|
import { existsSync } from "fs";
|
|
4447
4464
|
import { mkdir, rm, rmdir, writeFile } from "fs/promises";
|
|
4448
|
-
import { join as
|
|
4465
|
+
import { join as join13 } from "path";
|
|
4449
4466
|
|
|
4450
4467
|
// src/integration/io/git-exclude.ts
|
|
4451
4468
|
import { promises as fs10 } from "fs";
|
|
4452
|
-
import { isAbsolute as isAbsolute2, join as
|
|
4469
|
+
import { isAbsolute as isAbsolute2, join as join12 } from "path";
|
|
4453
4470
|
var ensureGitExcludeWildcard = async (repoRoot, pattern) => {
|
|
4454
4471
|
const resolved = await resolveExcludePath(String(repoRoot));
|
|
4455
4472
|
if (resolved === void 0) return Result.ok(void 0);
|
|
@@ -4457,7 +4474,7 @@ var ensureGitExcludeWildcard = async (repoRoot, pattern) => {
|
|
|
4457
4474
|
try {
|
|
4458
4475
|
existing = await fs10.readFile(resolved, "utf8");
|
|
4459
4476
|
} catch (cause) {
|
|
4460
|
-
if (
|
|
4477
|
+
if (isNodeErrnoCode3(cause, "ENOENT")) {
|
|
4461
4478
|
} else {
|
|
4462
4479
|
return Result.error(
|
|
4463
4480
|
new StorageError({
|
|
@@ -4478,16 +4495,16 @@ var ensureGitExcludeWildcard = async (repoRoot, pattern) => {
|
|
|
4478
4495
|
return writeTextAtomic(resolved, next);
|
|
4479
4496
|
};
|
|
4480
4497
|
var resolveExcludePath = async (repoRoot) => {
|
|
4481
|
-
const gitMarker =
|
|
4498
|
+
const gitMarker = join12(repoRoot, ".git");
|
|
4482
4499
|
let stat;
|
|
4483
4500
|
try {
|
|
4484
4501
|
stat = await fs10.stat(gitMarker);
|
|
4485
4502
|
} catch (cause) {
|
|
4486
|
-
if (
|
|
4503
|
+
if (isNodeErrnoCode3(cause, "ENOENT") || isNodeErrnoCode3(cause, "ENOTDIR")) return void 0;
|
|
4487
4504
|
throw cause;
|
|
4488
4505
|
}
|
|
4489
4506
|
if (stat.isDirectory()) {
|
|
4490
|
-
return
|
|
4507
|
+
return join12(gitMarker, "info", "exclude");
|
|
4491
4508
|
}
|
|
4492
4509
|
if (!stat.isFile()) return void 0;
|
|
4493
4510
|
let pointer;
|
|
@@ -4499,10 +4516,10 @@ var resolveExcludePath = async (repoRoot) => {
|
|
|
4499
4516
|
const match = /^gitdir:\s*(.+)\s*$/m.exec(pointer);
|
|
4500
4517
|
if (match === null) return void 0;
|
|
4501
4518
|
const gitdir = match[1].trim();
|
|
4502
|
-
const absoluteGitdir = isAbsolute2(gitdir) ? gitdir :
|
|
4503
|
-
return
|
|
4519
|
+
const absoluteGitdir = isAbsolute2(gitdir) ? gitdir : join12(repoRoot, gitdir);
|
|
4520
|
+
return join12(absoluteGitdir, "info", "exclude");
|
|
4504
4521
|
};
|
|
4505
|
-
var
|
|
4522
|
+
var isNodeErrnoCode3 = (cause, code) => typeof cause === "object" && cause !== null && cause.code === code;
|
|
4506
4523
|
|
|
4507
4524
|
// src/integration/ai/skills/_engine/filesystem-skills-adapter.ts
|
|
4508
4525
|
var renderSkill = (skill) => {
|
|
@@ -4525,7 +4542,7 @@ var tryRmdirIfEmpty = async (path) => {
|
|
|
4525
4542
|
var createFilesystemSkillsAdapter = (deps) => {
|
|
4526
4543
|
const installed = /* @__PURE__ */ new Map();
|
|
4527
4544
|
const excludeAttempted = /* @__PURE__ */ new Set();
|
|
4528
|
-
const skillsSubdir =
|
|
4545
|
+
const skillsSubdir = join13(deps.parentDir, "skills");
|
|
4529
4546
|
const excludePattern = `${skillsSubdir}/ralphctl-*`;
|
|
4530
4547
|
const pruneStale = () => {
|
|
4531
4548
|
for (const key of [...installed.keys()]) {
|
|
@@ -4535,14 +4552,14 @@ var createFilesystemSkillsAdapter = (deps) => {
|
|
|
4535
4552
|
return {
|
|
4536
4553
|
async install(sessionDir, skills) {
|
|
4537
4554
|
pruneStale();
|
|
4538
|
-
const skillsDir =
|
|
4555
|
+
const skillsDir = join13(String(sessionDir), skillsSubdir);
|
|
4539
4556
|
const tracked = installed.get(String(sessionDir)) ?? /* @__PURE__ */ new Set();
|
|
4540
4557
|
for (const skill of skills) {
|
|
4541
|
-
const dst =
|
|
4558
|
+
const dst = join13(skillsDir, skill.name);
|
|
4542
4559
|
if (existsSync(dst)) continue;
|
|
4543
4560
|
try {
|
|
4544
4561
|
await mkdir(dst, { recursive: true });
|
|
4545
|
-
await writeFile(
|
|
4562
|
+
await writeFile(join13(dst, "SKILL.md"), renderSkill(skill), "utf-8");
|
|
4546
4563
|
tracked.add(skill.name);
|
|
4547
4564
|
} catch (cause) {
|
|
4548
4565
|
if (tracked.size > 0) installed.set(String(sessionDir), tracked);
|
|
@@ -4573,10 +4590,10 @@ var createFilesystemSkillsAdapter = (deps) => {
|
|
|
4573
4590
|
const key = String(sessionDir);
|
|
4574
4591
|
const tracked = installed.get(key);
|
|
4575
4592
|
if (tracked === void 0 || tracked.size === 0) return Result.ok(void 0);
|
|
4576
|
-
const skillsDir =
|
|
4593
|
+
const skillsDir = join13(key, skillsSubdir);
|
|
4577
4594
|
try {
|
|
4578
4595
|
for (const id of tracked) {
|
|
4579
|
-
await rm(
|
|
4596
|
+
await rm(join13(skillsDir, id), { recursive: true, force: true });
|
|
4580
4597
|
}
|
|
4581
4598
|
installed.delete(key);
|
|
4582
4599
|
} catch (cause) {
|
|
@@ -4590,7 +4607,7 @@ var createFilesystemSkillsAdapter = (deps) => {
|
|
|
4590
4607
|
);
|
|
4591
4608
|
}
|
|
4592
4609
|
await tryRmdirIfEmpty(skillsDir);
|
|
4593
|
-
await tryRmdirIfEmpty(
|
|
4610
|
+
await tryRmdirIfEmpty(join13(key, deps.parentDir));
|
|
4594
4611
|
return Result.ok(void 0);
|
|
4595
4612
|
}
|
|
4596
4613
|
};
|
|
@@ -4652,7 +4669,7 @@ var createSkillsAdapter = (deps) => {
|
|
|
4652
4669
|
};
|
|
4653
4670
|
|
|
4654
4671
|
// src/integration/ai/skills/bundled/source.ts
|
|
4655
|
-
import { dirname as dirname7, join as
|
|
4672
|
+
import { dirname as dirname7, join as join14 } from "path";
|
|
4656
4673
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4657
4674
|
import { readFile } from "fs/promises";
|
|
4658
4675
|
|
|
@@ -4681,7 +4698,7 @@ var skillsForFlow = (flowId) => FLOW_SKILLS[flowId];
|
|
|
4681
4698
|
var defaultBundledRoot = (() => {
|
|
4682
4699
|
const here = dirname7(fileURLToPath2(import.meta.url));
|
|
4683
4700
|
const isBundled = import.meta.url.endsWith("/cli.mjs") || import.meta.url.endsWith("\\cli.mjs");
|
|
4684
|
-
return isBundled ?
|
|
4701
|
+
return isBundled ? join14(here, "skills") : here;
|
|
4685
4702
|
})();
|
|
4686
4703
|
var splitFrontmatter = (raw) => {
|
|
4687
4704
|
const trimmed = raw.replace(/^\uFEFF/u, "");
|
|
@@ -4709,7 +4726,7 @@ var parseSimpleYaml = (input) => {
|
|
|
4709
4726
|
return result;
|
|
4710
4727
|
};
|
|
4711
4728
|
var readSkill = async (root, name) => {
|
|
4712
|
-
const path =
|
|
4729
|
+
const path = join14(root, name, "SKILL.md");
|
|
4713
4730
|
let raw;
|
|
4714
4731
|
try {
|
|
4715
4732
|
raw = await readFile(path, "utf-8");
|
|
@@ -5699,13 +5716,13 @@ var probeGitConfig = async (id, label, key, runCommand2, hint) => {
|
|
|
5699
5716
|
}
|
|
5700
5717
|
return { id, label, status: "warn", detail: `${key} not set`, hint, group: "vcs" };
|
|
5701
5718
|
};
|
|
5702
|
-
var probeCliAuth = async (id, label, binary, args, hint, runCommand2) => {
|
|
5719
|
+
var probeCliAuth = async (id, label, binary, args, hint, runCommand2, group = "vcs") => {
|
|
5703
5720
|
const r = await runCommand2(binary, args);
|
|
5704
5721
|
if (r.ok) {
|
|
5705
|
-
return { id, label, status: "pass", detail: "authenticated", group
|
|
5722
|
+
return { id, label, status: "pass", detail: "authenticated", group };
|
|
5706
5723
|
}
|
|
5707
5724
|
const detail = r.stderr.split("\n").find((line) => line.trim().length > 0)?.trim() ?? "not authenticated";
|
|
5708
|
-
return { id, label, status: "warn", detail, hint, group
|
|
5725
|
+
return { id, label, status: "warn", detail, hint, group };
|
|
5709
5726
|
};
|
|
5710
5727
|
var createDoctorFlow = (deps) => leaf("doctor", {
|
|
5711
5728
|
useCase: {
|
|
@@ -5817,6 +5834,7 @@ var createDoctorFlow = (deps) => leaf("doctor", {
|
|
|
5817
5834
|
}
|
|
5818
5835
|
const settings = await deps.settingsRepo.load();
|
|
5819
5836
|
const configuredProvider = settings.ok ? settings.value.ai.provider : void 0;
|
|
5837
|
+
let codexInstalled = false;
|
|
5820
5838
|
for (const provider of Object.keys(PROVIDER_BINARY)) {
|
|
5821
5839
|
const binary = PROVIDER_BINARY[provider];
|
|
5822
5840
|
const isConfigured = provider === configuredProvider;
|
|
@@ -5828,12 +5846,26 @@ var createDoctorFlow = (deps) => leaf("doctor", {
|
|
|
5828
5846
|
deps.commandExists,
|
|
5829
5847
|
`install the '${binary}' CLI and ensure it is on your PATH`
|
|
5830
5848
|
);
|
|
5849
|
+
if (provider === "openai-codex") codexInstalled = probe.status === "pass";
|
|
5831
5850
|
if (probe.status === "fail") {
|
|
5832
5851
|
probes.push({ ...probe, status: "warn" });
|
|
5833
5852
|
} else {
|
|
5834
5853
|
probes.push(probe);
|
|
5835
5854
|
}
|
|
5836
5855
|
}
|
|
5856
|
+
if (configuredProvider === "openai-codex" && codexInstalled) {
|
|
5857
|
+
probes.push(
|
|
5858
|
+
await probeCliAuth(
|
|
5859
|
+
"codex-auth",
|
|
5860
|
+
"OpenAI Codex CLI authenticated",
|
|
5861
|
+
"codex",
|
|
5862
|
+
["login", "status"],
|
|
5863
|
+
"run `codex login` to sign in",
|
|
5864
|
+
deps.runCommand,
|
|
5865
|
+
"ai"
|
|
5866
|
+
)
|
|
5867
|
+
);
|
|
5868
|
+
}
|
|
5837
5869
|
const projects = await deps.projectRepo.list();
|
|
5838
5870
|
probes.push({
|
|
5839
5871
|
id: "projects-list",
|
|
@@ -9176,14 +9208,14 @@ var launchAddTickets = (ctx) => {
|
|
|
9176
9208
|
};
|
|
9177
9209
|
|
|
9178
9210
|
// src/application/ui/shared/launch/refine.ts
|
|
9179
|
-
import { join as
|
|
9211
|
+
import { join as join17 } from "path";
|
|
9180
9212
|
|
|
9181
9213
|
// src/application/flows/refine/flow.ts
|
|
9182
|
-
import { join as
|
|
9214
|
+
import { join as join16 } from "path";
|
|
9183
9215
|
|
|
9184
9216
|
// src/application/flows/_shared/build-unit.ts
|
|
9185
9217
|
import { promises as fs11 } from "fs";
|
|
9186
|
-
import { join as
|
|
9218
|
+
import { join as join15 } from "path";
|
|
9187
9219
|
var buildUnitLeaf = (opts) => leaf(opts.name, {
|
|
9188
9220
|
useCase: {
|
|
9189
9221
|
execute: async (input) => {
|
|
@@ -9204,7 +9236,7 @@ var buildUnitLeaf = (opts) => leaf(opts.name, {
|
|
|
9204
9236
|
return Result.ok(parsed.value);
|
|
9205
9237
|
}
|
|
9206
9238
|
},
|
|
9207
|
-
input: (ctx) => ({ path:
|
|
9239
|
+
input: (ctx) => ({ path: join15(String(opts.parent(ctx)), opts.slug(ctx)) }),
|
|
9208
9240
|
output: (ctx, root) => opts.write(ctx, root)
|
|
9209
9241
|
});
|
|
9210
9242
|
|
|
@@ -9666,8 +9698,8 @@ var createRefineFlow = (deps, opts) => {
|
|
|
9666
9698
|
parent: () => opts.refinementRoot,
|
|
9667
9699
|
slug: () => ticketSlug(ticket),
|
|
9668
9700
|
write: (ctx, root) => {
|
|
9669
|
-
const promptPath = AbsolutePath.parse(
|
|
9670
|
-
const outputPath = AbsolutePath.parse(
|
|
9701
|
+
const promptPath = AbsolutePath.parse(join16(String(root), "prompt.md"));
|
|
9702
|
+
const outputPath = AbsolutePath.parse(join16(String(root), "requirements.md"));
|
|
9671
9703
|
if (!promptPath.ok || !outputPath.ok) {
|
|
9672
9704
|
throw promptPath.ok ? outputPath.ok ? new Error("unreachable") : outputPath.error : promptPath.error;
|
|
9673
9705
|
}
|
|
@@ -9768,7 +9800,7 @@ var launchRefine = async (ctx) => {
|
|
|
9768
9800
|
if (!snapshot.sprint) return { ok: false, reason: "No sprint selected." };
|
|
9769
9801
|
const pending = snapshot.sprint.tickets.filter((t) => t.status === "pending");
|
|
9770
9802
|
const refinementRoot = AbsolutePath.parse(
|
|
9771
|
-
|
|
9803
|
+
join17(String(deps.storage.dataRoot), "sprints", String(snapshot.sprint.id), "refinement")
|
|
9772
9804
|
);
|
|
9773
9805
|
if (!refinementRoot.ok) return { ok: false, reason: refinementRoot.error.message };
|
|
9774
9806
|
let defaultIssueOrigin = snapshot.project?.defaultIssueOrigin;
|
|
@@ -9841,10 +9873,10 @@ ${body.trim()}`;
|
|
|
9841
9873
|
};
|
|
9842
9874
|
|
|
9843
9875
|
// src/application/ui/shared/launch/plan.ts
|
|
9844
|
-
import { join as
|
|
9876
|
+
import { join as join19 } from "path";
|
|
9845
9877
|
|
|
9846
9878
|
// src/application/flows/plan/flow.ts
|
|
9847
|
-
import { join as
|
|
9879
|
+
import { join as join18 } from "path";
|
|
9848
9880
|
|
|
9849
9881
|
// src/application/flows/_shared/sprint/load-execution.ts
|
|
9850
9882
|
var loadSprintExecutionLeaf = (deps, name = "load-sprint-execution") => leaf(name, {
|
|
@@ -10585,8 +10617,8 @@ var createPlanFlow = (deps, opts) => {
|
|
|
10585
10617
|
parent: () => opts.planRoot,
|
|
10586
10618
|
slug: () => slug,
|
|
10587
10619
|
write: (ctx, root) => {
|
|
10588
|
-
const promptPath = AbsolutePath.parse(
|
|
10589
|
-
const outputPath = AbsolutePath.parse(
|
|
10620
|
+
const promptPath = AbsolutePath.parse(join18(String(root), "prompt.md"));
|
|
10621
|
+
const outputPath = AbsolutePath.parse(join18(String(root), "plan.json"));
|
|
10590
10622
|
if (!promptPath.ok) throw promptPath.error;
|
|
10591
10623
|
if (!outputPath.ok) throw outputPath.error;
|
|
10592
10624
|
return {
|
|
@@ -10676,7 +10708,7 @@ var launchPlan = (ctx) => {
|
|
|
10676
10708
|
if (!snapshot.project) return { ok: false, reason: "No project loaded." };
|
|
10677
10709
|
if (!snapshot.sprint) return { ok: false, reason: "No sprint selected." };
|
|
10678
10710
|
const planRoot = AbsolutePath.parse(
|
|
10679
|
-
|
|
10711
|
+
join19(String(deps.storage.dataRoot), "sprints", String(snapshot.sprint.id), "plan")
|
|
10680
10712
|
);
|
|
10681
10713
|
if (!planRoot.ok) return { ok: false, reason: planRoot.error.message };
|
|
10682
10714
|
const reviewBeforeApprove = async (proposedTasks) => {
|
|
@@ -10730,7 +10762,7 @@ ${summary}`;
|
|
|
10730
10762
|
};
|
|
10731
10763
|
|
|
10732
10764
|
// src/application/ui/shared/launch/implement.ts
|
|
10733
|
-
import { join as
|
|
10765
|
+
import { join as join23 } from "path";
|
|
10734
10766
|
|
|
10735
10767
|
// src/application/chain/build/guard.ts
|
|
10736
10768
|
var guard = (name, predicate, body) => ({
|
|
@@ -11643,9 +11675,9 @@ var implementSession = (sandboxCwd, repoPath, prompt, model, signalsFile) => ({
|
|
|
11643
11675
|
});
|
|
11644
11676
|
|
|
11645
11677
|
// src/application/flows/implement/leaves/round-artifacts.ts
|
|
11646
|
-
import { join as
|
|
11678
|
+
import { join as join20 } from "path";
|
|
11647
11679
|
var nextRoundNum = async (workspaceRoot) => {
|
|
11648
|
-
const entries = await listDir(
|
|
11680
|
+
const entries = await listDir(join20(String(workspaceRoot), "rounds"));
|
|
11649
11681
|
if (!entries.ok) return 1;
|
|
11650
11682
|
let max = 0;
|
|
11651
11683
|
for (const name of entries.value) {
|
|
@@ -11654,12 +11686,12 @@ var nextRoundNum = async (workspaceRoot) => {
|
|
|
11654
11686
|
}
|
|
11655
11687
|
return max + 1;
|
|
11656
11688
|
};
|
|
11657
|
-
var roundSignalsPath = (workspaceRoot, round, role) =>
|
|
11658
|
-
var roundEvaluationRelativePath = (round) =>
|
|
11659
|
-
var roundDir = (workspaceRoot, round, role) =>
|
|
11689
|
+
var roundSignalsPath = (workspaceRoot, round, role) => join20(String(workspaceRoot), "rounds", String(round), role, "signals.json");
|
|
11690
|
+
var roundEvaluationRelativePath = (round) => join20("rounds", String(round), "evaluator", "evaluation.md");
|
|
11691
|
+
var roundDir = (workspaceRoot, round, role) => join20(String(workspaceRoot), "rounds", String(round), role);
|
|
11660
11692
|
var writeEvaluatorRoundArtifacts = async (workspaceRoot, round, signals, logger) => {
|
|
11661
11693
|
const base = roundDir(workspaceRoot, round, "evaluator");
|
|
11662
|
-
const evaluation = await writeTextAtomic(
|
|
11694
|
+
const evaluation = await writeTextAtomic(join20(base, "evaluation.md"), renderEvaluation(findEvaluation2(signals)));
|
|
11663
11695
|
if (!evaluation.ok) {
|
|
11664
11696
|
logger?.warn("failed to write evaluator round artifact", { round, base, error: evaluation.error.message });
|
|
11665
11697
|
}
|
|
@@ -12783,7 +12815,7 @@ var startAttemptLeaf = (deps, taskId) => leaf(
|
|
|
12783
12815
|
|
|
12784
12816
|
// src/application/flows/implement/leaves/build-task-workspace.ts
|
|
12785
12817
|
import { promises as fs16 } from "fs";
|
|
12786
|
-
import { join as
|
|
12818
|
+
import { join as join21 } from "path";
|
|
12787
12819
|
var renderDoneCriteria = (task) => {
|
|
12788
12820
|
const header = `# Done criteria \u2014 ${task.name}
|
|
12789
12821
|
|
|
@@ -12816,7 +12848,7 @@ var buildTaskWorkspaceLeaf = (deps, opts, taskId) => leaf(`build-task-workspace-
|
|
|
12816
12848
|
useCase: {
|
|
12817
12849
|
execute: async (input) => {
|
|
12818
12850
|
const log = deps.logger.named("implement.workspace");
|
|
12819
|
-
const workspaceRoot =
|
|
12851
|
+
const workspaceRoot = join21(String(opts.sprintDir), "implement", String(input.task.id));
|
|
12820
12852
|
const prompt = await buildImplementPrompt(deps.templateLoader, {
|
|
12821
12853
|
task: input.task,
|
|
12822
12854
|
projectPath: String(opts.cwd),
|
|
@@ -12824,10 +12856,10 @@ var buildTaskWorkspaceLeaf = (deps, opts, taskId) => leaf(`build-task-workspace-
|
|
|
12824
12856
|
...opts.checkScript !== void 0 ? { checkScript: opts.checkScript } : {}
|
|
12825
12857
|
});
|
|
12826
12858
|
if (!prompt.ok) return Result.error(prompt.error);
|
|
12827
|
-
const wrotePrompt = await writeOrError(
|
|
12859
|
+
const wrotePrompt = await writeOrError(join21(workspaceRoot, "prompt.md"), String(prompt.value));
|
|
12828
12860
|
if (!wrotePrompt.ok) return Result.error(wrotePrompt.error);
|
|
12829
12861
|
const wroteCriteria = await writeOrError(
|
|
12830
|
-
|
|
12862
|
+
join21(workspaceRoot, "done-criteria.md"),
|
|
12831
12863
|
renderDoneCriteria(input.task)
|
|
12832
12864
|
);
|
|
12833
12865
|
if (!wroteCriteria.ok) return Result.error(wroteCriteria.error);
|
|
@@ -12898,15 +12930,20 @@ var transitionSprintToReviewLeaf = (deps) => leaf("transition-sprint-to-review",
|
|
|
12898
12930
|
|
|
12899
12931
|
// src/integration/io/lock-paths.ts
|
|
12900
12932
|
import { createHash } from "crypto";
|
|
12901
|
-
import { join as
|
|
12933
|
+
import { join as join22 } from "path";
|
|
12902
12934
|
var repoLockFile = (locksRoot, worktreePath) => {
|
|
12903
12935
|
const hash = createHash("sha1").update(String(worktreePath)).digest("hex").slice(0, 16);
|
|
12904
|
-
return AbsolutePath.parse(
|
|
12936
|
+
return AbsolutePath.parse(join22(String(locksRoot), `repo-${hash}.lock`));
|
|
12905
12937
|
};
|
|
12906
12938
|
|
|
12907
12939
|
// src/application/flows/implement/leaves/with-repo-lock.ts
|
|
12908
12940
|
var withRepoLock = (opts, inner) => ({
|
|
12909
12941
|
name: `with-repo-lock(${inner.name})`,
|
|
12942
|
+
// Expose the wrapped chain through the composite-pattern `children` slot so `flattenLeaves`
|
|
12943
|
+
// walks into it when the TUI builds its planned-leaf list. Without this the wrapper looked
|
|
12944
|
+
// like an opaque single leaf and the Flow-steps panel rendered only "with-repo-lock(…)" —
|
|
12945
|
+
// never the real setup / per-task / teardown sequence inside the lock.
|
|
12946
|
+
children: [inner],
|
|
12910
12947
|
async execute(ctx, signal, onTrace) {
|
|
12911
12948
|
const lockPath = repoLockFile(opts.locksRoot, opts.worktreePath);
|
|
12912
12949
|
if (!lockPath.ok) {
|
|
@@ -13196,11 +13233,11 @@ var launchImplement = (ctx) => {
|
|
|
13196
13233
|
return a.status === "in_progress" ? -1 : 1;
|
|
13197
13234
|
});
|
|
13198
13235
|
if (todoTasks.length === 0) return { ok: false, reason: "No tasks to implement or resume." };
|
|
13199
|
-
const sprintDirPath = AbsolutePath.parse(
|
|
13236
|
+
const sprintDirPath = AbsolutePath.parse(join23(String(deps.storage.dataRoot), "sprints", String(snapshot.sprint.id)));
|
|
13200
13237
|
if (!sprintDirPath.ok) return { ok: false, reason: sprintDirPath.error.message };
|
|
13201
|
-
const progressPath = AbsolutePath.parse(
|
|
13238
|
+
const progressPath = AbsolutePath.parse(join23(String(sprintDirPath.value), "progress.md"));
|
|
13202
13239
|
if (!progressPath.ok) return { ok: false, reason: progressPath.error.message };
|
|
13203
|
-
const chainLogPath = AbsolutePath.parse(
|
|
13240
|
+
const chainLogPath = AbsolutePath.parse(join23(String(sprintDirPath.value), "chain.log"));
|
|
13204
13241
|
if (!chainLogPath.ok) return { ok: false, reason: chainLogPath.error.message };
|
|
13205
13242
|
const chainLog = startFileLogSink({ file: chainLogPath.value, bus: deps.app.eventBus });
|
|
13206
13243
|
const repositories = /* @__PURE__ */ new Map();
|
|
@@ -13265,7 +13302,7 @@ var launchImplement = (ctx) => {
|
|
|
13265
13302
|
};
|
|
13266
13303
|
|
|
13267
13304
|
// src/application/ui/shared/launch/review.ts
|
|
13268
|
-
import { join as
|
|
13305
|
+
import { join as join25 } from "path";
|
|
13269
13306
|
|
|
13270
13307
|
// src/application/flows/review/leaves/ensure-feedback-file.ts
|
|
13271
13308
|
import { promises as fs18 } from "fs";
|
|
@@ -13508,12 +13545,12 @@ var buildApplyFeedbackPrompt = async (deps, input) => buildPrompt(deps, applyFee
|
|
|
13508
13545
|
|
|
13509
13546
|
// src/integration/ai/signals/_engine/temp-signals-file.ts
|
|
13510
13547
|
import { tmpdir as tmpdir2 } from "os";
|
|
13511
|
-
import { join as
|
|
13548
|
+
import { join as join24 } from "path";
|
|
13512
13549
|
var counter = 0;
|
|
13513
13550
|
var allocSignalsTempPath = (label) => {
|
|
13514
13551
|
counter += 1;
|
|
13515
13552
|
const filename = `ralphctl-signals-${label}-${String(process.pid)}-${String(Date.now())}-${String(counter)}.json`;
|
|
13516
|
-
return AbsolutePath.parse(
|
|
13553
|
+
return AbsolutePath.parse(join24(tmpdir2(), filename));
|
|
13517
13554
|
};
|
|
13518
13555
|
var withSignalsTempPath = async (label, fn) => {
|
|
13519
13556
|
const path = allocSignalsTempPath(label);
|
|
@@ -13772,11 +13809,11 @@ var launchReview = (ctx) => {
|
|
|
13772
13809
|
if (!snapshot.sprint) return { ok: false, reason: "No sprint selected." };
|
|
13773
13810
|
if (!cwd) return { ok: false, reason: "No repository path resolvable from the project." };
|
|
13774
13811
|
const feedbackPath = AbsolutePath.parse(
|
|
13775
|
-
|
|
13812
|
+
join25(String(deps.storage.dataRoot), "sprints", String(snapshot.sprint.id), "feedback.md")
|
|
13776
13813
|
);
|
|
13777
13814
|
if (!feedbackPath.ok) return { ok: false, reason: feedbackPath.error.message };
|
|
13778
13815
|
const progressPath = AbsolutePath.parse(
|
|
13779
|
-
|
|
13816
|
+
join25(String(deps.storage.dataRoot), "sprints", String(snapshot.sprint.id), "progress.md")
|
|
13780
13817
|
);
|
|
13781
13818
|
if (!progressPath.ok) return { ok: false, reason: progressPath.error.message };
|
|
13782
13819
|
const checkScript = snapshot.project?.repositories.find((r) => r.checkScript !== void 0)?.checkScript;
|
|
@@ -14005,11 +14042,11 @@ var probeReadinessLeaf = (deps) => leaf("probe", {
|
|
|
14005
14042
|
import { promises as fs21 } from "fs";
|
|
14006
14043
|
|
|
14007
14044
|
// src/integration/ai/readiness/_engine/setup.ts
|
|
14008
|
-
import { join as
|
|
14045
|
+
import { join as join27 } from "path";
|
|
14009
14046
|
|
|
14010
14047
|
// src/integration/ai/runs/_engine/run-artifacts.ts
|
|
14011
14048
|
import { promises as fs20 } from "fs";
|
|
14012
|
-
import { join as
|
|
14049
|
+
import { join as join26 } from "path";
|
|
14013
14050
|
var BODY_PREVIEW_LIMIT = 800;
|
|
14014
14051
|
var buildRunDirName = () => {
|
|
14015
14052
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
@@ -14019,7 +14056,7 @@ var buildRunDirName = () => {
|
|
|
14019
14056
|
var readRunBodyPreview = async (runDir, options) => {
|
|
14020
14057
|
let raw;
|
|
14021
14058
|
try {
|
|
14022
|
-
raw = await fs20.readFile(
|
|
14059
|
+
raw = await fs20.readFile(join26(String(runDir), "body.txt"), "utf8");
|
|
14023
14060
|
} catch (cause) {
|
|
14024
14061
|
if (isErrnoException(cause) && cause.code === "ENOENT") return void 0;
|
|
14025
14062
|
const code = isErrnoException(cause) ? cause.code : "unknown";
|
|
@@ -14148,11 +14185,11 @@ var setupReadinessUseCase = async (deps, input) => {
|
|
|
14148
14185
|
...input.existingContextFile !== void 0 ? { existingContextFile: input.existingContextFile } : {}
|
|
14149
14186
|
});
|
|
14150
14187
|
if (!prompt.ok) return Result.error(prompt.error);
|
|
14151
|
-
const runDir = AbsolutePath.parse(
|
|
14188
|
+
const runDir = AbsolutePath.parse(join27(String(deps.runsRoot), "readiness", buildRunDirName()));
|
|
14152
14189
|
if (!runDir.ok) return Result.error(runDir.error);
|
|
14153
|
-
const promptFile = AbsolutePath.parse(
|
|
14190
|
+
const promptFile = AbsolutePath.parse(join27(String(runDir.value), "prompt.md"));
|
|
14154
14191
|
if (!promptFile.ok) return Result.error(promptFile.error);
|
|
14155
|
-
const bodyFile = AbsolutePath.parse(
|
|
14192
|
+
const bodyFile = AbsolutePath.parse(join27(String(runDir.value), "body.txt"));
|
|
14156
14193
|
if (!bodyFile.ok) return Result.error(bodyFile.error);
|
|
14157
14194
|
const promptWrote = await writeTextAtomic(String(promptFile.value), String(prompt.value));
|
|
14158
14195
|
if (!promptWrote.ok) return Result.error(promptWrote.error);
|
|
@@ -14201,7 +14238,7 @@ var setupReadinessUseCase = async (deps, input) => {
|
|
|
14201
14238
|
const verifyScript = signals.value.find(
|
|
14202
14239
|
(s) => s.type === "verify-script"
|
|
14203
14240
|
)?.command;
|
|
14204
|
-
const targetPath = AbsolutePath.parse(
|
|
14241
|
+
const targetPath = AbsolutePath.parse(join27(String(input.repository.path), targetPathFor(input.tool)));
|
|
14205
14242
|
if (!targetPath.ok) return Result.error(targetPath.error);
|
|
14206
14243
|
log.info(`proposal ready for repo ${input.repository.name}`, {
|
|
14207
14244
|
repositoryId: String(input.repository.id),
|
|
@@ -14267,6 +14304,9 @@ var pickExistingContextPath = (tool, state) => {
|
|
|
14267
14304
|
if (tool === "copilot" && a.tool === "copilot") {
|
|
14268
14305
|
return a.copilotInstructions !== void 0 ? String(a.copilotInstructions.path) : void 0;
|
|
14269
14306
|
}
|
|
14307
|
+
if (tool === "codex" && a.tool === "codex") {
|
|
14308
|
+
return a.agentsMd !== void 0 ? String(a.agentsMd.path) : void 0;
|
|
14309
|
+
}
|
|
14270
14310
|
return void 0;
|
|
14271
14311
|
};
|
|
14272
14312
|
var proposeReadinessLeaf = (deps) => leaf("propose", {
|
|
@@ -14447,7 +14487,7 @@ var launchReadiness = (ctx) => {
|
|
|
14447
14487
|
};
|
|
14448
14488
|
|
|
14449
14489
|
// src/application/flows/detect-skills/leaves/propose.ts
|
|
14450
|
-
import { join as
|
|
14490
|
+
import { join as join28 } from "path";
|
|
14451
14491
|
|
|
14452
14492
|
// src/integration/ai/prompts/detect-skills/definition.ts
|
|
14453
14493
|
var detectSkillsPromptDef = {
|
|
@@ -14511,11 +14551,11 @@ var proposeUseCase = async (deps, input) => {
|
|
|
14511
14551
|
skillsConvention: deps.skillsAdapter.describeSkillsConvention()
|
|
14512
14552
|
});
|
|
14513
14553
|
if (!prompt.ok) return Result.error(prompt.error);
|
|
14514
|
-
const runDir = AbsolutePath.parse(
|
|
14554
|
+
const runDir = AbsolutePath.parse(join28(String(deps.runsRoot), "detect-skills", buildRunDirName()));
|
|
14515
14555
|
if (!runDir.ok) return Result.error(runDir.error);
|
|
14516
|
-
const promptFile = AbsolutePath.parse(
|
|
14556
|
+
const promptFile = AbsolutePath.parse(join28(String(runDir.value), "prompt.md"));
|
|
14517
14557
|
if (!promptFile.ok) return Result.error(promptFile.error);
|
|
14518
|
-
const bodyFile = AbsolutePath.parse(
|
|
14558
|
+
const bodyFile = AbsolutePath.parse(join28(String(runDir.value), "body.txt"));
|
|
14519
14559
|
if (!bodyFile.ok) return Result.error(bodyFile.error);
|
|
14520
14560
|
const promptWrote = await writeTextAtomic(String(promptFile.value), String(prompt.value));
|
|
14521
14561
|
if (!promptWrote.ok) return Result.error(promptWrote.error);
|
|
@@ -15129,7 +15169,7 @@ var launchDetectSkills = (ctx) => {
|
|
|
15129
15169
|
};
|
|
15130
15170
|
|
|
15131
15171
|
// src/application/flows/detect-scripts/leaves/propose.ts
|
|
15132
|
-
import { join as
|
|
15172
|
+
import { join as join29 } from "path";
|
|
15133
15173
|
|
|
15134
15174
|
// src/integration/ai/prompts/detect-scripts/definition.ts
|
|
15135
15175
|
var detectScriptsPromptDef = {
|
|
@@ -15174,11 +15214,11 @@ var proposeUseCase2 = async (deps, input) => {
|
|
|
15174
15214
|
repositoryPath: String(input.repository.path)
|
|
15175
15215
|
});
|
|
15176
15216
|
if (!prompt.ok) return Result.error(prompt.error);
|
|
15177
|
-
const runDir = AbsolutePath.parse(
|
|
15217
|
+
const runDir = AbsolutePath.parse(join29(String(deps.runsRoot), "detect-scripts", buildRunDirName()));
|
|
15178
15218
|
if (!runDir.ok) return Result.error(runDir.error);
|
|
15179
|
-
const promptFile = AbsolutePath.parse(
|
|
15219
|
+
const promptFile = AbsolutePath.parse(join29(String(runDir.value), "prompt.md"));
|
|
15180
15220
|
if (!promptFile.ok) return Result.error(promptFile.error);
|
|
15181
|
-
const bodyFile = AbsolutePath.parse(
|
|
15221
|
+
const bodyFile = AbsolutePath.parse(join29(String(runDir.value), "body.txt"));
|
|
15182
15222
|
if (!bodyFile.ok) return Result.error(bodyFile.error);
|
|
15183
15223
|
const promptWrote = await writeTextAtomic(String(promptFile.value), String(prompt.value));
|
|
15184
15224
|
if (!promptWrote.ok) return Result.error(promptWrote.error);
|
|
@@ -15522,10 +15562,10 @@ var launchDetectScripts = (ctx) => {
|
|
|
15522
15562
|
};
|
|
15523
15563
|
|
|
15524
15564
|
// src/application/ui/shared/launch/ideate.ts
|
|
15525
|
-
import { join as
|
|
15565
|
+
import { join as join31 } from "path";
|
|
15526
15566
|
|
|
15527
15567
|
// src/application/flows/ideate/flow.ts
|
|
15528
|
-
import { join as
|
|
15568
|
+
import { join as join30 } from "path";
|
|
15529
15569
|
|
|
15530
15570
|
// src/integration/ai/prompts/ideate/definition.ts
|
|
15531
15571
|
var nonEmpty2 = (field) => (v) => v.trim().length === 0 ? Result.error(new ValidationError({ field, value: v, message: `${field} must not be empty` })) : Result.ok(v);
|
|
@@ -15762,8 +15802,8 @@ var createIdeateFlow = (deps, opts) => {
|
|
|
15762
15802
|
parent: () => opts.ideateRoot,
|
|
15763
15803
|
slug: () => slug,
|
|
15764
15804
|
write: (ctx, root) => {
|
|
15765
|
-
const promptPath = AbsolutePath.parse(
|
|
15766
|
-
const outputPath = AbsolutePath.parse(
|
|
15805
|
+
const promptPath = AbsolutePath.parse(join30(String(root), "prompt.md"));
|
|
15806
|
+
const outputPath = AbsolutePath.parse(join30(String(root), "ideate.json"));
|
|
15767
15807
|
if (!promptPath.ok) throw promptPath.error;
|
|
15768
15808
|
if (!outputPath.ok) throw outputPath.error;
|
|
15769
15809
|
return {
|
|
@@ -15827,7 +15867,7 @@ var launchIdeate = async (ctx) => {
|
|
|
15827
15867
|
const bodyAns = await deps.interactive.askText("Idea description (paste or type)");
|
|
15828
15868
|
if (!bodyAns.ok) return { ok: false, reason: bodyAns.error.message };
|
|
15829
15869
|
const ideateRoot = AbsolutePath.parse(
|
|
15830
|
-
|
|
15870
|
+
join31(String(deps.storage.dataRoot), "sprints", String(snapshot.sprint.id), "ideate")
|
|
15831
15871
|
);
|
|
15832
15872
|
if (!ideateRoot.ok) return { ok: false, reason: ideateRoot.error.message };
|
|
15833
15873
|
const element = createIdeateFlow(
|
|
@@ -17982,30 +18022,23 @@ var SignalLine = ({ signal }) => {
|
|
|
17982
18022
|
/* @__PURE__ */ jsx43(Text33, { bold: row.bold ?? false, children: truncate2(row.text, 80) })
|
|
17983
18023
|
] });
|
|
17984
18024
|
};
|
|
18025
|
+
var LEGEND_ENTRIES = [
|
|
18026
|
+
{ label: "change", color: inkColors.info, description: "file/code edit" },
|
|
18027
|
+
{ label: "learning", color: inkColors.highlight, description: "cross-task insight" },
|
|
18028
|
+
{ label: "decision", color: inkColors.highlight, description: "design choice" },
|
|
18029
|
+
{ label: "verified", color: inkColors.success, description: "task self-check passed" },
|
|
18030
|
+
{ label: "blocked", color: inkColors.error, description: "task self-blocked" },
|
|
18031
|
+
{ label: "commit", color: inkColors.info, description: "proposed commit message" }
|
|
18032
|
+
];
|
|
17985
18033
|
var SignalLegend = () => /* @__PURE__ */ jsxs32(Box31, { flexDirection: "column", paddingX: spacing.indent, marginBottom: spacing.section, children: [
|
|
17986
|
-
/* @__PURE__ */
|
|
17987
|
-
|
|
17988
|
-
|
|
17989
|
-
/* @__PURE__ */
|
|
17990
|
-
|
|
17991
|
-
|
|
17992
|
-
|
|
17993
|
-
|
|
17994
|
-
" ",
|
|
17995
|
-
/* @__PURE__ */ jsx43(Text33, { color: inkColors.highlight, children: "decision" }),
|
|
17996
|
-
" = design choice"
|
|
17997
|
-
] }),
|
|
17998
|
-
/* @__PURE__ */ jsxs32(Text33, { dimColor: true, children: [
|
|
17999
|
-
" ",
|
|
18000
|
-
/* @__PURE__ */ jsx43(Text33, { color: inkColors.success, children: "verified" }),
|
|
18001
|
-
" = task self-check passed",
|
|
18002
|
-
" ",
|
|
18003
|
-
/* @__PURE__ */ jsx43(Text33, { color: inkColors.error, children: "blocked" }),
|
|
18004
|
-
" = task self-blocked",
|
|
18005
|
-
" ",
|
|
18006
|
-
/* @__PURE__ */ jsx43(Text33, { color: inkColors.info, children: "commit" }),
|
|
18007
|
-
" = proposed commit message"
|
|
18008
|
-
] })
|
|
18034
|
+
/* @__PURE__ */ jsx43(Text33, { dimColor: true, bold: true, children: "legend" }),
|
|
18035
|
+
/* @__PURE__ */ jsx43(Box31, { flexDirection: "column", paddingLeft: 2, children: LEGEND_ENTRIES.map((entry) => /* @__PURE__ */ jsxs32(Box31, { children: [
|
|
18036
|
+
/* @__PURE__ */ jsx43(Text33, { color: entry.color, bold: true, children: padLabel2(entry.label) }),
|
|
18037
|
+
/* @__PURE__ */ jsxs32(Text33, { dimColor: true, children: [
|
|
18038
|
+
"= ",
|
|
18039
|
+
entry.description
|
|
18040
|
+
] })
|
|
18041
|
+
] }, entry.label)) })
|
|
18009
18042
|
] });
|
|
18010
18043
|
var EvaluationLine = ({ evaluation }) => {
|
|
18011
18044
|
const color = evaluation.status === "passed" ? inkColors.success : evaluation.status === "failed" ? inkColors.error : inkColors.warning;
|
|
@@ -18582,7 +18615,7 @@ var ExecuteView = () => {
|
|
|
18582
18615
|
] })
|
|
18583
18616
|
] })
|
|
18584
18617
|
] }) });
|
|
18585
|
-
const outerFlowFilter = (name) => !isPerTaskLeaf(name);
|
|
18618
|
+
const outerFlowFilter = (name) => !isPerTaskLeaf(name) && !name.startsWith("with-repo-lock(");
|
|
18586
18619
|
const flowStepsPanel = /* @__PURE__ */ jsx46(
|
|
18587
18620
|
StepTrace,
|
|
18588
18621
|
{
|
|
@@ -19314,7 +19347,7 @@ var ProbeRow = ({ probe }) => /* @__PURE__ */ jsxs39(Box38, { flexDirection: "co
|
|
|
19314
19347
|
// src/application/ui/tui/views/export-context-view.tsx
|
|
19315
19348
|
import { useCallback as useCallback7, useEffect as useEffect25, useState as useState31 } from "react";
|
|
19316
19349
|
import { Box as Box39, Text as Text41, useInput as useInput20 } from "ink";
|
|
19317
|
-
import { join as
|
|
19350
|
+
import { join as join32 } from "path";
|
|
19318
19351
|
|
|
19319
19352
|
// src/business/sprint/views/context-md.ts
|
|
19320
19353
|
var renderSprintContextMarkdown = (input) => {
|
|
@@ -19446,7 +19479,7 @@ var ExportContextView = () => {
|
|
|
19446
19479
|
return;
|
|
19447
19480
|
}
|
|
19448
19481
|
const outputPath = AbsolutePath.parse(
|
|
19449
|
-
|
|
19482
|
+
join32(String(storage2.dataRoot), "sprints", String(selection.sprintId), "context.md")
|
|
19450
19483
|
);
|
|
19451
19484
|
if (!outputPath.ok) {
|
|
19452
19485
|
setRun({ kind: "error", message: outputPath.error.message });
|
|
@@ -19504,7 +19537,7 @@ var ExportContextView = () => {
|
|
|
19504
19537
|
// src/application/ui/tui/views/export-requirements-view.tsx
|
|
19505
19538
|
import { useCallback as useCallback8, useEffect as useEffect26, useState as useState32 } from "react";
|
|
19506
19539
|
import { Box as Box40, Text as Text42, useInput as useInput21 } from "ink";
|
|
19507
|
-
import { join as
|
|
19540
|
+
import { join as join33 } from "path";
|
|
19508
19541
|
|
|
19509
19542
|
// src/business/sprint/views/requirements-md.ts
|
|
19510
19543
|
var renderSprintRequirementsMarkdown = (sprint) => {
|
|
@@ -19569,7 +19602,7 @@ var ExportRequirementsView = () => {
|
|
|
19569
19602
|
return;
|
|
19570
19603
|
}
|
|
19571
19604
|
const outputPath = AbsolutePath.parse(
|
|
19572
|
-
|
|
19605
|
+
join33(String(storage2.dataRoot), "sprints", String(selection.sprintId), "requirements.md")
|
|
19573
19606
|
);
|
|
19574
19607
|
if (!outputPath.ok) {
|
|
19575
19608
|
setRun({ kind: "error", message: outputPath.error.message });
|
|
@@ -19904,12 +19937,12 @@ var WelcomeView = () => {
|
|
|
19904
19937
|
import { useEffect as useEffect29, useState as useState36 } from "react";
|
|
19905
19938
|
import { Box as Box44, Text as Text46 } from "ink";
|
|
19906
19939
|
import { homedir as osHomedir2 } from "os";
|
|
19907
|
-
import { basename as basename3, join as
|
|
19940
|
+
import { basename as basename3, join as join35 } from "path";
|
|
19908
19941
|
|
|
19909
19942
|
// src/application/ui/tui/prompts/path-picker-prompt.tsx
|
|
19910
19943
|
import { useEffect as useEffect28, useState as useState35 } from "react";
|
|
19911
19944
|
import { promises as fs24 } from "fs";
|
|
19912
|
-
import { dirname as dirname10, join as
|
|
19945
|
+
import { dirname as dirname10, join as join34 } from "path";
|
|
19913
19946
|
import { homedir } from "os";
|
|
19914
19947
|
import { Box as Box43, Text as Text45, useInput as useInput23 } from "ink";
|
|
19915
19948
|
import { jsx as jsx55, jsxs as jsxs44 } from "react/jsx-runtime";
|
|
@@ -19917,7 +19950,7 @@ var VISIBLE_ROWS2 = 12;
|
|
|
19917
19950
|
var clamp5 = (n, min, max) => Math.max(min, Math.min(max, n));
|
|
19918
19951
|
var expandHome = (input) => {
|
|
19919
19952
|
if (input === "~") return homedir();
|
|
19920
|
-
if (input.startsWith("~/")) return
|
|
19953
|
+
if (input.startsWith("~/")) return join34(homedir(), input.slice(2));
|
|
19921
19954
|
return input;
|
|
19922
19955
|
};
|
|
19923
19956
|
var PathPickerPrompt = ({
|
|
@@ -20005,7 +20038,7 @@ var PathPickerPrompt = ({
|
|
|
20005
20038
|
onSubmit(cwd);
|
|
20006
20039
|
return;
|
|
20007
20040
|
}
|
|
20008
|
-
setCwd(
|
|
20041
|
+
setCwd(join34(cwd, row.entry.name));
|
|
20009
20042
|
setCursor(1);
|
|
20010
20043
|
}
|
|
20011
20044
|
},
|
|
@@ -20087,7 +20120,7 @@ var labelFor = (row) => {
|
|
|
20087
20120
|
import { jsx as jsx56, jsxs as jsxs45 } from "react/jsx-runtime";
|
|
20088
20121
|
var expandHome2 = (input) => {
|
|
20089
20122
|
if (input === "~") return osHomedir2();
|
|
20090
|
-
if (input.startsWith("~/")) return
|
|
20123
|
+
if (input.startsWith("~/")) return join35(osHomedir2(), input.slice(2));
|
|
20091
20124
|
return input;
|
|
20092
20125
|
};
|
|
20093
20126
|
var backStep = (step) => {
|
|
@@ -20303,11 +20336,11 @@ var StepView = ({ step, onChange, onCancel, onSubmit }) => {
|
|
|
20303
20336
|
import { useEffect as useEffect30, useState as useState37 } from "react";
|
|
20304
20337
|
import { Box as Box45, Text as Text47 } from "ink";
|
|
20305
20338
|
import { homedir as osHomedir3 } from "os";
|
|
20306
|
-
import { basename as basename4, join as
|
|
20339
|
+
import { basename as basename4, join as join36 } from "path";
|
|
20307
20340
|
import { jsx as jsx57, jsxs as jsxs46 } from "react/jsx-runtime";
|
|
20308
20341
|
var expandHome3 = (input) => {
|
|
20309
20342
|
if (input === "~") return osHomedir3();
|
|
20310
|
-
if (input.startsWith("~/")) return
|
|
20343
|
+
if (input.startsWith("~/")) return join36(osHomedir3(), input.slice(2));
|
|
20311
20344
|
return input;
|
|
20312
20345
|
};
|
|
20313
20346
|
var backStep2 = (step) => {
|
|
@@ -21138,10 +21171,10 @@ var resolveInitialState = ({
|
|
|
21138
21171
|
|
|
21139
21172
|
// src/integration/persistence/selection/last-selection-store.ts
|
|
21140
21173
|
import { promises as fs25 } from "fs";
|
|
21141
|
-
import { join as
|
|
21174
|
+
import { join as join37 } from "path";
|
|
21142
21175
|
var FILE_NAME = "last-selection.json";
|
|
21143
21176
|
var createLastSelectionStore = (stateRoot) => {
|
|
21144
|
-
const path =
|
|
21177
|
+
const path = join37(String(stateRoot), FILE_NAME);
|
|
21145
21178
|
return {
|
|
21146
21179
|
async read() {
|
|
21147
21180
|
try {
|
package/dist/manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ralphctl",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
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",
|