@neriros/ralphy 3.3.2 → 3.5.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/mcp/index.js +64 -17
- package/dist/shell/index.js +416 -74
- package/package.json +1 -1
package/dist/mcp/index.js
CHANGED
|
@@ -25106,22 +25106,6 @@ ${existing.trimStart()}` : `${message}
|
|
|
25106
25106
|
await mkdir(dirname3(tasksPath), { recursive: true });
|
|
25107
25107
|
await Bun.write(tasksPath, next);
|
|
25108
25108
|
}
|
|
25109
|
-
async readSection(name, artifact, heading) {
|
|
25110
|
-
const file = Bun.file(join4("openspec", "changes", name, artifact));
|
|
25111
|
-
if (!await file.exists())
|
|
25112
|
-
return "";
|
|
25113
|
-
const content = await file.text();
|
|
25114
|
-
const headingIndex = content.indexOf(heading);
|
|
25115
|
-
if (headingIndex === -1)
|
|
25116
|
-
return "";
|
|
25117
|
-
const afterHeading = content.slice(headingIndex + heading.length);
|
|
25118
|
-
const levelMatch = heading.match(/^(#+)/);
|
|
25119
|
-
const level = levelMatch ? levelMatch[1].length : 2;
|
|
25120
|
-
const nextHeadingPattern = new RegExp(`\\n#{1,${level}} `);
|
|
25121
|
-
const nextMatch = afterHeading.match(nextHeadingPattern);
|
|
25122
|
-
const sectionContent = nextMatch ? afterHeading.slice(0, nextMatch.index) : afterHeading;
|
|
25123
|
-
return sectionContent.trim();
|
|
25124
|
-
}
|
|
25125
25109
|
async validateChange(name) {
|
|
25126
25110
|
const result = runOpenspec(["validate", name, "--json", "--no-interactive"]);
|
|
25127
25111
|
if (result.stdout) {
|
|
@@ -25140,8 +25124,71 @@ ${existing.trimStart()}` : `${message}
|
|
|
25140
25124
|
errors: result.stderr ? [result.stderr] : []
|
|
25141
25125
|
};
|
|
25142
25126
|
}
|
|
25127
|
+
async getStatus(name) {
|
|
25128
|
+
const result = runOpenspec(["status", "--change", name, "--json"]);
|
|
25129
|
+
if (result.stdout) {
|
|
25130
|
+
try {
|
|
25131
|
+
const parsed = JSON.parse(result.stdout);
|
|
25132
|
+
const status = {
|
|
25133
|
+
changeName: parsed.changeName ?? name,
|
|
25134
|
+
isComplete: parsed.isComplete ?? false,
|
|
25135
|
+
applyRequires: parsed.applyRequires ?? [],
|
|
25136
|
+
artifacts: parsed.artifacts ?? []
|
|
25137
|
+
};
|
|
25138
|
+
if (parsed.schemaName !== undefined)
|
|
25139
|
+
status.schemaName = parsed.schemaName;
|
|
25140
|
+
return status;
|
|
25141
|
+
} catch {}
|
|
25142
|
+
}
|
|
25143
|
+
return {
|
|
25144
|
+
changeName: name,
|
|
25145
|
+
isComplete: false,
|
|
25146
|
+
applyRequires: [],
|
|
25147
|
+
artifacts: []
|
|
25148
|
+
};
|
|
25149
|
+
}
|
|
25150
|
+
async getInstructions(name, artifact) {
|
|
25151
|
+
const result = runOpenspec(["instructions", artifact, "--change", name, "--json"]);
|
|
25152
|
+
if (result.stdout) {
|
|
25153
|
+
try {
|
|
25154
|
+
const parsed = JSON.parse(result.stdout);
|
|
25155
|
+
const out = {
|
|
25156
|
+
changeName: parsed.changeName ?? name,
|
|
25157
|
+
artifactId: parsed.artifactId ?? artifact,
|
|
25158
|
+
instruction: parsed.instruction ?? ""
|
|
25159
|
+
};
|
|
25160
|
+
if (parsed.outputPath !== undefined)
|
|
25161
|
+
out.outputPath = parsed.outputPath;
|
|
25162
|
+
if (parsed.description !== undefined)
|
|
25163
|
+
out.description = parsed.description;
|
|
25164
|
+
if (parsed.template !== undefined)
|
|
25165
|
+
out.template = parsed.template;
|
|
25166
|
+
if (parsed.dependencies !== undefined)
|
|
25167
|
+
out.dependencies = parsed.dependencies;
|
|
25168
|
+
return out;
|
|
25169
|
+
} catch {}
|
|
25170
|
+
}
|
|
25171
|
+
return { changeName: name, artifactId: artifact, instruction: "" };
|
|
25172
|
+
}
|
|
25173
|
+
async showChange(name) {
|
|
25174
|
+
const result = runOpenspec(["show", name, "--json", "--type", "change"]);
|
|
25175
|
+
if (result.stdout) {
|
|
25176
|
+
try {
|
|
25177
|
+
const parsed = JSON.parse(result.stdout);
|
|
25178
|
+
const out = {
|
|
25179
|
+
id: parsed.id ?? name,
|
|
25180
|
+
deltaCount: parsed.deltaCount ?? 0,
|
|
25181
|
+
deltas: parsed.deltas ?? []
|
|
25182
|
+
};
|
|
25183
|
+
if (parsed.title !== undefined)
|
|
25184
|
+
out.title = parsed.title;
|
|
25185
|
+
return out;
|
|
25186
|
+
} catch {}
|
|
25187
|
+
}
|
|
25188
|
+
return { id: name, deltaCount: 0, deltas: [] };
|
|
25189
|
+
}
|
|
25143
25190
|
async archiveChange(name) {
|
|
25144
|
-
const result = runOpenspec(["archive", name, "-y"
|
|
25191
|
+
const result = runOpenspec(["archive", name, "-y"], { inherit: true });
|
|
25145
25192
|
if (result.status !== 0) {
|
|
25146
25193
|
throw new Error("openspec archive failed");
|
|
25147
25194
|
}
|
package/dist/shell/index.js
CHANGED
|
@@ -18928,8 +18928,8 @@ import { readFileSync } from "fs";
|
|
|
18928
18928
|
import { resolve } from "path";
|
|
18929
18929
|
function getVersion() {
|
|
18930
18930
|
try {
|
|
18931
|
-
if ("3.
|
|
18932
|
-
return "3.
|
|
18931
|
+
if ("3.5.0")
|
|
18932
|
+
return "3.5.0";
|
|
18933
18933
|
} catch {}
|
|
18934
18934
|
const dirsToTry = [];
|
|
18935
18935
|
try {
|
|
@@ -59558,22 +59558,6 @@ ${existing.trimStart()}` : `${message}
|
|
|
59558
59558
|
await mkdir(dirname4(tasksPath), { recursive: true });
|
|
59559
59559
|
await Bun.write(tasksPath, next);
|
|
59560
59560
|
}
|
|
59561
|
-
async readSection(name, artifact, heading) {
|
|
59562
|
-
const file = Bun.file(join6("openspec", "changes", name, artifact));
|
|
59563
|
-
if (!await file.exists())
|
|
59564
|
-
return "";
|
|
59565
|
-
const content = await file.text();
|
|
59566
|
-
const headingIndex = content.indexOf(heading);
|
|
59567
|
-
if (headingIndex === -1)
|
|
59568
|
-
return "";
|
|
59569
|
-
const afterHeading = content.slice(headingIndex + heading.length);
|
|
59570
|
-
const levelMatch = heading.match(/^(#+)/);
|
|
59571
|
-
const level = levelMatch ? levelMatch[1].length : 2;
|
|
59572
|
-
const nextHeadingPattern = new RegExp(`\\n#{1,${level}} `);
|
|
59573
|
-
const nextMatch = afterHeading.match(nextHeadingPattern);
|
|
59574
|
-
const sectionContent = nextMatch ? afterHeading.slice(0, nextMatch.index) : afterHeading;
|
|
59575
|
-
return sectionContent.trim();
|
|
59576
|
-
}
|
|
59577
59561
|
async validateChange(name) {
|
|
59578
59562
|
const result2 = runOpenspec(["validate", name, "--json", "--no-interactive"]);
|
|
59579
59563
|
if (result2.stdout) {
|
|
@@ -59592,8 +59576,71 @@ ${existing.trimStart()}` : `${message}
|
|
|
59592
59576
|
errors: result2.stderr ? [result2.stderr] : []
|
|
59593
59577
|
};
|
|
59594
59578
|
}
|
|
59579
|
+
async getStatus(name) {
|
|
59580
|
+
const result2 = runOpenspec(["status", "--change", name, "--json"]);
|
|
59581
|
+
if (result2.stdout) {
|
|
59582
|
+
try {
|
|
59583
|
+
const parsed = JSON.parse(result2.stdout);
|
|
59584
|
+
const status = {
|
|
59585
|
+
changeName: parsed.changeName ?? name,
|
|
59586
|
+
isComplete: parsed.isComplete ?? false,
|
|
59587
|
+
applyRequires: parsed.applyRequires ?? [],
|
|
59588
|
+
artifacts: parsed.artifacts ?? []
|
|
59589
|
+
};
|
|
59590
|
+
if (parsed.schemaName !== undefined)
|
|
59591
|
+
status.schemaName = parsed.schemaName;
|
|
59592
|
+
return status;
|
|
59593
|
+
} catch {}
|
|
59594
|
+
}
|
|
59595
|
+
return {
|
|
59596
|
+
changeName: name,
|
|
59597
|
+
isComplete: false,
|
|
59598
|
+
applyRequires: [],
|
|
59599
|
+
artifacts: []
|
|
59600
|
+
};
|
|
59601
|
+
}
|
|
59602
|
+
async getInstructions(name, artifact) {
|
|
59603
|
+
const result2 = runOpenspec(["instructions", artifact, "--change", name, "--json"]);
|
|
59604
|
+
if (result2.stdout) {
|
|
59605
|
+
try {
|
|
59606
|
+
const parsed = JSON.parse(result2.stdout);
|
|
59607
|
+
const out = {
|
|
59608
|
+
changeName: parsed.changeName ?? name,
|
|
59609
|
+
artifactId: parsed.artifactId ?? artifact,
|
|
59610
|
+
instruction: parsed.instruction ?? ""
|
|
59611
|
+
};
|
|
59612
|
+
if (parsed.outputPath !== undefined)
|
|
59613
|
+
out.outputPath = parsed.outputPath;
|
|
59614
|
+
if (parsed.description !== undefined)
|
|
59615
|
+
out.description = parsed.description;
|
|
59616
|
+
if (parsed.template !== undefined)
|
|
59617
|
+
out.template = parsed.template;
|
|
59618
|
+
if (parsed.dependencies !== undefined)
|
|
59619
|
+
out.dependencies = parsed.dependencies;
|
|
59620
|
+
return out;
|
|
59621
|
+
} catch {}
|
|
59622
|
+
}
|
|
59623
|
+
return { changeName: name, artifactId: artifact, instruction: "" };
|
|
59624
|
+
}
|
|
59625
|
+
async showChange(name) {
|
|
59626
|
+
const result2 = runOpenspec(["show", name, "--json", "--type", "change"]);
|
|
59627
|
+
if (result2.stdout) {
|
|
59628
|
+
try {
|
|
59629
|
+
const parsed = JSON.parse(result2.stdout);
|
|
59630
|
+
const out = {
|
|
59631
|
+
id: parsed.id ?? name,
|
|
59632
|
+
deltaCount: parsed.deltaCount ?? 0,
|
|
59633
|
+
deltas: parsed.deltas ?? []
|
|
59634
|
+
};
|
|
59635
|
+
if (parsed.title !== undefined)
|
|
59636
|
+
out.title = parsed.title;
|
|
59637
|
+
return out;
|
|
59638
|
+
} catch {}
|
|
59639
|
+
}
|
|
59640
|
+
return { id: name, deltaCount: 0, deltas: [] };
|
|
59641
|
+
}
|
|
59595
59642
|
async archiveChange(name) {
|
|
59596
|
-
const result2 = runOpenspec(["archive", name, "-y"
|
|
59643
|
+
const result2 = runOpenspec(["archive", name, "-y"], { inherit: true });
|
|
59597
59644
|
if (result2.status !== 0) {
|
|
59598
59645
|
throw new Error("openspec archive failed");
|
|
59599
59646
|
}
|
|
@@ -70726,6 +70773,14 @@ function useLoop(opts) {
|
|
|
70726
70773
|
writeState(stateDir, currentState);
|
|
70727
70774
|
setState(currentState);
|
|
70728
70775
|
try {
|
|
70776
|
+
if (typeof opts.changeStore.getStatus === "function") {
|
|
70777
|
+
const status = await opts.changeStore.getStatus(opts.name);
|
|
70778
|
+
if (!status.isComplete) {
|
|
70779
|
+
const blocked = status.artifacts.filter((a) => a.status !== "done").map((a) => `${a.id}=${a.status}`).join(", ");
|
|
70780
|
+
addInfo(`Archive skipped: openspec status reports change incomplete (${blocked || "no artifacts"}).`);
|
|
70781
|
+
throw new Error("openspec status: change not complete");
|
|
70782
|
+
}
|
|
70783
|
+
}
|
|
70729
70784
|
await opts.changeStore.archiveChange(opts.name);
|
|
70730
70785
|
addInfo("Change archived.");
|
|
70731
70786
|
} catch (err) {
|
|
@@ -92613,6 +92668,7 @@ var init_schema = __esm(() => {
|
|
|
92613
92668
|
codeReviewTrigger: exports_external2.boolean().default(true),
|
|
92614
92669
|
codeReviewStaleHours: exports_external2.number().nonnegative().default(24),
|
|
92615
92670
|
syncTasksToComment: exports_external2.boolean().default(true),
|
|
92671
|
+
syncSpecsAsAttachments: exports_external2.boolean().default(true),
|
|
92616
92672
|
indicators: IndicatorsSchema.default({})
|
|
92617
92673
|
}).strict().default({
|
|
92618
92674
|
postComments: true,
|
|
@@ -92622,6 +92678,7 @@ var init_schema = __esm(() => {
|
|
|
92622
92678
|
codeReviewTrigger: true,
|
|
92623
92679
|
codeReviewStaleHours: 24,
|
|
92624
92680
|
syncTasksToComment: true,
|
|
92681
|
+
syncSpecsAsAttachments: true,
|
|
92625
92682
|
indicators: {}
|
|
92626
92683
|
}),
|
|
92627
92684
|
github: exports_external2.object({
|
|
@@ -92776,6 +92833,11 @@ linear:
|
|
|
92776
92833
|
# cadence as updateEveryIterations, and on done-transition.
|
|
92777
92834
|
syncTasksToComment: true
|
|
92778
92835
|
|
|
92836
|
+
# Upload openspec proposal.md and design.md as Linear attachments on the
|
|
92837
|
+
# parent issue. Refreshed when file contents change, no-op otherwise.
|
|
92838
|
+
# Requires syncTasksToComment.
|
|
92839
|
+
syncSpecsAsAttachments: true
|
|
92840
|
+
|
|
92779
92841
|
# Indicators map Ralph lifecycle events to Linear labels/statuses.
|
|
92780
92842
|
#
|
|
92781
92843
|
# Filter semantics (per indicator's \`filter:\` list):
|
|
@@ -93763,10 +93825,45 @@ function buildIssueFilter(spec) {
|
|
|
93763
93825
|
}
|
|
93764
93826
|
return where;
|
|
93765
93827
|
}
|
|
93828
|
+
function clauseFromMarkers(markers) {
|
|
93829
|
+
if (markers.length === 0)
|
|
93830
|
+
return null;
|
|
93831
|
+
const { statuses, labels, attachmentSubtitles, projects } = partition2(markers);
|
|
93832
|
+
const parts = {};
|
|
93833
|
+
if (statuses.length > 0)
|
|
93834
|
+
parts.state = { name: { in: statuses } };
|
|
93835
|
+
if (labels.length > 0)
|
|
93836
|
+
parts.labels = { some: { name: { in: labels } } };
|
|
93837
|
+
if (attachmentSubtitles.length > 0) {
|
|
93838
|
+
parts.attachments = {
|
|
93839
|
+
some: {
|
|
93840
|
+
title: { eq: RALPHY_ATTACHMENT_TITLE_FILTER },
|
|
93841
|
+
subtitle: { in: attachmentSubtitles }
|
|
93842
|
+
}
|
|
93843
|
+
};
|
|
93844
|
+
}
|
|
93845
|
+
if (projects.length > 0)
|
|
93846
|
+
parts.project = { name: { in: projects } };
|
|
93847
|
+
return Object.keys(parts).length > 0 ? parts : null;
|
|
93848
|
+
}
|
|
93766
93849
|
async function fetchMentionScanIssues(apiKey, spec) {
|
|
93767
|
-
const
|
|
93768
|
-
|
|
93769
|
-
|
|
93850
|
+
const branches = [];
|
|
93851
|
+
const { getTodo, getInProgress, setDone } = spec.indicators;
|
|
93852
|
+
for (const ind of [getTodo, getInProgress]) {
|
|
93853
|
+
if (!ind)
|
|
93854
|
+
continue;
|
|
93855
|
+
const c = clauseFromMarkers(ind.filter);
|
|
93856
|
+
if (c)
|
|
93857
|
+
branches.push(c);
|
|
93858
|
+
}
|
|
93859
|
+
if (setDone) {
|
|
93860
|
+
const c = clauseFromMarkers(markersOf(setDone));
|
|
93861
|
+
if (c)
|
|
93862
|
+
branches.push(c);
|
|
93863
|
+
}
|
|
93864
|
+
if (branches.length === 0)
|
|
93865
|
+
return [];
|
|
93866
|
+
const where = branches.length === 1 ? { ...branches[0] } : { or: branches };
|
|
93770
93867
|
if (spec.team)
|
|
93771
93868
|
where.team = { key: { eq: spec.team } };
|
|
93772
93869
|
if (spec.assignee) {
|
|
@@ -93949,6 +94046,66 @@ async function linearRequest(apiKey, query, variables) {
|
|
|
93949
94046
|
}
|
|
93950
94047
|
throw lastHttpError ?? new Error("Linear API request failed");
|
|
93951
94048
|
}
|
|
94049
|
+
async function uploadFileToLinear(apiKey, input) {
|
|
94050
|
+
const mutation = `mutation FileUpload($filename: String!, $contentType: String!, $size: Int!) {
|
|
94051
|
+
fileUpload(filename: $filename, contentType: $contentType, size: $size) {
|
|
94052
|
+
uploadFile { uploadUrl assetUrl headers { key value } }
|
|
94053
|
+
}
|
|
94054
|
+
}`;
|
|
94055
|
+
const data = await linearRequest(apiKey, mutation, {
|
|
94056
|
+
filename: input.filename,
|
|
94057
|
+
contentType: input.contentType,
|
|
94058
|
+
size: input.bytes.byteLength
|
|
94059
|
+
});
|
|
94060
|
+
const up = data.uploadFile;
|
|
94061
|
+
if (!up)
|
|
94062
|
+
throw new Error("fileUpload returned no uploadFile payload");
|
|
94063
|
+
const headers = { "Content-Type": input.contentType };
|
|
94064
|
+
for (const h of up.headers)
|
|
94065
|
+
headers[h.key] = h.value;
|
|
94066
|
+
const res = await fetch(up.uploadUrl, {
|
|
94067
|
+
method: "PUT",
|
|
94068
|
+
headers,
|
|
94069
|
+
body: input.bytes
|
|
94070
|
+
});
|
|
94071
|
+
if (!res.ok) {
|
|
94072
|
+
const body = await res.text().catch(() => "");
|
|
94073
|
+
const err = new Error("Linear file upload PUT failed");
|
|
94074
|
+
err.status = res.status;
|
|
94075
|
+
err.body = body;
|
|
94076
|
+
throw err;
|
|
94077
|
+
}
|
|
94078
|
+
return { assetUrl: up.assetUrl };
|
|
94079
|
+
}
|
|
94080
|
+
async function createAttachmentForUrl(apiKey, input) {
|
|
94081
|
+
const mutation = `mutation CreateAttachment(
|
|
94082
|
+
$issueId: String!, $url: String!, $title: String!, $subtitle: String
|
|
94083
|
+
) {
|
|
94084
|
+
attachmentCreate(input: { issueId: $issueId, url: $url, title: $title, subtitle: $subtitle }) {
|
|
94085
|
+
success
|
|
94086
|
+
attachment { id }
|
|
94087
|
+
}
|
|
94088
|
+
}`;
|
|
94089
|
+
const data = await linearRequest(apiKey, mutation, {
|
|
94090
|
+
issueId: input.issueId,
|
|
94091
|
+
url: input.url,
|
|
94092
|
+
title: input.title,
|
|
94093
|
+
subtitle: input.subtitle ?? null
|
|
94094
|
+
});
|
|
94095
|
+
const id = data.attachmentCreate.attachment?.id;
|
|
94096
|
+
if (!id)
|
|
94097
|
+
throw new Error("attachmentCreate returned no attachment id");
|
|
94098
|
+
return id;
|
|
94099
|
+
}
|
|
94100
|
+
async function updateAttachmentUrl(apiKey, attachmentId, url2, subtitle) {
|
|
94101
|
+
const mutation = subtitle === undefined ? `mutation UpdateAttachmentUrl($id: String!, $url: String!) {
|
|
94102
|
+
attachmentUpdate(id: $id, input: { url: $url }) { success }
|
|
94103
|
+
}` : `mutation UpdateAttachmentUrl($id: String!, $url: String!, $subtitle: String!) {
|
|
94104
|
+
attachmentUpdate(id: $id, input: { url: $url, subtitle: $subtitle }) { success }
|
|
94105
|
+
}`;
|
|
94106
|
+
const variables = subtitle === undefined ? { id: attachmentId, url: url2 } : { id: attachmentId, url: url2, subtitle };
|
|
94107
|
+
await linearRequest(apiKey, mutation, variables);
|
|
94108
|
+
}
|
|
93952
94109
|
async function addReactionToComment(apiKey, commentId, emoji3) {
|
|
93953
94110
|
const mutation = `mutation Reaction($commentId: String!, $emoji: String!) {
|
|
93954
94111
|
reactionCreate(input: { commentId: $commentId, emoji: $emoji }) { success }
|
|
@@ -94277,6 +94434,7 @@ async function removeLabelFromIssue(apiKey, issueId, labelId) {
|
|
|
94277
94434
|
}
|
|
94278
94435
|
var LINEAR_API = "https://api.linear.app/graphql", RALPHY_ATTACHMENT_TITLE_FILTER = "Ralphy", linearRequestInternals, MAX_LINEAR_ATTEMPTS = 3, MAX_RETRY_AFTER_MS = 2000, RALPHY_ATTACHMENT_TITLE = "Ralphy", BRANCH_LABEL_PREFIX = "ralph:branch:";
|
|
94279
94436
|
var init_linear = __esm(() => {
|
|
94437
|
+
init_types2();
|
|
94280
94438
|
linearRequestInternals = {
|
|
94281
94439
|
sleep: (ms) => Bun.sleep(ms)
|
|
94282
94440
|
};
|
|
@@ -94471,6 +94629,7 @@ class AgentCoordinator {
|
|
|
94471
94629
|
this.spawnNext();
|
|
94472
94630
|
const prStatus = await this.scanDoneForConflicts();
|
|
94473
94631
|
await this.reportProgress();
|
|
94632
|
+
await this.syncWorkerTasks();
|
|
94474
94633
|
const buckets = {
|
|
94475
94634
|
todo: todo.length,
|
|
94476
94635
|
inProgress: inProgress.length,
|
|
@@ -94525,12 +94684,26 @@ class AgentCoordinator {
|
|
|
94525
94684
|
} catch (err) {
|
|
94526
94685
|
this.deps.onLog(`! Linear progress comment failed for ${w.issueIdentifier}: ${err.message}`, "red");
|
|
94527
94686
|
}
|
|
94528
|
-
|
|
94529
|
-
|
|
94530
|
-
|
|
94531
|
-
|
|
94532
|
-
|
|
94533
|
-
|
|
94687
|
+
}
|
|
94688
|
+
}
|
|
94689
|
+
async syncWorkerTasks() {
|
|
94690
|
+
if (!this.deps.syncTasks || !this.deps.getIterationCount)
|
|
94691
|
+
return;
|
|
94692
|
+
for (const w of this.workers) {
|
|
94693
|
+
let count;
|
|
94694
|
+
try {
|
|
94695
|
+
count = await this.deps.getIterationCount(w.changeName);
|
|
94696
|
+
} catch (err) {
|
|
94697
|
+
this.deps.onLog(`! iteration count read failed for ${w.issueIdentifier}: ${err.message}`, "yellow");
|
|
94698
|
+
continue;
|
|
94699
|
+
}
|
|
94700
|
+
if (count === w.lastSyncedIteration)
|
|
94701
|
+
continue;
|
|
94702
|
+
try {
|
|
94703
|
+
await this.deps.syncTasks(w, count);
|
|
94704
|
+
w.lastSyncedIteration = count;
|
|
94705
|
+
} catch (err) {
|
|
94706
|
+
this.deps.onLog(`! sync-tasks (poll) failed for ${w.issueIdentifier}: ${err.message}`, "yellow");
|
|
94534
94707
|
}
|
|
94535
94708
|
}
|
|
94536
94709
|
}
|
|
@@ -94690,6 +94863,7 @@ class AgentCoordinator {
|
|
|
94690
94863
|
mode,
|
|
94691
94864
|
kill: handle.kill,
|
|
94692
94865
|
lastReportedIteration: 0,
|
|
94866
|
+
lastSyncedIteration: 0,
|
|
94693
94867
|
restarting: false
|
|
94694
94868
|
};
|
|
94695
94869
|
this.workers.push(worker);
|
|
@@ -94773,6 +94947,7 @@ class AgentCoordinator {
|
|
|
94773
94947
|
mode,
|
|
94774
94948
|
kill: () => {},
|
|
94775
94949
|
lastReportedIteration: 0,
|
|
94950
|
+
lastSyncedIteration: 0,
|
|
94776
94951
|
restarting: false
|
|
94777
94952
|
};
|
|
94778
94953
|
try {
|
|
@@ -94880,7 +95055,7 @@ var init_coordinator = __esm(() => {
|
|
|
94880
95055
|
import { join as join18 } from "path";
|
|
94881
95056
|
import { mkdir as mkdir5 } from "fs/promises";
|
|
94882
95057
|
function changeNameForIssue(issue2) {
|
|
94883
|
-
const slug = issue2.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "")
|
|
95058
|
+
const slug = issue2.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 40).replace(/^-+|-+$/g, "");
|
|
94884
95059
|
return slug ? `${issue2.identifier.toLowerCase()}-${slug}` : issue2.identifier.toLowerCase();
|
|
94885
95060
|
}
|
|
94886
95061
|
async function scaffoldChangeForIssue(tasksDir, statesDir, issue2, comments = [], appendPrompt = "") {
|
|
@@ -96235,24 +96410,28 @@ function truncate4(s, max2) {
|
|
|
96235
96410
|
\u2026(truncated)`;
|
|
96236
96411
|
}
|
|
96237
96412
|
function renderTasksBlock(tasksMd, meta3) {
|
|
96238
|
-
const sections = parseTasksMd(tasksMd);
|
|
96413
|
+
const sections = parseTasksMd(tasksMd).filter((s) => s.heading.trim().toLowerCase() !== "planning");
|
|
96239
96414
|
const out = [];
|
|
96240
96415
|
out.push(RALPHY_TASKS_START);
|
|
96241
96416
|
out.push("### Ralph progress");
|
|
96242
96417
|
out.push("");
|
|
96243
|
-
|
|
96244
|
-
|
|
96245
|
-
|
|
96246
|
-
out.push(`**${section.heading}**`);
|
|
96418
|
+
const renderable = sections.filter((s) => s.items.length > 0);
|
|
96419
|
+
if (renderable.length === 0) {
|
|
96420
|
+
out.push("_No mission tasks yet \u2014 planning in progress._");
|
|
96247
96421
|
out.push("");
|
|
96248
|
-
|
|
96249
|
-
|
|
96250
|
-
|
|
96251
|
-
|
|
96252
|
-
|
|
96422
|
+
} else {
|
|
96423
|
+
for (const section of renderable) {
|
|
96424
|
+
out.push(`**${section.heading}**`);
|
|
96425
|
+
out.push("");
|
|
96426
|
+
for (const item of section.items) {
|
|
96427
|
+
out.push(item.bullet);
|
|
96428
|
+
if (item.code !== undefined) {
|
|
96429
|
+
const inner = truncate4(item.code, MAX_CODE_BLOCK_BYTES);
|
|
96430
|
+
out.push(` <details><summary>output</summary><pre>${inner}</pre></details>`);
|
|
96431
|
+
}
|
|
96253
96432
|
}
|
|
96433
|
+
out.push("");
|
|
96254
96434
|
}
|
|
96255
|
-
out.push("");
|
|
96256
96435
|
}
|
|
96257
96436
|
out.push(`<sub>\`${meta3.changeName}\` \xB7 iteration ${meta3.iteration}</sub>`);
|
|
96258
96437
|
out.push(RALPHY_TASKS_END);
|
|
@@ -96485,9 +96664,139 @@ var init_comment_sync = __esm(() => {
|
|
|
96485
96664
|
init_linear_sync();
|
|
96486
96665
|
});
|
|
96487
96666
|
|
|
96488
|
-
// apps/agent/src/agent/
|
|
96489
|
-
import { join as join22 } from "path";
|
|
96667
|
+
// apps/agent/src/agent/linear-sync/spec-attachments.ts
|
|
96668
|
+
import { dirname as dirname8, join as join22 } from "path";
|
|
96490
96669
|
import { mkdir as mkdir7 } from "fs/promises";
|
|
96670
|
+
async function readStateJson2(statePath) {
|
|
96671
|
+
const file2 = Bun.file(statePath);
|
|
96672
|
+
if (!await file2.exists())
|
|
96673
|
+
return null;
|
|
96674
|
+
try {
|
|
96675
|
+
return await file2.json();
|
|
96676
|
+
} catch {
|
|
96677
|
+
return null;
|
|
96678
|
+
}
|
|
96679
|
+
}
|
|
96680
|
+
async function writeStateJson2(statePath, state) {
|
|
96681
|
+
await mkdir7(dirname8(statePath), { recursive: true });
|
|
96682
|
+
await Bun.write(statePath, JSON.stringify(state, null, 2) + `
|
|
96683
|
+
`);
|
|
96684
|
+
}
|
|
96685
|
+
function readSpecState(state) {
|
|
96686
|
+
const raw = state?.specAttachments ?? {};
|
|
96687
|
+
return {
|
|
96688
|
+
proposal: {
|
|
96689
|
+
attachmentId: raw?.proposal?.attachmentId ?? null,
|
|
96690
|
+
sha256: raw?.proposal?.sha256 ?? null
|
|
96691
|
+
},
|
|
96692
|
+
design: {
|
|
96693
|
+
attachmentId: raw?.design?.attachmentId ?? null,
|
|
96694
|
+
sha256: raw?.design?.sha256 ?? null
|
|
96695
|
+
}
|
|
96696
|
+
};
|
|
96697
|
+
}
|
|
96698
|
+
async function patchSpecState(statePath, patch) {
|
|
96699
|
+
const existing = await readStateJson2(statePath) ?? {};
|
|
96700
|
+
const current = readSpecState(existing);
|
|
96701
|
+
const next = { ...current, [patch.slot]: patch.value };
|
|
96702
|
+
await writeStateJson2(statePath, { ...existing, specAttachments: next });
|
|
96703
|
+
}
|
|
96704
|
+
function sha256Hex(bytes) {
|
|
96705
|
+
const hasher = new Bun.CryptoHasher("sha256");
|
|
96706
|
+
hasher.update(bytes);
|
|
96707
|
+
return hasher.digest("hex");
|
|
96708
|
+
}
|
|
96709
|
+
async function syncSlot(deps, slot) {
|
|
96710
|
+
const filename = SLOT_FILES[slot];
|
|
96711
|
+
const path = join22(deps.changeDir, filename);
|
|
96712
|
+
const file2 = Bun.file(path);
|
|
96713
|
+
if (!await file2.exists()) {
|
|
96714
|
+
deps.log(` spec-attachments: ${filename} missing, skipping`, "gray");
|
|
96715
|
+
return;
|
|
96716
|
+
}
|
|
96717
|
+
let bytes;
|
|
96718
|
+
try {
|
|
96719
|
+
bytes = await file2.bytes();
|
|
96720
|
+
} catch (err) {
|
|
96721
|
+
deps.log(`! spec-attachments: read ${filename} failed: ${err.message}`, "yellow");
|
|
96722
|
+
return;
|
|
96723
|
+
}
|
|
96724
|
+
const hash2 = sha256Hex(bytes);
|
|
96725
|
+
const state = await readStateJson2(deps.statePath);
|
|
96726
|
+
const current = readSpecState(state)[slot] ?? EMPTY_SLOT;
|
|
96727
|
+
if (current.attachmentId && current.sha256 === hash2) {
|
|
96728
|
+
deps.log(` spec-attachments: ${filename} unchanged, skipping`, "gray");
|
|
96729
|
+
return;
|
|
96730
|
+
}
|
|
96731
|
+
const subtitle = `iteration ${deps.iteration}`;
|
|
96732
|
+
let assetUrl;
|
|
96733
|
+
try {
|
|
96734
|
+
const uploaded = await deps.mutations.uploadFileToLinear(deps.apiKey, {
|
|
96735
|
+
filename,
|
|
96736
|
+
contentType: "text/markdown",
|
|
96737
|
+
bytes
|
|
96738
|
+
});
|
|
96739
|
+
assetUrl = uploaded.assetUrl;
|
|
96740
|
+
} catch (err) {
|
|
96741
|
+
deps.log(`! spec-attachments: upload ${filename} failed: ${err.message}`, "yellow");
|
|
96742
|
+
return;
|
|
96743
|
+
}
|
|
96744
|
+
if (current.attachmentId) {
|
|
96745
|
+
try {
|
|
96746
|
+
await deps.mutations.updateAttachmentUrl(deps.apiKey, current.attachmentId, assetUrl, subtitle);
|
|
96747
|
+
await patchSpecState(deps.statePath, {
|
|
96748
|
+
slot,
|
|
96749
|
+
value: { attachmentId: current.attachmentId, sha256: hash2 }
|
|
96750
|
+
});
|
|
96751
|
+
deps.log(` spec-attachments: refreshed ${filename}`, "gray");
|
|
96752
|
+
return;
|
|
96753
|
+
} catch (err) {
|
|
96754
|
+
if (!isCommentNotFoundError(err)) {
|
|
96755
|
+
deps.log(`! spec-attachments: updateAttachmentUrl ${filename} failed: ${err.message}`, "yellow");
|
|
96756
|
+
return;
|
|
96757
|
+
}
|
|
96758
|
+
deps.log(` spec-attachments: attachment ${current.attachmentId} not found \u2014 recreating`, "gray");
|
|
96759
|
+
}
|
|
96760
|
+
}
|
|
96761
|
+
let newId;
|
|
96762
|
+
try {
|
|
96763
|
+
newId = await deps.mutations.createAttachmentForUrl(deps.apiKey, {
|
|
96764
|
+
issueId: deps.issueId,
|
|
96765
|
+
url: assetUrl,
|
|
96766
|
+
title: ATTACHMENT_TITLES[slot],
|
|
96767
|
+
subtitle
|
|
96768
|
+
});
|
|
96769
|
+
} catch (err) {
|
|
96770
|
+
deps.log(`! spec-attachments: createAttachmentForUrl ${filename} failed: ${err.message}`, "yellow");
|
|
96771
|
+
return;
|
|
96772
|
+
}
|
|
96773
|
+
await patchSpecState(deps.statePath, {
|
|
96774
|
+
slot,
|
|
96775
|
+
value: { attachmentId: newId, sha256: hash2 }
|
|
96776
|
+
});
|
|
96777
|
+
deps.log(` spec-attachments: created ${filename} attachment`, "gray");
|
|
96778
|
+
}
|
|
96779
|
+
async function syncSpecAttachments(deps) {
|
|
96780
|
+
await syncSlot(deps, "proposal");
|
|
96781
|
+
await syncSlot(deps, "design");
|
|
96782
|
+
}
|
|
96783
|
+
var ATTACHMENT_TITLES, SLOT_FILES, EMPTY_SLOT;
|
|
96784
|
+
var init_spec_attachments = __esm(() => {
|
|
96785
|
+
init_comment_sync();
|
|
96786
|
+
ATTACHMENT_TITLES = {
|
|
96787
|
+
proposal: "Ralph proposal",
|
|
96788
|
+
design: "Ralph design"
|
|
96789
|
+
};
|
|
96790
|
+
SLOT_FILES = {
|
|
96791
|
+
proposal: "proposal.md",
|
|
96792
|
+
design: "design.md"
|
|
96793
|
+
};
|
|
96794
|
+
EMPTY_SLOT = { attachmentId: null, sha256: null };
|
|
96795
|
+
});
|
|
96796
|
+
|
|
96797
|
+
// apps/agent/src/agent/wire.ts
|
|
96798
|
+
import { join as join23 } from "path";
|
|
96799
|
+
import { mkdir as mkdir8 } from "fs/promises";
|
|
96491
96800
|
async function pickOpenPrUrlFromAttachments(urls, issueIdent, cmd, cwd2, onLog) {
|
|
96492
96801
|
const candidates = urls.filter((url2) => GITHUB_PR_URL_RE.test(url2));
|
|
96493
96802
|
let sawNonOpenPr = false;
|
|
@@ -96691,7 +97000,7 @@ function buildAgentCoordinator(input) {
|
|
|
96691
97000
|
onWorkerOutput,
|
|
96692
97001
|
onWorkerCmd
|
|
96693
97002
|
} = input;
|
|
96694
|
-
const logsDir =
|
|
97003
|
+
const logsDir = join23(projectRoot, ".ralph", "logs");
|
|
96695
97004
|
const concurrency = args.concurrency || cfg.concurrency;
|
|
96696
97005
|
const pollInterval = args.pollInterval || cfg.pollIntervalSeconds;
|
|
96697
97006
|
const indicators = mergeIndicators(cfg.linear.indicators, args.indicators);
|
|
@@ -96883,7 +97192,15 @@ function buildAgentCoordinator(input) {
|
|
|
96883
97192
|
async function prepare(issue2, mode, trigger) {
|
|
96884
97193
|
const { workerCwd, scaffoldTasksDir, scaffoldStatesDir, branch } = await setupWorktree(issue2);
|
|
96885
97194
|
let changeName;
|
|
96886
|
-
const
|
|
97195
|
+
const wtLayoutPre = projectLayout(workerCwd);
|
|
97196
|
+
const derivedName = changeNameForIssue(issue2);
|
|
97197
|
+
const tasksMdPath = join23(wtLayoutPre.changeDir(derivedName), "tasks.md");
|
|
97198
|
+
const tasksMdExists = await Bun.file(tasksMdPath).exists();
|
|
97199
|
+
const needsScaffold = !tasksMdExists;
|
|
97200
|
+
if (mode !== "fresh" && needsScaffold) {
|
|
97201
|
+
onLog(` ${issue2.identifier}: tasks.md missing at ${tasksMdPath} \u2014 rescaffolding`, "yellow");
|
|
97202
|
+
}
|
|
97203
|
+
const isFresh = mode === "fresh" || needsScaffold;
|
|
96887
97204
|
if (isFresh) {
|
|
96888
97205
|
let comments = [];
|
|
96889
97206
|
try {
|
|
@@ -96913,10 +97230,9 @@ function buildAgentCoordinator(input) {
|
|
|
96913
97230
|
`);
|
|
96914
97231
|
changeName = await scaffoldChangeForIssue(scaffoldTasksDir, scaffoldStatesDir, issue2, comments, appendPrompt);
|
|
96915
97232
|
} else {
|
|
96916
|
-
changeName =
|
|
96917
|
-
|
|
96918
|
-
await
|
|
96919
|
-
await mkdir7(wtLayout.taskStateDir(changeName), { recursive: true });
|
|
97233
|
+
changeName = derivedName;
|
|
97234
|
+
await mkdir8(wtLayoutPre.changeDir(changeName), { recursive: true });
|
|
97235
|
+
await mkdir8(wtLayoutPre.taskStateDir(changeName), { recursive: true });
|
|
96920
97236
|
}
|
|
96921
97237
|
cwdByChange.set(changeName, workerCwd);
|
|
96922
97238
|
statesDirByChange.set(changeName, scaffoldStatesDir);
|
|
@@ -96925,7 +97241,7 @@ function buildAgentCoordinator(input) {
|
|
|
96925
97241
|
branchByChange.set(changeName, branch);
|
|
96926
97242
|
if (mode === "review") {
|
|
96927
97243
|
const wtLayout = projectLayout(workerCwd);
|
|
96928
|
-
const tasksFile =
|
|
97244
|
+
const tasksFile = join23(wtLayout.changeDir(changeName), AGENT_TASKS_FILENAME);
|
|
96929
97245
|
let body;
|
|
96930
97246
|
let heading;
|
|
96931
97247
|
if (trigger) {
|
|
@@ -96950,7 +97266,7 @@ function buildAgentCoordinator(input) {
|
|
|
96950
97266
|
await reactivateState2(wtLayout.stateFile(changeName), changeName);
|
|
96951
97267
|
} else if (mode === "conflict-fix") {
|
|
96952
97268
|
const wtLayout = projectLayout(workerCwd);
|
|
96953
|
-
const tasksFile =
|
|
97269
|
+
const tasksFile = join23(wtLayout.changeDir(changeName), AGENT_TASKS_FILENAME);
|
|
96954
97270
|
const prUrl = prByChange.get(changeName);
|
|
96955
97271
|
const body = [
|
|
96956
97272
|
`The PR for this change has merge conflicts with \`${cfg.prBaseBranch}\`.`,
|
|
@@ -97030,7 +97346,7 @@ PR: ${prUrl}` : ""
|
|
|
97030
97346
|
return c;
|
|
97031
97347
|
}
|
|
97032
97348
|
function defaultSpawn(changeName, cmd, cwd2, note) {
|
|
97033
|
-
const logFilePath =
|
|
97349
|
+
const logFilePath = join23(logsDir, `${changeName}.log`);
|
|
97034
97350
|
const ANSI_RE2 = /\x1b(?:\[[0-9;]*[A-Za-z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|.)/g;
|
|
97035
97351
|
const BOX_ONLY_RE = /^[\s\u2500\u2502\u256D\u256E\u2570\u256F\u254C\u2504\u2501\u2503]+$/;
|
|
97036
97352
|
const STATUS_BAR_LINE_RE = /^[\u280B\u2819\u2839\u2838\u283C\u2834\u2826\u2827\u2807\u280F\u2713\u2717]\s+iter\s+\d+/;
|
|
@@ -97089,7 +97405,7 @@ PR: ${prUrl}` : ""
|
|
|
97089
97405
|
function spawnWorker(changeName) {
|
|
97090
97406
|
const cwd2 = cwdByChange.get(changeName) ?? projectRoot;
|
|
97091
97407
|
const injected = input.runners?.spawnWorker;
|
|
97092
|
-
const missionTasksPath =
|
|
97408
|
+
const missionTasksPath = join23(projectLayout(cwd2).changeDir(changeName), MISSION_TASKS_FILENAME);
|
|
97093
97409
|
const prevTasksPromise = (async () => {
|
|
97094
97410
|
const f2 = Bun.file(missionTasksPath);
|
|
97095
97411
|
return await f2.exists() ? await f2.text() : "";
|
|
@@ -97097,7 +97413,7 @@ PR: ${prUrl}` : ""
|
|
|
97097
97413
|
let logFilePath;
|
|
97098
97414
|
let handle;
|
|
97099
97415
|
if (injected) {
|
|
97100
|
-
logFilePath =
|
|
97416
|
+
logFilePath = join23(logsDir, `${changeName}.log`);
|
|
97101
97417
|
handle = injected(buildTaskCmdFor(changeName), cwd2);
|
|
97102
97418
|
} else {
|
|
97103
97419
|
const r = defaultSpawn(changeName, buildTaskCmdFor(changeName), cwd2, `spawn at ${new Date().toISOString()}`);
|
|
@@ -97313,7 +97629,15 @@ PR: ${prUrl}` : ""
|
|
|
97313
97629
|
const handle = cfg.linear.mentionHandle;
|
|
97314
97630
|
let candidates = [];
|
|
97315
97631
|
try {
|
|
97316
|
-
candidates = await fetchMentionScanIssues(apiKey, {
|
|
97632
|
+
candidates = await fetchMentionScanIssues(apiKey, {
|
|
97633
|
+
team,
|
|
97634
|
+
assignee,
|
|
97635
|
+
indicators: {
|
|
97636
|
+
...indicators.getTodo !== undefined ? { getTodo: indicators.getTodo } : {},
|
|
97637
|
+
...indicators.getInProgress !== undefined ? { getInProgress: indicators.getInProgress } : {},
|
|
97638
|
+
...indicators.setDone !== undefined ? { setDone: indicators.setDone } : {}
|
|
97639
|
+
}
|
|
97640
|
+
});
|
|
97317
97641
|
} catch (err) {
|
|
97318
97642
|
if (isRateLimitedError(err)) {
|
|
97319
97643
|
onLog(`! mention scan: rate limited, deferring rest of scan to next poll`, "yellow");
|
|
@@ -97604,6 +97928,12 @@ PR: ${prUrl}` : ""
|
|
|
97604
97928
|
updateIssueComment,
|
|
97605
97929
|
deleteIssueComment
|
|
97606
97930
|
};
|
|
97931
|
+
const specAttachmentsEnabled = Boolean(commentSyncEnabled && cfg.linear.syncSpecsAsAttachments);
|
|
97932
|
+
const specAttachmentMutations = {
|
|
97933
|
+
uploadFileToLinear,
|
|
97934
|
+
createAttachmentForUrl,
|
|
97935
|
+
updateAttachmentUrl
|
|
97936
|
+
};
|
|
97607
97937
|
const coord = new AgentCoordinator({
|
|
97608
97938
|
fetchTodo: () => fetchByGet(indicators.getTodo, excludeFromTodo),
|
|
97609
97939
|
fetchInProgress: () => fetchByGet(indicators.getInProgress, []),
|
|
@@ -97657,6 +97987,17 @@ PR: ${prUrl}` : ""
|
|
|
97657
97987
|
log: onLog,
|
|
97658
97988
|
mutations: commentMutations
|
|
97659
97989
|
});
|
|
97990
|
+
if (specAttachmentsEnabled) {
|
|
97991
|
+
await syncSpecAttachments({
|
|
97992
|
+
apiKey,
|
|
97993
|
+
issueId: worker.issueId,
|
|
97994
|
+
statePath,
|
|
97995
|
+
changeDir,
|
|
97996
|
+
iteration,
|
|
97997
|
+
log: onLog,
|
|
97998
|
+
mutations: specAttachmentMutations
|
|
97999
|
+
});
|
|
98000
|
+
}
|
|
97660
98001
|
},
|
|
97661
98002
|
onSteeringAppended: async (changeName, message) => {
|
|
97662
98003
|
const root = cwdByChange.get(changeName) ?? projectRoot;
|
|
@@ -97794,6 +98135,7 @@ var init_wire = __esm(() => {
|
|
|
97794
98135
|
init_gate();
|
|
97795
98136
|
init_workflow();
|
|
97796
98137
|
init_comment_sync();
|
|
98138
|
+
init_spec_attachments();
|
|
97797
98139
|
GITHUB_PR_URL_RE = /^https:\/\/github\.com\/[^/]+\/[^/]+\/pull\/\d+/;
|
|
97798
98140
|
bunGitRunner = {
|
|
97799
98141
|
run: async (args, cwd2) => {
|
|
@@ -98121,7 +98463,7 @@ var init_SteeringField = __esm(async () => {
|
|
|
98121
98463
|
});
|
|
98122
98464
|
|
|
98123
98465
|
// apps/agent/src/components/AgentMode.tsx
|
|
98124
|
-
import { join as
|
|
98466
|
+
import { join as join24 } from "path";
|
|
98125
98467
|
async function appendSteeringImpl(changeDir, message) {
|
|
98126
98468
|
await runWithContext(createDefaultContext(), async () => {
|
|
98127
98469
|
appendSteeringMessage(changeDir, message);
|
|
@@ -98576,7 +98918,7 @@ function AgentMode({
|
|
|
98576
98918
|
(async () => {
|
|
98577
98919
|
for (const [changeName, meta3] of workerMetaRef.current) {
|
|
98578
98920
|
try {
|
|
98579
|
-
const file2 = Bun.file(
|
|
98921
|
+
const file2 = Bun.file(join24(meta3.statesDir, changeName, ".ralph-state.json"));
|
|
98580
98922
|
if (await file2.exists()) {
|
|
98581
98923
|
const json2 = await file2.json();
|
|
98582
98924
|
meta3.iter = json2.iteration ?? meta3.iter;
|
|
@@ -98586,9 +98928,9 @@ function AgentMode({
|
|
|
98586
98928
|
}
|
|
98587
98929
|
if (meta3.changeDir) {
|
|
98588
98930
|
try {
|
|
98589
|
-
const tasksFile = Bun.file(
|
|
98590
|
-
const proposalFile = Bun.file(
|
|
98591
|
-
const designFile = Bun.file(
|
|
98931
|
+
const tasksFile = Bun.file(join24(meta3.changeDir, "tasks.md"));
|
|
98932
|
+
const proposalFile = Bun.file(join24(meta3.changeDir, "proposal.md"));
|
|
98933
|
+
const designFile = Bun.file(join24(meta3.changeDir, "design.md"));
|
|
98592
98934
|
const [tasksText, proposalText, designText] = await Promise.all([
|
|
98593
98935
|
tasksFile.exists().then((ok) => ok ? tasksFile.text() : null),
|
|
98594
98936
|
proposalFile.exists().then((ok) => ok ? proposalFile.text() : null),
|
|
@@ -98636,7 +98978,7 @@ function AgentMode({
|
|
|
98636
98978
|
use_input_default((input, key) => {
|
|
98637
98979
|
if (steeringFocusedRef.current)
|
|
98638
98980
|
return;
|
|
98639
|
-
if (key.ctrl &&
|
|
98981
|
+
if (key.ctrl && (input === "l" || input === "L")) {
|
|
98640
98982
|
if (activeCount > 0)
|
|
98641
98983
|
setShowAllSubtasks((v) => !v);
|
|
98642
98984
|
return;
|
|
@@ -99397,7 +99739,7 @@ function AgentMode({
|
|
|
99397
99739
|
}),
|
|
99398
99740
|
!showAllSubtasks && subtasks.length > MAX_PENDING_DISPLAY && /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
|
|
99399
99741
|
dimColor: true,
|
|
99400
|
-
children: ` \u2026 +${subtasks.length - MAX_PENDING_DISPLAY} more (CTRL+
|
|
99742
|
+
children: ` \u2026 +${subtasks.length - MAX_PENDING_DISPLAY} more (CTRL+L to expand)`
|
|
99401
99743
|
}, undefined, false, undefined, this)
|
|
99402
99744
|
]
|
|
99403
99745
|
}, undefined, true, undefined, this),
|
|
@@ -99419,7 +99761,7 @@ function AgentMode({
|
|
|
99419
99761
|
},
|
|
99420
99762
|
onSubmit: async (message) => {
|
|
99421
99763
|
try {
|
|
99422
|
-
await appendSteering(
|
|
99764
|
+
await appendSteering(join24(tasksDir, w.changeName), message);
|
|
99423
99765
|
} catch (err) {
|
|
99424
99766
|
appendLog(`! steering append failed for ${w.changeName}: ${err.message}`, "red");
|
|
99425
99767
|
throw err;
|
|
@@ -99629,7 +99971,7 @@ var exports_list = {};
|
|
|
99629
99971
|
__export(exports_list, {
|
|
99630
99972
|
runList: () => runList
|
|
99631
99973
|
});
|
|
99632
|
-
import { join as
|
|
99974
|
+
import { join as join25 } from "path";
|
|
99633
99975
|
function countTaskItems(content) {
|
|
99634
99976
|
const checked = (content.match(/^- \[x\]/gm) ?? []).length;
|
|
99635
99977
|
const unchecked = (content.match(/^- \[ \]/gm) ?? []).length;
|
|
@@ -99642,13 +99984,13 @@ function buildLocalRows(statesDir, projectRoot) {
|
|
|
99642
99984
|
const sources = [{ dir: statesDir, label: "main" }];
|
|
99643
99985
|
const worktreesRoot = worktreesDir2(projectRoot);
|
|
99644
99986
|
for (const wt of storage.list(worktreesRoot)) {
|
|
99645
|
-
sources.push({ dir:
|
|
99987
|
+
sources.push({ dir: join25(worktreesRoot, wt, ".ralph", "tasks"), label: `wt:${wt}` });
|
|
99646
99988
|
}
|
|
99647
99989
|
for (const { dir, label } of sources) {
|
|
99648
99990
|
for (const entry of storage.list(dir)) {
|
|
99649
99991
|
if (seen.has(entry))
|
|
99650
99992
|
continue;
|
|
99651
|
-
const raw = storage.read(
|
|
99993
|
+
const raw = storage.read(join25(dir, entry, ".ralph-state.json"));
|
|
99652
99994
|
if (raw === null)
|
|
99653
99995
|
continue;
|
|
99654
99996
|
let state;
|
|
@@ -99663,7 +100005,7 @@ function buildLocalRows(statesDir, projectRoot) {
|
|
|
99663
100005
|
const firstLine = promptRaw.split(`
|
|
99664
100006
|
`).find((l) => l.trim() !== "") ?? "";
|
|
99665
100007
|
let progress = "\u2014";
|
|
99666
|
-
const tasksContent = storage.read(
|
|
100008
|
+
const tasksContent = storage.read(join25(dir, entry, "tasks.md"));
|
|
99667
100009
|
if (tasksContent !== null) {
|
|
99668
100010
|
const { checked, unchecked } = countTaskItems(tasksContent);
|
|
99669
100011
|
const total = checked + unchecked;
|
|
@@ -100090,8 +100432,8 @@ var exports_json_runner = {};
|
|
|
100090
100432
|
__export(exports_json_runner, {
|
|
100091
100433
|
runAgentJson: () => runAgentJson
|
|
100092
100434
|
});
|
|
100093
|
-
import { join as
|
|
100094
|
-
import { mkdir as
|
|
100435
|
+
import { join as join26 } from "path";
|
|
100436
|
+
import { mkdir as mkdir9 } from "fs/promises";
|
|
100095
100437
|
import { homedir as homedir5 } from "os";
|
|
100096
100438
|
function cleanOutputLine2(raw) {
|
|
100097
100439
|
const clean = raw.replace(ANSI_STRIP_RE2, "").trim();
|
|
@@ -100115,7 +100457,7 @@ async function runAgentJson({
|
|
|
100115
100457
|
statesDir,
|
|
100116
100458
|
tasksDir
|
|
100117
100459
|
}) {
|
|
100118
|
-
await
|
|
100460
|
+
await mkdir9(join26(homedir5(), ".ralph"), { recursive: true }).catch(() => {
|
|
100119
100461
|
return;
|
|
100120
100462
|
});
|
|
100121
100463
|
const cfgPath = await ensureRalphyConfig(projectRoot);
|
|
@@ -100269,8 +100611,8 @@ var exports_src2 = {};
|
|
|
100269
100611
|
__export(exports_src2, {
|
|
100270
100612
|
main: () => main2
|
|
100271
100613
|
});
|
|
100272
|
-
import { mkdir as
|
|
100273
|
-
import { join as
|
|
100614
|
+
import { mkdir as mkdir10 } from "fs/promises";
|
|
100615
|
+
import { join as join27 } from "path";
|
|
100274
100616
|
async function main2(argv) {
|
|
100275
100617
|
if (argv.includes("--help") || argv.includes("-h")) {
|
|
100276
100618
|
printHelp2();
|
|
@@ -100304,9 +100646,9 @@ async function main2(argv) {
|
|
|
100304
100646
|
});
|
|
100305
100647
|
return typeof process.exitCode === "number" ? process.exitCode : 0;
|
|
100306
100648
|
}
|
|
100307
|
-
await
|
|
100308
|
-
await
|
|
100309
|
-
await
|
|
100649
|
+
await mkdir10(statesDir, { recursive: true });
|
|
100650
|
+
await mkdir10(tasksDir, { recursive: true });
|
|
100651
|
+
await mkdir10(join27(projectRoot, ".ralph"), { recursive: true });
|
|
100310
100652
|
if (args.jsonOutput) {
|
|
100311
100653
|
const { runAgentJson: runAgentJson2 } = await Promise.resolve().then(() => (init_json_runner(), exports_json_runner));
|
|
100312
100654
|
await runAgentJson2({ args, projectRoot, statesDir, tasksDir });
|