@neriros/ralphy 3.10.16 → 3.10.17
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/shell/index.js +722 -84
- package/package.json +2 -2
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.10.
|
|
18932
|
-
return "3.10.
|
|
18931
|
+
if ("3.10.17")
|
|
18932
|
+
return "3.10.17";
|
|
18933
18933
|
} catch {}
|
|
18934
18934
|
const dirsToTry = [];
|
|
18935
18935
|
try {
|
|
@@ -19395,7 +19395,7 @@ function modelOptionValues() {
|
|
|
19395
19395
|
const field = findField("model");
|
|
19396
19396
|
return field && field.spec.kind === "select" ? field.spec.options.map((o) => o.value) : [];
|
|
19397
19397
|
}
|
|
19398
|
-
var PROMPT_BODY_FIELD_ID = "promptBody", REPO_LINK_FIELD_ID = "repo.link", yes = () => ({ kind: "confirm", defaultChoice: "confirm" }), no = () => ({ kind: "confirm", defaultChoice: "cancel" }), PROJECT_NAME, LINEAR_TEAM, REPO_LINK, LINEAR_ASSIGNEE_CHOICE_FIELD_ID = "linear.assigneeChoice", LINEAR_ASSIGNEE_VALUE_FIELD_ID = "linear.assigneeValue", LINEAR_FILTER_DESCRIPTION, LINEAR_ASSIGNEE_CHOICE, LINEAR_ASSIGNEE_VALUE, QUICK_FIELDS, isOn = (id) => (answers) => answers[id] === true, concurrencyForcesWorktree = (answers) => {
|
|
19398
|
+
var PROMPT_BODY_FIELD_ID = "promptBody", REPO_LINK_FIELD_ID = "repo.link", yes = () => ({ kind: "confirm", defaultChoice: "confirm" }), no = () => ({ kind: "confirm", defaultChoice: "cancel" }), PROJECT_NAME, LINEAR_TEAM, REPO_LINK, LINEAR_ASSIGNEE_CHOICE_FIELD_ID = "linear.assigneeChoice", LINEAR_ASSIGNEE_VALUE_FIELD_ID = "linear.assigneeValue", LINEAR_FILTER_DESCRIPTION, LINEAR_ASSIGNEE_CHOICE, LINEAR_ASSIGNEE_VALUE, QUICK_FIELDS, isOn = (id) => (answers) => answers[id] === true, trackerIsGithub = (answers) => answers["tracker.kind"] === "github", concurrencyForcesWorktree = (answers) => {
|
|
19399
19399
|
const value = answers["concurrency"];
|
|
19400
19400
|
return typeof value === "number" && value > 1;
|
|
19401
19401
|
}, worktreeEnabled = (answers) => answers["useWorktree"] === true || concurrencyForcesWorktree(answers), HIDDEN_FIELD_IDS, CUSTOMIZED_FIELDS, COMMON_CLI_OPTIONS, FIELD_DESCRIPTIONS;
|
|
@@ -19537,6 +19537,66 @@ var init_fields = __esm(() => {
|
|
|
19537
19537
|
description: "Show detailed per-task output (passes --verbose to the task sub-process) for extra diagnostics.",
|
|
19538
19538
|
spec: no()
|
|
19539
19539
|
},
|
|
19540
|
+
{
|
|
19541
|
+
id: "tracker.kind",
|
|
19542
|
+
label: "Issue tracker",
|
|
19543
|
+
description: "Which issue tracker drives the loop: 'linear' (the default) or 'github' (GitHub Issues, via the gh CLI).",
|
|
19544
|
+
spec: {
|
|
19545
|
+
kind: "select",
|
|
19546
|
+
options: [
|
|
19547
|
+
{ label: "Linear", value: "linear" },
|
|
19548
|
+
{ label: "GitHub", value: "github" }
|
|
19549
|
+
]
|
|
19550
|
+
}
|
|
19551
|
+
},
|
|
19552
|
+
{
|
|
19553
|
+
id: "github.issues.repo",
|
|
19554
|
+
label: "GitHub repository (owner/name)",
|
|
19555
|
+
hint: "blank = detected from origin",
|
|
19556
|
+
description: "The owner/name of the GitHub repo whose issues drive the loop. Leave blank to use the repo detected from the git 'origin' remote.",
|
|
19557
|
+
emptyLabel: "detected from origin",
|
|
19558
|
+
spec: { kind: "text", placeholder: "owner/name" },
|
|
19559
|
+
when: trackerIsGithub
|
|
19560
|
+
},
|
|
19561
|
+
{
|
|
19562
|
+
id: "github.issues.label",
|
|
19563
|
+
label: "Todo label",
|
|
19564
|
+
hint: "blank = any open issue",
|
|
19565
|
+
description: "Only pick up GitHub issues carrying this label. Leave blank to consider every open issue.",
|
|
19566
|
+
emptyLabel: "any open issue",
|
|
19567
|
+
spec: { kind: "text", placeholder: "ralph:todo" },
|
|
19568
|
+
when: trackerIsGithub
|
|
19569
|
+
},
|
|
19570
|
+
{
|
|
19571
|
+
id: "github.issues.assignee",
|
|
19572
|
+
label: "Assignee filter",
|
|
19573
|
+
hint: "blank = any assignee; @me = you",
|
|
19574
|
+
description: "Only pick up GitHub issues assigned to this login. Use '@me' for yourself; leave blank to ignore the assignee.",
|
|
19575
|
+
emptyLabel: "any assignee",
|
|
19576
|
+
spec: { kind: "text", placeholder: "@me" },
|
|
19577
|
+
when: trackerIsGithub
|
|
19578
|
+
},
|
|
19579
|
+
{
|
|
19580
|
+
id: "github.issues.statusLabels.inProgress",
|
|
19581
|
+
label: "In-progress label",
|
|
19582
|
+
description: "GitHub label applied to an issue while Ralphy is actively working on it (and removed from the todo label).",
|
|
19583
|
+
spec: { kind: "text", placeholder: "ralph:in-progress" },
|
|
19584
|
+
when: trackerIsGithub
|
|
19585
|
+
},
|
|
19586
|
+
{
|
|
19587
|
+
id: "github.issues.statusLabels.done",
|
|
19588
|
+
label: "Done label",
|
|
19589
|
+
description: "GitHub label applied to an issue when its work completes; the issue is also closed.",
|
|
19590
|
+
spec: { kind: "text", placeholder: "ralph:done" },
|
|
19591
|
+
when: trackerIsGithub
|
|
19592
|
+
},
|
|
19593
|
+
{
|
|
19594
|
+
id: "github.issues.statusLabels.error",
|
|
19595
|
+
label: "Error label",
|
|
19596
|
+
description: "GitHub label applied to an issue when Ralphy quarantines it after repeated failures.",
|
|
19597
|
+
spec: { kind: "text", placeholder: "ralph:error" },
|
|
19598
|
+
when: trackerIsGithub
|
|
19599
|
+
},
|
|
19540
19600
|
{
|
|
19541
19601
|
id: "concurrency",
|
|
19542
19602
|
label: "Concurrency (parallel tasks)",
|
|
@@ -81255,7 +81315,7 @@ function foldLegacyAssignee(v) {
|
|
|
81255
81315
|
}
|
|
81256
81316
|
return rest2;
|
|
81257
81317
|
}
|
|
81258
|
-
var CURRENT_WORKFLOW_VERSION =
|
|
81318
|
+
var CURRENT_WORKFLOW_VERSION = 7, MarkerSchema, FilterMarkerSchema, LinearFilterSchema, SET_INDICATOR_KEYS, GetIndicatorSchema, SetIndicatorSchema, IndicatorsSchema, ProjectSchema, CommandsSchema, DEFAULT_META_ONLY_FILES, BoundariesSchema, WorkflowConfigSchema;
|
|
81259
81319
|
var init_schema = __esm(() => {
|
|
81260
81320
|
init_zod();
|
|
81261
81321
|
MarkerSchema = exports_external.discriminatedUnion("type", [
|
|
@@ -81401,6 +81461,9 @@ var init_schema = __esm(() => {
|
|
|
81401
81461
|
finalizeNoOpAsDone: exports_external.boolean().default(true),
|
|
81402
81462
|
engine: exports_external.enum(["claude", "codex"]).default("claude"),
|
|
81403
81463
|
model: exports_external.enum(["haiku", "sonnet", "opus"]).default("opus"),
|
|
81464
|
+
tracker: exports_external.object({
|
|
81465
|
+
kind: exports_external.enum(["linear", "github"]).default("linear")
|
|
81466
|
+
}).strict().default({ kind: "linear" }),
|
|
81404
81467
|
linear: exports_external.preprocess(foldLegacyAssignee, exports_external.object({
|
|
81405
81468
|
team: exports_external.string().optional(),
|
|
81406
81469
|
filter: LinearFilterSchema,
|
|
@@ -81445,7 +81508,21 @@ var init_schema = __esm(() => {
|
|
|
81445
81508
|
}),
|
|
81446
81509
|
github: exports_external.object({
|
|
81447
81510
|
base_branch: exports_external.string().optional(),
|
|
81448
|
-
auto_merge_strategy: exports_external.enum(["squash", "merge", "rebase"]).optional()
|
|
81511
|
+
auto_merge_strategy: exports_external.enum(["squash", "merge", "rebase"]).optional(),
|
|
81512
|
+
issues: exports_external.object({
|
|
81513
|
+
repo: exports_external.string().optional(),
|
|
81514
|
+
label: exports_external.string().optional(),
|
|
81515
|
+
assignee: exports_external.string().optional(),
|
|
81516
|
+
statusLabels: exports_external.object({
|
|
81517
|
+
inProgress: exports_external.string().default("ralph:in-progress"),
|
|
81518
|
+
done: exports_external.string().default("ralph:done"),
|
|
81519
|
+
error: exports_external.string().default("ralph:error")
|
|
81520
|
+
}).strict().default({
|
|
81521
|
+
inProgress: "ralph:in-progress",
|
|
81522
|
+
done: "ralph:done",
|
|
81523
|
+
error: "ralph:error"
|
|
81524
|
+
})
|
|
81525
|
+
}).strict().optional()
|
|
81449
81526
|
}).strict().optional(),
|
|
81450
81527
|
agent: exports_external.object({
|
|
81451
81528
|
engine: exports_external.enum(["claude", "codex"]).optional(),
|
|
@@ -81514,7 +81591,7 @@ var init_schema = __esm(() => {
|
|
|
81514
81591
|
var FRONTMATTER_RE, DEFAULT_WORKFLOW_MD = `---
|
|
81515
81592
|
# WORKFLOW.md schema version \u2014 managed by \`ralphy init\`. When a newer version
|
|
81516
81593
|
# ships, re-running init migrates this file and fills in the new settings.
|
|
81517
|
-
version:
|
|
81594
|
+
version: 7
|
|
81518
81595
|
|
|
81519
81596
|
project:
|
|
81520
81597
|
name: ralphy
|
|
@@ -81581,6 +81658,16 @@ preExistingErrorCheck:
|
|
|
81581
81658
|
label: "ralph:pre-existing-error"
|
|
81582
81659
|
outputCharLimit: 4000
|
|
81583
81660
|
|
|
81661
|
+
tracker:
|
|
81662
|
+
kind: linear
|
|
81663
|
+
|
|
81664
|
+
github:
|
|
81665
|
+
issues:
|
|
81666
|
+
statusLabels:
|
|
81667
|
+
inProgress: "ralph:in-progress"
|
|
81668
|
+
done: "ralph:done"
|
|
81669
|
+
error: "ralph:error"
|
|
81670
|
+
|
|
81584
81671
|
linear:
|
|
81585
81672
|
# Global filter ANDed into every Linear query (and the GitHub PR searches
|
|
81586
81673
|
# rooted at those issues). A marker list of \`assignee\` and \`label\` clauses;
|
|
@@ -83405,6 +83492,12 @@ function buildFromAnswers(mode, answers, build = buildWorkflowMarkdown) {
|
|
|
83405
83492
|
}
|
|
83406
83493
|
}
|
|
83407
83494
|
}
|
|
83495
|
+
if (values2["tracker.kind"] !== "github") {
|
|
83496
|
+
for (const id of Object.keys(values2)) {
|
|
83497
|
+
if (id.startsWith("github.issues."))
|
|
83498
|
+
delete values2[id];
|
|
83499
|
+
}
|
|
83500
|
+
}
|
|
83408
83501
|
const linkRepo = values2[REPO_LINK_FIELD_ID] === true;
|
|
83409
83502
|
delete values2[REPO_LINK_FIELD_ID];
|
|
83410
83503
|
if (!linkRepo) {
|
|
@@ -84585,6 +84678,19 @@ var init_migrations = __esm(() => {
|
|
|
84585
84678
|
version: 6,
|
|
84586
84679
|
description: "PR recovery is unified under one `prRecovery` block (replacing " + "`prTracker`, `fixCiOnFailure`, `maxCiFixAttempts`, `ciPollIntervalSeconds`, " + "and `ignoreCiChecks`). Workers now open the PR and leave the ticket " + "in-review; a single background watcher advances it to done once the PR is " + "mergeable (CI green, no conflicts) and recovers red PRs \u2014 resolving merge " + "conflicts AND fixing failing CI (both `prRecovery.fixConflicts` and " + "`prRecovery.fixCi` default on; tune them in WORKFLOW.md). " + "`prRecovery.enabled: false` turns the watcher off everywhere and marks the " + "ticket done immediately on PR open. Your old values are migrated " + "automatically; review them here or keep them.",
|
|
84587
84680
|
fields: ["prRecovery.enabled", "prRecovery.maxRecoverySessions", "prRecovery.ignoreChecks"]
|
|
84681
|
+
},
|
|
84682
|
+
{
|
|
84683
|
+
version: 7,
|
|
84684
|
+
description: "Pick your issue tracker: Linear (default) or GitHub Issues. The new " + "`tracker.kind` switch selects the provider; choosing GitHub drives the " + "loop off `gh` issues, filtered by `github.issues.label`/`assignee` and " + "moved through the `github.issues.statusLabels` (in-progress / done / " + "error). Existing files default to Linear, so nothing changes unless you " + "switch.",
|
|
84685
|
+
fields: [
|
|
84686
|
+
"tracker.kind",
|
|
84687
|
+
"github.issues.repo",
|
|
84688
|
+
"github.issues.label",
|
|
84689
|
+
"github.issues.assignee",
|
|
84690
|
+
"github.issues.statusLabels.inProgress",
|
|
84691
|
+
"github.issues.statusLabels.done",
|
|
84692
|
+
"github.issues.statusLabels.error"
|
|
84693
|
+
]
|
|
84588
84694
|
}
|
|
84589
84695
|
];
|
|
84590
84696
|
LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max2, migration) => Math.max(max2, migration.version), 0);
|
|
@@ -100164,7 +100270,7 @@ function normalizeNewlyAppendedSectionWithReport(previous, current) {
|
|
|
100164
100270
|
});
|
|
100165
100271
|
return { text: count > 0 ? out.join("") : current, headings, count };
|
|
100166
100272
|
}
|
|
100167
|
-
var MISSION_TASKS_FILENAME = "tasks.md", AGENT_TASKS_FILENAME = "agent-tasks.md", FLOW_TASK_HEADING_PREFIXES;
|
|
100273
|
+
var MISSION_TASKS_FILENAME = "tasks.md", AGENT_TASKS_FILENAME = "agent-tasks.md", HANDOFF_FILENAME = "handoff.md", FLOW_TASK_HEADING_PREFIXES;
|
|
100168
100274
|
var init_tasks_md = __esm(() => {
|
|
100169
100275
|
FLOW_TASK_HEADING_PREFIXES = [
|
|
100170
100276
|
"Fix failing CI checks",
|
|
@@ -100417,6 +100523,20 @@ function buildTaskPrompt(state, taskDir, reviewPhase) {
|
|
|
100417
100523
|
`;
|
|
100418
100524
|
}
|
|
100419
100525
|
}
|
|
100526
|
+
const handoffContent = storage.read(join15(taskDir, HANDOFF_FILENAME));
|
|
100527
|
+
if (handoffContent !== null && handoffContent.trim()) {
|
|
100528
|
+
prompt += `---
|
|
100529
|
+
`;
|
|
100530
|
+
prompt += `# Previous Iteration Handoff (context from the last iteration)
|
|
100531
|
+
|
|
100532
|
+
`;
|
|
100533
|
+
prompt += handoffContent.trim() + `
|
|
100534
|
+
|
|
100535
|
+
`;
|
|
100536
|
+
prompt += `---
|
|
100537
|
+
|
|
100538
|
+
`;
|
|
100539
|
+
}
|
|
100420
100540
|
const agentTasksPath = join15(taskDir, AGENT_TASKS_FILENAME);
|
|
100421
100541
|
const missionTasksPath = join15(taskDir, MISSION_TASKS_FILENAME);
|
|
100422
100542
|
const agentTasksContent = storage.read(agentTasksPath);
|
|
@@ -100564,6 +100684,22 @@ When all tasks are complete and all files are committed, push your branch and op
|
|
|
100564
100684
|
prompt += `Use the change name as the PR title and write a concise summary of the implementation in the body.
|
|
100565
100685
|
`;
|
|
100566
100686
|
}
|
|
100687
|
+
const handoffPath = join15(taskDir, HANDOFF_FILENAME);
|
|
100688
|
+
prompt += `
|
|
100689
|
+
---
|
|
100690
|
+
|
|
100691
|
+
## Write Handoff (do this LAST, before you finish)
|
|
100692
|
+
`;
|
|
100693
|
+
prompt += `Before ending this iteration, record a handoff for the next iteration:
|
|
100694
|
+
`;
|
|
100695
|
+
prompt += `- If a \`handoff\` skill is available, invoke it; otherwise write the document yourself.
|
|
100696
|
+
`;
|
|
100697
|
+
prompt += `- Save it to \`${handoffPath}\` (OVERWRITE the existing file \u2014 do NOT append, do NOT save to a temp dir).
|
|
100698
|
+
`;
|
|
100699
|
+
prompt += `- Keep it compact: what you did this iteration, what remains, key decisions, and any blockers/gotchas. Reference artifacts by path instead of duplicating them.
|
|
100700
|
+
`;
|
|
100701
|
+
prompt += `- This file is loop scratch \u2014 do NOT commit it.
|
|
100702
|
+
`;
|
|
100567
100703
|
return prompt;
|
|
100568
100704
|
}
|
|
100569
100705
|
function buildSteeringBlock(taskDir) {
|
|
@@ -102600,13 +102736,91 @@ class PollContext {
|
|
|
102600
102736
|
}
|
|
102601
102737
|
}
|
|
102602
102738
|
|
|
102603
|
-
//
|
|
102604
|
-
function
|
|
102739
|
+
// packages/comms/src/index.ts
|
|
102740
|
+
function sanitizeMarkerValue(value) {
|
|
102741
|
+
return value.replace(/--+>?/g, "-").replace(/\s+/g, "_").trim();
|
|
102742
|
+
}
|
|
102743
|
+
function buildRalphyMarker(type, fields) {
|
|
102744
|
+
const pairs = [`v=${RALPHY_MARKER_VERSION}`, `type=${type}`];
|
|
102745
|
+
for (const [key, raw] of Object.entries(fields ?? {})) {
|
|
102746
|
+
if (raw === undefined || raw === null || raw === "")
|
|
102747
|
+
continue;
|
|
102748
|
+
pairs.push(`${key}=${sanitizeMarkerValue(String(raw))}`);
|
|
102749
|
+
}
|
|
102750
|
+
return `<!-- ralphy:${pairs.join(" ")} -->`;
|
|
102751
|
+
}
|
|
102752
|
+
function parseRalphyMarker(body) {
|
|
102753
|
+
const match = /<!--\s*ralphy:([^>]*?\btype=[^>]*?)\s*-->/.exec(body);
|
|
102754
|
+
if (!match)
|
|
102755
|
+
return null;
|
|
102756
|
+
const fields = {};
|
|
102757
|
+
let type = "";
|
|
102758
|
+
let version3 = 0;
|
|
102759
|
+
for (const token of match[1].trim().split(/\s+/)) {
|
|
102760
|
+
const eq = token.indexOf("=");
|
|
102761
|
+
if (eq < 0)
|
|
102762
|
+
continue;
|
|
102763
|
+
const key = token.slice(0, eq);
|
|
102764
|
+
const value = token.slice(eq + 1);
|
|
102765
|
+
if (key === "v")
|
|
102766
|
+
version3 = Number(value) || 0;
|
|
102767
|
+
else if (key === "type")
|
|
102768
|
+
type = value;
|
|
102769
|
+
else
|
|
102770
|
+
fields[key] = value;
|
|
102771
|
+
}
|
|
102772
|
+
if (!type)
|
|
102773
|
+
return null;
|
|
102774
|
+
return { version: version3, type, fields };
|
|
102775
|
+
}
|
|
102776
|
+
function buildRalphyComment(input) {
|
|
102777
|
+
const lines = [`${RALPHY_TITLE_PREFIX}${input.action}`];
|
|
102778
|
+
const body = input.body?.trim();
|
|
102779
|
+
if (body)
|
|
102780
|
+
lines.push("", body);
|
|
102781
|
+
lines.push("", buildRalphyMarker(input.type, input.fields));
|
|
102782
|
+
return lines.join(`
|
|
102783
|
+
`);
|
|
102784
|
+
}
|
|
102785
|
+
function isRalphyComment(body) {
|
|
102605
102786
|
const trimmed = body.trimStart();
|
|
102606
|
-
if (
|
|
102787
|
+
if (trimmed.startsWith(RALPHY_BRAND))
|
|
102788
|
+
return true;
|
|
102789
|
+
if (parseRalphyMarker(body))
|
|
102790
|
+
return true;
|
|
102791
|
+
if (LEGACY_RALPH_LEAD.test(trimmed))
|
|
102792
|
+
return true;
|
|
102793
|
+
return LEGACY_MENTION_ACK.test(trimmed);
|
|
102794
|
+
}
|
|
102795
|
+
function isPickupComment(body) {
|
|
102796
|
+
if (parseRalphyMarker(body)?.type === "review-pickup")
|
|
102797
|
+
return true;
|
|
102798
|
+
return /^\uD83D\uDD01\s*Ralph picked up/u.test(body.trimStart());
|
|
102799
|
+
}
|
|
102800
|
+
function isStartedComment(body) {
|
|
102801
|
+
if (parseRalphyMarker(body)?.type === "started")
|
|
102802
|
+
return true;
|
|
102803
|
+
return /^\uD83E\uDD16\s*Ralph started working/u.test(body.trimStart());
|
|
102804
|
+
}
|
|
102805
|
+
function isMentionAckComment(body) {
|
|
102806
|
+
if (parseRalphyMarker(body)?.type === "mention-ack")
|
|
102607
102807
|
return true;
|
|
102608
|
-
return
|
|
102808
|
+
return LEGACY_MENTION_ACK.test(body.trimStart());
|
|
102809
|
+
}
|
|
102810
|
+
var RALPHY_BRAND = "\uD83E\uDD16 Ralphy", RALPHY_TITLE_PREFIX, RALPHY_MARKER_VERSION = 1, LEGACY_RALPH_LEAD, LEGACY_MENTION_ACK;
|
|
102811
|
+
var init_src8 = __esm(() => {
|
|
102812
|
+
RALPHY_TITLE_PREFIX = `${RALPHY_BRAND} \xB7 `;
|
|
102813
|
+
LEGACY_RALPH_LEAD = /^(\uD83E\uDD16|\uD83D\uDD04|\u2705|\u2717|\u274C|\u26A0\uFE0F?|\uD83D\uDD01|\uD83D\uDCCB|\u23F0|\u2139\uFE0F)\s*Ralphy?\b/u;
|
|
102814
|
+
LEGACY_MENTION_ACK = /^\uD83D\uDC40\s*(Got it\b|Acknowledged\b)/u;
|
|
102815
|
+
});
|
|
102816
|
+
|
|
102817
|
+
// apps/agent/src/shared/utils/ralph-comment.ts
|
|
102818
|
+
function isRalphComment(body) {
|
|
102819
|
+
return isRalphyComment(body);
|
|
102609
102820
|
}
|
|
102821
|
+
var init_ralph_comment = __esm(() => {
|
|
102822
|
+
init_src8();
|
|
102823
|
+
});
|
|
102610
102824
|
|
|
102611
102825
|
// apps/agent/src/shared/capabilities/linear-client.ts
|
|
102612
102826
|
var exports_linear_client = {};
|
|
@@ -103543,6 +103757,7 @@ async function removeLabelFromIssue(apiKey, issueId, labelId) {
|
|
|
103543
103757
|
var LINEAR_API = "https://api.linear.app/graphql", TICKET_IDENTIFIER_RE, TICKET_BARE_NUMBER_RE, RALPHY_ATTACHMENT_TITLE_FILTER = "Ralphy", linearRequestInternals, MAX_LINEAR_ATTEMPTS = 3, MAX_RETRY_AFTER_MS = 2000, BODY_TRUNCATE_CHARS = 512, RALPHY_ATTACHMENT_TITLE = "Ralphy", BRANCH_LABEL_PREFIX = "ralph:branch:";
|
|
103544
103758
|
var init_linear_client = __esm(() => {
|
|
103545
103759
|
init_types2();
|
|
103760
|
+
init_ralph_comment();
|
|
103546
103761
|
TICKET_IDENTIFIER_RE = /^([A-Za-z]+)-(\d+)(?:-.*)?$/;
|
|
103547
103762
|
TICKET_BARE_NUMBER_RE = /^(\d+)$/;
|
|
103548
103763
|
linearRequestInternals = {
|
|
@@ -103885,7 +104100,7 @@ function stackedOnLine(stackedOn) {
|
|
|
103885
104100
|
return `> \uD83E\uDD5E Stacked on ${prRef}${ticket} \u2014 review/merge that PR first. Base is its branch, not the default branch.`;
|
|
103886
104101
|
}
|
|
103887
104102
|
function defaultBody(issue2, branch, stackedOn) {
|
|
103888
|
-
|
|
104103
|
+
const detail = [
|
|
103889
104104
|
`Auto-generated by Ralph for ${issue2.identifier}.`,
|
|
103890
104105
|
stackedOn ? stackedOnLine(stackedOn) : "",
|
|
103891
104106
|
`Source: ${issue2.url}`,
|
|
@@ -103896,6 +104111,12 @@ function defaultBody(issue2, branch, stackedOn) {
|
|
|
103896
104111
|
${issue2.description.trim()}` : ""
|
|
103897
104112
|
].filter(Boolean).join(`
|
|
103898
104113
|
`);
|
|
104114
|
+
return buildRalphyComment({
|
|
104115
|
+
type: "pr-body",
|
|
104116
|
+
action: "opened PR",
|
|
104117
|
+
body: detail,
|
|
104118
|
+
fields: { issue: issue2.identifier }
|
|
104119
|
+
});
|
|
103899
104120
|
}
|
|
103900
104121
|
async function diffFilesAgainstBase(runner, cwd2, base2) {
|
|
103901
104122
|
let raw = "";
|
|
@@ -104015,7 +104236,9 @@ async function createPullRequest(input, runner) {
|
|
|
104015
104236
|
`).pop() ?? "";
|
|
104016
104237
|
return { url: url2, created: true };
|
|
104017
104238
|
}
|
|
104018
|
-
var init_pr = () => {
|
|
104239
|
+
var init_pr = __esm(() => {
|
|
104240
|
+
init_src8();
|
|
104241
|
+
});
|
|
104019
104242
|
|
|
104020
104243
|
// apps/agent/src/shared/pr/ci-classify.ts
|
|
104021
104244
|
async function runGhWithRetry(cmd, runner, cwd2, onRetry, sleep2 = (ms) => new Promise((r) => setTimeout(r, ms))) {
|
|
@@ -105126,6 +105349,25 @@ async function runPostTask(input, deps) {
|
|
|
105126
105349
|
}
|
|
105127
105350
|
if (input.mode === "conflict-fix" && effectiveCode === 0) {
|
|
105128
105351
|
const identifier = issue2?.identifier ?? changeName;
|
|
105352
|
+
if (branch) {
|
|
105353
|
+
let aheadCount = 0;
|
|
105354
|
+
let checked = true;
|
|
105355
|
+
try {
|
|
105356
|
+
const r = await cmd.run(["git", "rev-list", "--count", `origin/${branch}..HEAD`], cwd2);
|
|
105357
|
+
aheadCount = Number.parseInt(r.stdout.trim(), 10) || 0;
|
|
105358
|
+
} catch (err) {
|
|
105359
|
+
checked = false;
|
|
105360
|
+
log3(`! ${identifier}: could not check for unpushed conflict-fix commits: ${err.message}`, "yellow");
|
|
105361
|
+
}
|
|
105362
|
+
if (checked && aheadCount > 0) {
|
|
105363
|
+
log3(`! ${identifier}: conflict-fix worker left ${aheadCount} unpushed commit(s) ahead of ` + `origin/${branch} \u2014 the resolution never reached the PR. Failing the iteration so it ` + `is retried instead of reported as resolved.`, "red");
|
|
105364
|
+
emit3("gave-up", "unpushed conflict resolution");
|
|
105365
|
+
await recordGaveUp(stateFilePath, log3, changeName);
|
|
105366
|
+
await runWorktreeCleanupPhase({ changeName, cwd: cwd2, projectRoot, useWorktree, effectiveCode: PR_FAILED_EXIT, cfg }, { git: git2, log: log3, emit: emit3 });
|
|
105367
|
+
await runTeardownPhase({ cwd: cwd2, teardownScript: cfg.teardownScript }, { runScript, log: log3, emit: emit3 });
|
|
105368
|
+
return PR_FAILED_EXIT;
|
|
105369
|
+
}
|
|
105370
|
+
}
|
|
105129
105371
|
let prUrl = input.prUrl ?? null;
|
|
105130
105372
|
if (!prUrl && branch) {
|
|
105131
105373
|
prUrl = await findExistingOpenPrUrl(cmd, cwd2, branch);
|
|
@@ -105500,19 +105742,37 @@ function emitCapture(bus, event, properties) {
|
|
|
105500
105742
|
function completionCommentBody(args) {
|
|
105501
105743
|
const { noChanges, ok, trigger, changeName, code } = args;
|
|
105502
105744
|
if (noChanges) {
|
|
105503
|
-
return
|
|
105745
|
+
return buildRalphyComment({
|
|
105746
|
+
type: "completed-noop",
|
|
105747
|
+
action: "completed \u2014 no code changes",
|
|
105748
|
+
body: `Completed all tasks for this issue but produced no code changes \u2014 the requested ` + `work appears to already be present on the base branch (or was a no-op). No PR was ` + `opened. Change: \`${changeName}\`
|
|
105504
105749
|
|
|
105505
|
-
` + `Marking this done; please verify the work is genuinely in place. If it is not, ` + `reopen the issue with more specifics
|
|
105750
|
+
` + `Marking this done; please verify the work is genuinely in place. If it is not, ` + `reopen the issue with more specifics.`,
|
|
105751
|
+
fields: { change: changeName }
|
|
105752
|
+
});
|
|
105506
105753
|
}
|
|
105507
105754
|
if (!ok) {
|
|
105508
|
-
return
|
|
105509
|
-
|
|
105510
|
-
|
|
105755
|
+
return buildRalphyComment({
|
|
105756
|
+
type: "exited",
|
|
105757
|
+
action: `exited with code ${code}`,
|
|
105758
|
+
body: `This issue has been quarantined and will not be auto-resumed on the next poll. ` + `Inspect the worktree at \`~/.ralph/<project>/worktrees/${changeName}\`, fix the ` + `underlying failure, then remove the error marker on this Linear issue (or run ` + `\`ralph clean --name ${changeName}\`) to clear the quarantine. Change: \`${changeName}\``,
|
|
105759
|
+
fields: { change: changeName, code }
|
|
105760
|
+
});
|
|
105511
105761
|
}
|
|
105512
105762
|
if (trigger === "conflict-fix") {
|
|
105513
|
-
return
|
|
105763
|
+
return buildRalphyComment({
|
|
105764
|
+
type: "conflicts-resolved",
|
|
105765
|
+
action: "resolved merge conflicts",
|
|
105766
|
+
body: `Change: \`${changeName}\``,
|
|
105767
|
+
fields: { change: changeName }
|
|
105768
|
+
});
|
|
105514
105769
|
}
|
|
105515
|
-
return
|
|
105770
|
+
return buildRalphyComment({
|
|
105771
|
+
type: "completed",
|
|
105772
|
+
action: "completed work",
|
|
105773
|
+
body: `Change: \`${changeName}\``,
|
|
105774
|
+
fields: { change: changeName }
|
|
105775
|
+
});
|
|
105516
105776
|
}
|
|
105517
105777
|
function extractPrNumber(url2) {
|
|
105518
105778
|
const m = /\/pull\/(\d+)(?:[/?#]|$)/.exec(url2);
|
|
@@ -105811,7 +106071,12 @@ class AgentCoordinator {
|
|
|
105811
106071
|
const prNum = extractPrNumber(pr.url);
|
|
105812
106072
|
const ref = prNum !== null ? `PR #${prNum}` : `PR ${pr.url}`;
|
|
105813
106073
|
try {
|
|
105814
|
-
await this.deps.postComment(issue2,
|
|
106074
|
+
await this.deps.postComment(issue2, buildRalphyComment({
|
|
106075
|
+
type: "promoted",
|
|
106076
|
+
action: `promoted to ${trigger} flow`,
|
|
106077
|
+
body: `${ref} is ${stateLabel} \u2014 promoted to ${trigger} flow.`,
|
|
106078
|
+
fields: { trigger, pr: extractPrNumber(pr.url) ?? pr.url }
|
|
106079
|
+
}));
|
|
105815
106080
|
this.deps.onLog(` ${issue2.identifier}: posted ${trigger}-promotion comment`, "gray");
|
|
105816
106081
|
} catch (err) {
|
|
105817
106082
|
this.deps.onLog(`! Linear ${trigger}-promotion comment failed for ${issue2.identifier}: ${err.message}`, "yellow");
|
|
@@ -105864,7 +106129,12 @@ class AgentCoordinator {
|
|
|
105864
106129
|
if (currMilestone <= lastMilestone)
|
|
105865
106130
|
continue;
|
|
105866
106131
|
try {
|
|
105867
|
-
await this.deps.postComment(w.issue,
|
|
106132
|
+
await this.deps.postComment(w.issue, buildRalphyComment({
|
|
106133
|
+
type: "progress",
|
|
106134
|
+
action: `progress update \u2014 iteration ${count}`,
|
|
106135
|
+
body: `Iteration ${count} on \`${w.changeName}\``,
|
|
106136
|
+
fields: { change: w.changeName, iter: count }
|
|
106137
|
+
}));
|
|
105868
106138
|
w.lastReportedIteration = count;
|
|
105869
106139
|
this.deps.onLog(` ${w.issueIdentifier}: posted progress comment (iteration ${count})`, "gray");
|
|
105870
106140
|
} catch (err) {
|
|
@@ -106004,7 +106274,12 @@ class AgentCoordinator {
|
|
|
106004
106274
|
this.deps.onLog(` ${issue2.identifier}: PR ${pr.url} conflicting \u2014 queued (conflict-fix)`, "yellow");
|
|
106005
106275
|
if (this.opts.postComments !== false) {
|
|
106006
106276
|
try {
|
|
106007
|
-
await this.deps.postComment(issue2,
|
|
106277
|
+
await this.deps.postComment(issue2, buildRalphyComment({
|
|
106278
|
+
type: "conflict-detected",
|
|
106279
|
+
action: "detected merge conflicts",
|
|
106280
|
+
body: `Detected merge conflicts on this PR (${pr.url}) \u2014 re-running to resolve.`,
|
|
106281
|
+
fields: { pr: extractPrNumber(pr.url) ?? pr.url }
|
|
106282
|
+
}));
|
|
106008
106283
|
} catch (err) {
|
|
106009
106284
|
this.deps.onLog(`! Linear conflict comment failed for ${issue2.identifier}: ${err.message}`, "yellow");
|
|
106010
106285
|
}
|
|
@@ -106039,7 +106314,12 @@ class AgentCoordinator {
|
|
|
106039
106314
|
this.deps.onLog(` ${issue2.identifier}: PR ${pr.url} CI failing \u2014 queued (ci-fix)`, "yellow");
|
|
106040
106315
|
if (this.opts.postComments !== false) {
|
|
106041
106316
|
try {
|
|
106042
|
-
await this.deps.postComment(issue2,
|
|
106317
|
+
await this.deps.postComment(issue2, buildRalphyComment({
|
|
106318
|
+
type: "ci-failed",
|
|
106319
|
+
action: "detected failing CI",
|
|
106320
|
+
body: `Detected failing CI on this PR (${pr.url}) \u2014 re-running to fix.`,
|
|
106321
|
+
fields: { pr: extractPrNumber(pr.url) ?? pr.url }
|
|
106322
|
+
}));
|
|
106043
106323
|
} catch (err) {
|
|
106044
106324
|
this.deps.onLog(`! Linear ci-failed comment failed for ${issue2.identifier}: ${err.message}`, "yellow");
|
|
106045
106325
|
}
|
|
@@ -106105,7 +106385,12 @@ class AgentCoordinator {
|
|
|
106105
106385
|
}
|
|
106106
106386
|
if (this.opts.postComments !== false) {
|
|
106107
106387
|
try {
|
|
106108
|
-
await this.deps.postComment(issue2,
|
|
106388
|
+
await this.deps.postComment(issue2, buildRalphyComment({
|
|
106389
|
+
type: "verified",
|
|
106390
|
+
action: "verified PR mergeable",
|
|
106391
|
+
body: `Verified this PR (${prUrl}) is mergeable (CI green, no conflicts) \u2014 moving to done.`,
|
|
106392
|
+
fields: { pr: extractPrNumber(prUrl) ?? prUrl }
|
|
106393
|
+
}));
|
|
106109
106394
|
} catch (err) {
|
|
106110
106395
|
this.deps.onLog(`! Linear done comment failed for ${issue2.identifier}: ${err.message}`, "yellow");
|
|
106111
106396
|
}
|
|
@@ -106151,7 +106436,12 @@ class AgentCoordinator {
|
|
|
106151
106436
|
if (this.opts.postComments !== false) {
|
|
106152
106437
|
const human = reason === "conflicting" ? "merge conflicts" : "failing CI";
|
|
106153
106438
|
try {
|
|
106154
|
-
await this.deps.postComment(issue2,
|
|
106439
|
+
await this.deps.postComment(issue2, buildRalphyComment({
|
|
106440
|
+
type: "recovery-gaveup",
|
|
106441
|
+
action: "gave up auto-recovering PR",
|
|
106442
|
+
body: `Gave up auto-recovering this PR (${prUrl}) after ${decision.attempts} attempts \u2014 last failure: ${human}. The \`ralph:error\` label has been applied; clear it (or merge the PR) once a human has looked at it.`,
|
|
106443
|
+
fields: { pr: extractPrNumber(prUrl) ?? prUrl, attempts: decision.attempts }
|
|
106444
|
+
}));
|
|
106155
106445
|
} catch (err) {
|
|
106156
106446
|
this.deps.onLog(`! Linear bail comment failed for ${issue2.identifier}: ${err.message}`, "yellow");
|
|
106157
106447
|
}
|
|
@@ -106238,7 +106528,12 @@ class AgentCoordinator {
|
|
|
106238
106528
|
if (trigger === "review" && this.opts.postComments !== false) {
|
|
106239
106529
|
const sourceTag = mention ? mention.source === "github" ? " (GitHub @mention)" : mention.source === "github-review" ? " (GitHub code review)" : " (Linear @mention)" : "";
|
|
106240
106530
|
try {
|
|
106241
|
-
await this.deps.postComment(issue2,
|
|
106531
|
+
await this.deps.postComment(issue2, buildRalphyComment({
|
|
106532
|
+
type: "review-pickup",
|
|
106533
|
+
action: "picked up review comments",
|
|
106534
|
+
body: `Picked up new review comments${sourceTag}. Tracking change: \`${prep.changeName}\``,
|
|
106535
|
+
fields: { change: prep.changeName }
|
|
106536
|
+
}));
|
|
106242
106537
|
} catch (err) {
|
|
106243
106538
|
this.deps.onLog(`! Linear review comment failed for ${issue2.identifier}: ${err.message}`, "yellow");
|
|
106244
106539
|
}
|
|
@@ -106247,13 +106542,18 @@ class AgentCoordinator {
|
|
|
106247
106542
|
let alreadyPosted = false;
|
|
106248
106543
|
try {
|
|
106249
106544
|
const comments = await this.deps.fetchComments(issue2.id);
|
|
106250
|
-
alreadyPosted = comments.some((c) => c.body
|
|
106545
|
+
alreadyPosted = comments.some((c) => isStartedComment(c.body));
|
|
106251
106546
|
} catch (err) {
|
|
106252
106547
|
this.deps.onLog(`! Linear comment fetch failed for ${issue2.identifier}: ${err.message}`, "yellow");
|
|
106253
106548
|
}
|
|
106254
106549
|
if (!alreadyPosted) {
|
|
106255
106550
|
try {
|
|
106256
|
-
await this.deps.postComment(issue2,
|
|
106551
|
+
await this.deps.postComment(issue2, buildRalphyComment({
|
|
106552
|
+
type: "started",
|
|
106553
|
+
action: "started working",
|
|
106554
|
+
body: `Tracking change: \`${prep.changeName}\``,
|
|
106555
|
+
fields: { change: prep.changeName }
|
|
106556
|
+
}));
|
|
106257
106557
|
this.deps.onLog(` ${issue2.identifier}: posted "started" comment`, "gray");
|
|
106258
106558
|
} catch (err) {
|
|
106259
106559
|
this.deps.onLog(`! Linear comment failed for ${issue2.identifier}: ${err.message}`, "red");
|
|
@@ -106568,6 +106868,7 @@ var init_coordinator = __esm(() => {
|
|
|
106568
106868
|
init_registry();
|
|
106569
106869
|
init_run_feature();
|
|
106570
106870
|
init_machines();
|
|
106871
|
+
init_src8();
|
|
106571
106872
|
});
|
|
106572
106873
|
|
|
106573
106874
|
// apps/agent/src/agent/coordinator.ts
|
|
@@ -106575,11 +106876,38 @@ var init_coordinator2 = __esm(() => {
|
|
|
106575
106876
|
init_coordinator();
|
|
106576
106877
|
});
|
|
106577
106878
|
|
|
106879
|
+
// apps/agent/src/shared/capabilities/gh-client.ts
|
|
106880
|
+
var init_gh_client = () => {};
|
|
106881
|
+
|
|
106882
|
+
// apps/agent/src/shared/capabilities/github/github-client.ts
|
|
106883
|
+
var STARTED_LABEL_NAMES, ISSUE_FIELDS = "id,number,title,body,state,stateReason,labels,assignees,author,createdAt,url", ISSUE_FIELDS_WITH_COMMENTS;
|
|
106884
|
+
var init_github_client = __esm(() => {
|
|
106885
|
+
init_worktree();
|
|
106886
|
+
init_gh_client();
|
|
106887
|
+
STARTED_LABEL_NAMES = new Set(["in progress", "in-progress", "started"]);
|
|
106888
|
+
ISSUE_FIELDS_WITH_COMMENTS = `${ISSUE_FIELDS},comments`;
|
|
106889
|
+
});
|
|
106890
|
+
|
|
106891
|
+
// apps/agent/src/shared/capabilities/github/identifier-strategy.ts
|
|
106892
|
+
function linearChangeName(issue2) {
|
|
106893
|
+
const slug = issue2.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 40).replace(/^-+|-+$/g, "");
|
|
106894
|
+
return slug ? `${issue2.identifier.toLowerCase()}-${slug}` : issue2.identifier.toLowerCase();
|
|
106895
|
+
}
|
|
106896
|
+
var linearIdentifierStrategy;
|
|
106897
|
+
var init_identifier_strategy = __esm(() => {
|
|
106898
|
+
init_worktree();
|
|
106899
|
+
init_github_client();
|
|
106900
|
+
linearIdentifierStrategy = {
|
|
106901
|
+
scopeKey: (issue2) => issue2.identifier.split("-")[0],
|
|
106902
|
+
changeName: linearChangeName,
|
|
106903
|
+
branchName: (issue2) => branchForChange(linearChangeName(issue2))
|
|
106904
|
+
};
|
|
106905
|
+
});
|
|
106906
|
+
|
|
106578
106907
|
// apps/agent/src/agent/scaffold.ts
|
|
106579
106908
|
import { join as join24 } from "path";
|
|
106580
106909
|
function changeNameForIssue(issue2) {
|
|
106581
|
-
|
|
106582
|
-
return slug ? `${issue2.identifier.toLowerCase()}-${slug}` : issue2.identifier.toLowerCase();
|
|
106910
|
+
return linearIdentifierStrategy.changeName(issue2);
|
|
106583
106911
|
}
|
|
106584
106912
|
async function scaffoldChangeForIssue(tasksDir, statesDir, issue2, comments = [], appendPrompt = "", attachments = []) {
|
|
106585
106913
|
const name = changeNameForIssue(issue2);
|
|
@@ -106661,6 +106989,7 @@ async function scaffoldChangeForIssue(tasksDir, statesDir, issue2, comments = []
|
|
|
106661
106989
|
}
|
|
106662
106990
|
var init_scaffold = __esm(() => {
|
|
106663
106991
|
init_fs_change();
|
|
106992
|
+
init_identifier_strategy();
|
|
106664
106993
|
});
|
|
106665
106994
|
|
|
106666
106995
|
// packages/core/src/detections/tasks.ts
|
|
@@ -106704,19 +107033,21 @@ function gateActive(inputs) {
|
|
|
106704
107033
|
}
|
|
106705
107034
|
|
|
106706
107035
|
// packages/core/src/detections/mention.ts
|
|
106707
|
-
function buildMentionAckComment(
|
|
106708
|
-
const
|
|
106709
|
-
|
|
106710
|
-
|
|
106711
|
-
|
|
106712
|
-
|
|
106713
|
-
|
|
106714
|
-
|
|
106715
|
-
> ${excerpt}`;
|
|
107036
|
+
function buildMentionAckComment(_body, author) {
|
|
107037
|
+
const greeting = author ? `Got it, ${author} \u2014 picked up your mention and queued a review pass.` : `Acknowledged \u2014 picked up your mention and queued a review pass.`;
|
|
107038
|
+
return buildRalphyComment({
|
|
107039
|
+
type: "mention-ack",
|
|
107040
|
+
action: "picked up your mention",
|
|
107041
|
+
body: greeting
|
|
107042
|
+
});
|
|
106716
107043
|
}
|
|
107044
|
+
var init_mention2 = __esm(() => {
|
|
107045
|
+
init_src8();
|
|
107046
|
+
});
|
|
106717
107047
|
// packages/core/src/detections/index.ts
|
|
106718
107048
|
var init_detections = __esm(() => {
|
|
106719
107049
|
init_phase2();
|
|
107050
|
+
init_mention2();
|
|
106720
107051
|
});
|
|
106721
107052
|
|
|
106722
107053
|
// apps/agent/src/features/confirmation/state.ts
|
|
@@ -106839,7 +107170,17 @@ function buildMentionTaskBody(trigger, issueUrl) {
|
|
|
106839
107170
|
function findLastRalphPickupISO(comments) {
|
|
106840
107171
|
let latest = null;
|
|
106841
107172
|
for (const c of comments) {
|
|
106842
|
-
if (
|
|
107173
|
+
if (!isPickupComment(c.body))
|
|
107174
|
+
continue;
|
|
107175
|
+
if (latest === null || c.createdAt > latest)
|
|
107176
|
+
latest = c.createdAt;
|
|
107177
|
+
}
|
|
107178
|
+
return latest;
|
|
107179
|
+
}
|
|
107180
|
+
function findLastMentionAckISO(comments) {
|
|
107181
|
+
let latest = null;
|
|
107182
|
+
for (const c of comments) {
|
|
107183
|
+
if (!isMentionAckComment(c.body))
|
|
106843
107184
|
continue;
|
|
106844
107185
|
if (latest === null || c.createdAt > latest)
|
|
106845
107186
|
latest = c.createdAt;
|
|
@@ -106875,7 +107216,10 @@ function githubReactionSlug(emoji3) {
|
|
|
106875
107216
|
return emoji3;
|
|
106876
107217
|
}
|
|
106877
107218
|
}
|
|
106878
|
-
var init_task_bodies = () => {
|
|
107219
|
+
var init_task_bodies = __esm(() => {
|
|
107220
|
+
init_src8();
|
|
107221
|
+
init_ralph_comment();
|
|
107222
|
+
});
|
|
106879
107223
|
|
|
106880
107224
|
// apps/agent/src/features/confirmation/inspect.ts
|
|
106881
107225
|
function buildReviseRegex(handle) {
|
|
@@ -106902,7 +107246,12 @@ async function inspectAwaitingTicket(state, cfg, deps) {
|
|
|
106902
107246
|
if (!next.stuckPostedAt) {
|
|
106903
107247
|
if (cfg.postComments) {
|
|
106904
107248
|
try {
|
|
106905
|
-
await deps.postComment(
|
|
107249
|
+
await deps.postComment(buildRalphyComment({
|
|
107250
|
+
type: "confirmation-stuck",
|
|
107251
|
+
action: "confirmation gate stuck",
|
|
107252
|
+
body: `Confirmation gate stuck after ${next.rounds} revise round(s) ` + `(max ${cfg.maxConfirmationRounds}). Applying \`ralph:stuck\` \u2014 ` + `clear the label to retry, or apply the approval marker to proceed.`,
|
|
107253
|
+
fields: { rounds: next.rounds }
|
|
107254
|
+
}));
|
|
106906
107255
|
} catch (err) {
|
|
106907
107256
|
deps.log(`! plan-stuck comment failed: ${err.message}`, "yellow");
|
|
106908
107257
|
}
|
|
@@ -106947,7 +107296,12 @@ ${revise.reason}`);
|
|
|
106947
107296
|
}
|
|
106948
107297
|
if (cfg.postComments) {
|
|
106949
107298
|
try {
|
|
106950
|
-
await deps.postComment(
|
|
107299
|
+
await deps.postComment(buildRalphyComment({
|
|
107300
|
+
type: "revise-ack",
|
|
107301
|
+
action: "revise request acknowledged",
|
|
107302
|
+
body: `Revise request acknowledged \u2014 restarting at design (round ${next.rounds + 1}/${cfg.maxConfirmationRounds}).`,
|
|
107303
|
+
fields: { round: next.rounds + 1 }
|
|
107304
|
+
}));
|
|
106951
107305
|
} catch (err) {
|
|
106952
107306
|
deps.log(`! revise ack comment failed: ${err.message}`, "yellow");
|
|
106953
107307
|
}
|
|
@@ -106976,7 +107330,12 @@ ${revise.reason}`);
|
|
|
106976
107330
|
const limitMs = cfg.timeoutHours * 60 * 60 * 1000;
|
|
106977
107331
|
if (elapsedMs >= limitMs && cfg.postComments) {
|
|
106978
107332
|
try {
|
|
106979
|
-
await deps.postComment(
|
|
107333
|
+
await deps.postComment(buildRalphyComment({
|
|
107334
|
+
type: "confirmation-reminder",
|
|
107335
|
+
action: "awaiting confirmation",
|
|
107336
|
+
body: `Still awaiting confirmation on this plan (round ${next.rounds + 1}/${cfg.maxConfirmationRounds}). ` + `Approve to continue or reply \`${cfg.mentionHandle} revise: <reason>\` to send it back.`,
|
|
107337
|
+
fields: { round: next.rounds + 1 }
|
|
107338
|
+
}));
|
|
106980
107339
|
next.lastReminderAt = nowIso;
|
|
106981
107340
|
} catch (err) {
|
|
106982
107341
|
deps.log(`! reminder comment failed: ${err.message}`, "yellow");
|
|
@@ -106986,6 +107345,7 @@ ${revise.reason}`);
|
|
|
106986
107345
|
return { outcome: "stay-awaiting", next };
|
|
106987
107346
|
}
|
|
106988
107347
|
var init_inspect = __esm(() => {
|
|
107348
|
+
init_src8();
|
|
106989
107349
|
init_task_bodies();
|
|
106990
107350
|
});
|
|
106991
107351
|
|
|
@@ -107028,7 +107388,12 @@ async function postPlanReadyCommentOnce(issue2, statePath, changeName, deps) {
|
|
|
107028
107388
|
return;
|
|
107029
107389
|
const approvalSentence = describeApprovalMarker(deps.cfg.linear.indicators.getApproved);
|
|
107030
107390
|
const handle = deps.cfg.linear.mentionHandle;
|
|
107031
|
-
const body =
|
|
107391
|
+
const body = buildRalphyComment({
|
|
107392
|
+
type: "plan-ready",
|
|
107393
|
+
action: "plan ready",
|
|
107394
|
+
body: `Plan ready for \`${changeName}\` \u2014 review proposal.md / design.md / tasks.md ` + `and ${approvalSentence} to continue, ` + `or reply with \`${handle} revise: <reason>\` to send it back to design.`,
|
|
107395
|
+
fields: { change: changeName }
|
|
107396
|
+
});
|
|
107032
107397
|
try {
|
|
107033
107398
|
await addIssueComment(deps.apiKey, issue2.id, body);
|
|
107034
107399
|
} catch (err) {
|
|
@@ -107307,11 +107672,13 @@ async function processAwaitingForIssue(issue2, deps) {
|
|
|
107307
107672
|
}
|
|
107308
107673
|
var init_awaiting = __esm(() => {
|
|
107309
107674
|
init_layout();
|
|
107675
|
+
init_src8();
|
|
107310
107676
|
init_detections();
|
|
107311
107677
|
init_phase();
|
|
107312
107678
|
init_worktree();
|
|
107313
107679
|
init_scaffold();
|
|
107314
107680
|
init_linear();
|
|
107681
|
+
init_ralph_comment();
|
|
107315
107682
|
init_types2();
|
|
107316
107683
|
init_workflow();
|
|
107317
107684
|
init_state2();
|
|
@@ -107538,10 +107905,13 @@ function unionMarkers(...sets) {
|
|
|
107538
107905
|
}
|
|
107539
107906
|
return out;
|
|
107540
107907
|
}
|
|
107541
|
-
function describeIndicators(indicators, team, assignee, anyAssignee) {
|
|
107908
|
+
function describeIndicators(indicators, team, assignee, anyAssignee, requireAllLabels) {
|
|
107542
107909
|
const parts = [];
|
|
107543
107910
|
parts.push(`team=${team ?? "*"}`);
|
|
107544
107911
|
parts.push(`assignee=${anyAssignee ? "any" : assignee ?? "*"}`);
|
|
107912
|
+
if (requireAllLabels && requireAllLabels.length > 0) {
|
|
107913
|
+
parts.push(`labels=[${requireAllLabels.join(",")}]`);
|
|
107914
|
+
}
|
|
107545
107915
|
if (indicators.getTodo) {
|
|
107546
107916
|
parts.push(`todo=[${indicators.getTodo.filter.map((m) => `${m.type}:${m.value}`).join(",")}]`);
|
|
107547
107917
|
}
|
|
@@ -107561,7 +107931,7 @@ function createLinearResolvers(input) {
|
|
|
107561
107931
|
const stateCache = new Map;
|
|
107562
107932
|
const labelCache = new Map;
|
|
107563
107933
|
const teamIdCache = new Map;
|
|
107564
|
-
const teamKeyOf = (issue2) =>
|
|
107934
|
+
const teamKeyOf = (issue2) => linearIdentifierStrategy.scopeKey(issue2);
|
|
107565
107935
|
async function resolveStateId(issue2, name) {
|
|
107566
107936
|
const t = teamKeyOf(issue2);
|
|
107567
107937
|
let map3 = stateCache.get(t);
|
|
@@ -107757,6 +108127,201 @@ async function fetchDoneCandidatesWith(apiKey, team, assignee, anyAssignee, requ
|
|
|
107757
108127
|
var init_linear_resolvers = __esm(() => {
|
|
107758
108128
|
init_types2();
|
|
107759
108129
|
init_linear();
|
|
108130
|
+
init_identifier_strategy();
|
|
108131
|
+
});
|
|
108132
|
+
|
|
108133
|
+
// apps/agent/src/agent/wire/tracker/github.ts
|
|
108134
|
+
function identifierForNumber(n) {
|
|
108135
|
+
return `issue-${n}`;
|
|
108136
|
+
}
|
|
108137
|
+
function toLinearIssue(gh) {
|
|
108138
|
+
const open = (gh.state ?? "OPEN").toUpperCase() === "OPEN";
|
|
108139
|
+
return {
|
|
108140
|
+
id: String(gh.number),
|
|
108141
|
+
identifier: identifierForNumber(gh.number),
|
|
108142
|
+
title: gh.title,
|
|
108143
|
+
description: gh.body ?? null,
|
|
108144
|
+
url: gh.url,
|
|
108145
|
+
state: { name: open ? "Open" : "Closed", type: open ? "started" : "completed" },
|
|
108146
|
+
assignee: null,
|
|
108147
|
+
project: null,
|
|
108148
|
+
labels: (gh.labels ?? []).map((l) => l.name),
|
|
108149
|
+
priority: 0,
|
|
108150
|
+
createdAt: gh.createdAt ?? "",
|
|
108151
|
+
blockedByIds: []
|
|
108152
|
+
};
|
|
108153
|
+
}
|
|
108154
|
+
function labelValues(markers) {
|
|
108155
|
+
return markers.filter((m) => m.type === "label").map((m) => m.value);
|
|
108156
|
+
}
|
|
108157
|
+
function createGithubTrackerProvider(input) {
|
|
108158
|
+
const { cmdRunner, projectRoot, diag } = input;
|
|
108159
|
+
const statusLabels = input.issues?.statusLabels ?? DEFAULT_STATUS_LABELS;
|
|
108160
|
+
const todoLabel = input.issues?.label;
|
|
108161
|
+
const assignee = input.issues?.assignee;
|
|
108162
|
+
const configuredRepo = input.issues?.repo;
|
|
108163
|
+
let repoPromise = null;
|
|
108164
|
+
async function repo() {
|
|
108165
|
+
if (configuredRepo && configuredRepo.trim() !== "")
|
|
108166
|
+
return configuredRepo.trim();
|
|
108167
|
+
if (!repoPromise) {
|
|
108168
|
+
repoPromise = (async () => {
|
|
108169
|
+
try {
|
|
108170
|
+
const { stdout } = await cmdRunner.run(["gh", "repo", "view", "--json", "nameWithOwner", "-q", ".nameWithOwner"], projectRoot);
|
|
108171
|
+
const detected = stdout.trim();
|
|
108172
|
+
if (!detected)
|
|
108173
|
+
throw new Error("empty");
|
|
108174
|
+
diag("github-tracker", ` using detected GitHub repo ${detected}`, "gray");
|
|
108175
|
+
return detected;
|
|
108176
|
+
} catch (err) {
|
|
108177
|
+
throw new Error("github tracker: could not determine the repository \u2014 set " + "`github.issues.repo` (owner/name) in WORKFLOW.md or run inside a " + `repo with a GitHub 'origin' remote (${err.message})`);
|
|
108178
|
+
}
|
|
108179
|
+
})();
|
|
108180
|
+
}
|
|
108181
|
+
return repoPromise;
|
|
108182
|
+
}
|
|
108183
|
+
async function listIssues(args) {
|
|
108184
|
+
const r = await repo();
|
|
108185
|
+
const { stdout } = await cmdRunner.run([
|
|
108186
|
+
"gh",
|
|
108187
|
+
"issue",
|
|
108188
|
+
"list",
|
|
108189
|
+
"--repo",
|
|
108190
|
+
r,
|
|
108191
|
+
"--state",
|
|
108192
|
+
"open",
|
|
108193
|
+
"--json",
|
|
108194
|
+
"number,title,url,body,state,createdAt,labels",
|
|
108195
|
+
"--limit",
|
|
108196
|
+
"100",
|
|
108197
|
+
...args
|
|
108198
|
+
], projectRoot);
|
|
108199
|
+
const parsed = JSON.parse(stdout.trim() || "[]");
|
|
108200
|
+
return parsed.map(toLinearIssue);
|
|
108201
|
+
}
|
|
108202
|
+
async function fetchByGet(inc, excl) {
|
|
108203
|
+
if (!inc)
|
|
108204
|
+
return [];
|
|
108205
|
+
const include = !Array.isArray(inc) && "filter" in inc ? inc.filter : [];
|
|
108206
|
+
const wantLabels = labelValues(include);
|
|
108207
|
+
const args = [];
|
|
108208
|
+
for (const label of wantLabels)
|
|
108209
|
+
args.push("--label", label);
|
|
108210
|
+
if (assignee && assignee.trim() !== "")
|
|
108211
|
+
args.push("--assignee", assignee.trim());
|
|
108212
|
+
const issues = await listIssues(args);
|
|
108213
|
+
const excludeLabels = new Set(labelValues(excl));
|
|
108214
|
+
if (excludeLabels.size === 0)
|
|
108215
|
+
return issues;
|
|
108216
|
+
return issues.filter((issue2) => !issue2.labels.some((l) => excludeLabels.has(l)));
|
|
108217
|
+
}
|
|
108218
|
+
async function ghIssue(issueNumber, ...args) {
|
|
108219
|
+
const r = await repo();
|
|
108220
|
+
await cmdRunner.run(["gh", "issue", ...args, issueNumber, "--repo", r], projectRoot);
|
|
108221
|
+
}
|
|
108222
|
+
async function applyMarker(issue2, m) {
|
|
108223
|
+
if (m.type === "comment") {
|
|
108224
|
+
await ghIssue(issue2.id, "comment", "--body", m.value);
|
|
108225
|
+
diag("github-marker", ` \u2192 ${issue2.identifier} comment`, "gray");
|
|
108226
|
+
return;
|
|
108227
|
+
}
|
|
108228
|
+
if (m.type !== "label") {
|
|
108229
|
+
diag("github-marker", `! ${issue2.identifier}: '${m.type}' markers are not supported by the GitHub tracker \u2014 skipped`, "yellow");
|
|
108230
|
+
return;
|
|
108231
|
+
}
|
|
108232
|
+
await ghIssue(issue2.id, "edit", "--add-label", m.value);
|
|
108233
|
+
diag("github-marker", ` \u2192 ${issue2.identifier} +label='${m.value}'`, "gray");
|
|
108234
|
+
if (m.value === statusLabels.inProgress && todoLabel && todoLabel.trim() !== "") {
|
|
108235
|
+
await ghIssue(issue2.id, "edit", "--remove-label", todoLabel.trim());
|
|
108236
|
+
diag("github-marker", ` \u2192 ${issue2.identifier} -label='${todoLabel.trim()}'`, "gray");
|
|
108237
|
+
}
|
|
108238
|
+
if (m.value === statusLabels.done) {
|
|
108239
|
+
await ghIssue(issue2.id, "close");
|
|
108240
|
+
diag("github-marker", ` \u2192 ${issue2.identifier} closed`, "gray");
|
|
108241
|
+
}
|
|
108242
|
+
}
|
|
108243
|
+
async function applyIndicator(issue2, ind) {
|
|
108244
|
+
for (const m of markersOf(ind))
|
|
108245
|
+
await applyMarker(issue2, m);
|
|
108246
|
+
}
|
|
108247
|
+
async function removeIndicator(issue2, ind) {
|
|
108248
|
+
for (const m of markersOf(ind)) {
|
|
108249
|
+
if (m.type !== "label")
|
|
108250
|
+
continue;
|
|
108251
|
+
await ghIssue(issue2.id, "edit", "--remove-label", m.value);
|
|
108252
|
+
diag("github-marker", ` \u2192 ${issue2.identifier} -label='${m.value}'`, "gray");
|
|
108253
|
+
}
|
|
108254
|
+
}
|
|
108255
|
+
async function fetchDoneCandidates() {
|
|
108256
|
+
return listIssues(["--label", statusLabels.inProgress]);
|
|
108257
|
+
}
|
|
108258
|
+
async function resolveLabelIdForTeam() {
|
|
108259
|
+
return null;
|
|
108260
|
+
}
|
|
108261
|
+
return {
|
|
108262
|
+
fetchByGet,
|
|
108263
|
+
applyIndicator,
|
|
108264
|
+
removeIndicator,
|
|
108265
|
+
applyMarker,
|
|
108266
|
+
fetchDoneCandidates,
|
|
108267
|
+
resolveLabelIdForTeam
|
|
108268
|
+
};
|
|
108269
|
+
}
|
|
108270
|
+
function githubIndicators(issues) {
|
|
108271
|
+
const statusLabels = issues?.statusLabels ?? DEFAULT_STATUS_LABELS;
|
|
108272
|
+
const todoLabel = issues?.label?.trim();
|
|
108273
|
+
return {
|
|
108274
|
+
getTodo: { filter: todoLabel ? [{ type: "label", value: todoLabel }] : [] },
|
|
108275
|
+
getInProgress: { filter: [{ type: "label", value: statusLabels.inProgress }] },
|
|
108276
|
+
setInProgress: { type: "label", value: statusLabels.inProgress },
|
|
108277
|
+
setDone: { type: "label", value: statusLabels.done },
|
|
108278
|
+
setError: { type: "label", value: statusLabels.error }
|
|
108279
|
+
};
|
|
108280
|
+
}
|
|
108281
|
+
var DEFAULT_STATUS_LABELS;
|
|
108282
|
+
var init_github = __esm(() => {
|
|
108283
|
+
init_types2();
|
|
108284
|
+
DEFAULT_STATUS_LABELS = {
|
|
108285
|
+
inProgress: "ralph:in-progress",
|
|
108286
|
+
done: "ralph:done",
|
|
108287
|
+
error: "ralph:error"
|
|
108288
|
+
};
|
|
108289
|
+
});
|
|
108290
|
+
|
|
108291
|
+
// apps/agent/src/agent/wire/tracker/linear-tracker-provider.ts
|
|
108292
|
+
function createLinearTrackerProvider(input) {
|
|
108293
|
+
const {
|
|
108294
|
+
apiKey,
|
|
108295
|
+
team,
|
|
108296
|
+
assignee,
|
|
108297
|
+
anyAssignee,
|
|
108298
|
+
requireAllLabels,
|
|
108299
|
+
indicators,
|
|
108300
|
+
resolvers,
|
|
108301
|
+
fetchMentions,
|
|
108302
|
+
ticketNumbers
|
|
108303
|
+
} = input;
|
|
108304
|
+
const excludeFromTodo = unionMarkers(indicators.setDone, indicators.setError);
|
|
108305
|
+
const excludeFromInProgress = unionMarkers(indicators.setError);
|
|
108306
|
+
return {
|
|
108307
|
+
fetchTodo: () => resolvers.fetchByGet(indicators.getTodo, excludeFromTodo),
|
|
108308
|
+
fetchInProgress: () => resolvers.fetchByGet(indicators.getInProgress, excludeFromInProgress),
|
|
108309
|
+
fetchReview: () => resolvers.fetchByGet(indicators.getReview, []),
|
|
108310
|
+
fetchMentions,
|
|
108311
|
+
fetchDoneCandidates: () => fetchDoneCandidatesWith(apiKey, team, assignee, anyAssignee, requireAllLabels, indicators, ticketNumbers && ticketNumbers.length > 0 ? ticketNumbers : undefined),
|
|
108312
|
+
applyIndicator: resolvers.applyIndicator,
|
|
108313
|
+
removeIndicator: resolvers.removeIndicator,
|
|
108314
|
+
postComment: (issue2, body) => addIssueComment(apiKey, issue2.id, body),
|
|
108315
|
+
fetchComments: async (issueId) => {
|
|
108316
|
+
const c = await fetchIssueComments(apiKey, issueId);
|
|
108317
|
+
return c.map((x) => ({ body: x.body }));
|
|
108318
|
+
}
|
|
108319
|
+
};
|
|
108320
|
+
}
|
|
108321
|
+
var init_linear_tracker_provider = __esm(() => {
|
|
108322
|
+
init_linear();
|
|
108323
|
+
init_indicators();
|
|
108324
|
+
init_linear_resolvers();
|
|
107760
108325
|
});
|
|
107761
108326
|
|
|
107762
108327
|
// apps/agent/src/agent/wire/prepare.ts
|
|
@@ -108371,7 +108936,12 @@ async function maybePingStaleReviewer(issue2, prUrl, state, newestReviewerActivi
|
|
|
108371
108936
|
if (!m)
|
|
108372
108937
|
return;
|
|
108373
108938
|
const [, owner, repo, num] = m;
|
|
108374
|
-
const body =
|
|
108939
|
+
const body = buildRalphyComment({
|
|
108940
|
+
type: "reviewer-ping",
|
|
108941
|
+
action: "nudging reviewer",
|
|
108942
|
+
body: `@${reviewer} \u2014 Ralph has been waiting ${elapsedH.toFixed(0)}h on a re-review for ${prUrl}. Could you take another look when you have a moment?`,
|
|
108943
|
+
fields: { reviewer }
|
|
108944
|
+
});
|
|
108375
108945
|
try {
|
|
108376
108946
|
await deps.cmdRunner.run(["gh", "api", `repos/${owner}/${repo}/issues/${num}/comments`, "-f", `body=${body}`], deps.projectRoot);
|
|
108377
108947
|
deps.stalePingedAt.set(prUrl, now2);
|
|
@@ -108437,6 +109007,7 @@ async function scanCodeReview(issue2, prUrl, lastRalphPickup, deps) {
|
|
|
108437
109007
|
var init_scan = __esm(() => {
|
|
108438
109008
|
init_layout();
|
|
108439
109009
|
init_store();
|
|
109010
|
+
init_src8();
|
|
108440
109011
|
init_scaffold();
|
|
108441
109012
|
init_worktree();
|
|
108442
109013
|
});
|
|
@@ -108486,7 +109057,7 @@ async function fetchPrIssueComments(cmdRunner, projectRoot, prUrl, onLog) {
|
|
|
108486
109057
|
return [];
|
|
108487
109058
|
}
|
|
108488
109059
|
}
|
|
108489
|
-
var
|
|
109060
|
+
var init_github2 = __esm(() => {
|
|
108490
109061
|
init_linear();
|
|
108491
109062
|
init_task_bodies();
|
|
108492
109063
|
});
|
|
@@ -108494,6 +109065,19 @@ var init_github = __esm(() => {
|
|
|
108494
109065
|
// apps/agent/src/agent/wire/mention-scan.ts
|
|
108495
109066
|
import { readdir as readdir2 } from "fs/promises";
|
|
108496
109067
|
import { join as join29 } from "path";
|
|
109068
|
+
function latestIso(...values2) {
|
|
109069
|
+
let latest = null;
|
|
109070
|
+
for (const value of values2) {
|
|
109071
|
+
if (value && (latest === null || value > latest))
|
|
109072
|
+
latest = value;
|
|
109073
|
+
}
|
|
109074
|
+
return latest;
|
|
109075
|
+
}
|
|
109076
|
+
function isAlreadyReactedError(err) {
|
|
109077
|
+
const e = err;
|
|
109078
|
+
const text = [...e?.messages ?? [], e?.message ?? ""].join(" ").toLowerCase();
|
|
109079
|
+
return text.includes("conflict on insert of reaction") || text.includes("already exists") || text.includes("already reacted");
|
|
109080
|
+
}
|
|
108497
109081
|
function createMentionScanner(input) {
|
|
108498
109082
|
const {
|
|
108499
109083
|
apiKey,
|
|
@@ -108555,13 +109139,14 @@ function createMentionScanner(input) {
|
|
|
108555
109139
|
for (const issue2 of candidates) {
|
|
108556
109140
|
const comments = issue2.comments ?? [];
|
|
108557
109141
|
const lastRalphPickup = findLastRalphPickupISO(comments);
|
|
109142
|
+
const linearMentionGate = latestIso(lastRalphPickup, findLastMentionAckISO(comments));
|
|
108558
109143
|
if (wantMention) {
|
|
108559
109144
|
for (const c of comments) {
|
|
108560
109145
|
if (isRalphComment(c.body))
|
|
108561
109146
|
continue;
|
|
108562
109147
|
if (!containsHandle(c.body, handle))
|
|
108563
109148
|
continue;
|
|
108564
|
-
if (
|
|
109149
|
+
if (linearMentionGate && c.createdAt <= linearMentionGate)
|
|
108565
109150
|
continue;
|
|
108566
109151
|
out.push({
|
|
108567
109152
|
issue: issue2,
|
|
@@ -108581,7 +109166,9 @@ function createMentionScanner(input) {
|
|
|
108581
109166
|
queued.add(issue2.id);
|
|
108582
109167
|
break;
|
|
108583
109168
|
}
|
|
108584
|
-
|
|
109169
|
+
if (!isAlreadyReactedError(err)) {
|
|
109170
|
+
diag("mention", `! mention scan: Linear reaction failed for ${issue2.identifier}: ${formatLinearError(err)}`, "yellow");
|
|
109171
|
+
}
|
|
108585
109172
|
}
|
|
108586
109173
|
if (cfg.linear.postComments !== false) {
|
|
108587
109174
|
try {
|
|
@@ -108605,11 +109192,14 @@ function createMentionScanner(input) {
|
|
|
108605
109192
|
continue;
|
|
108606
109193
|
if (wantMention) {
|
|
108607
109194
|
const ghComments = await fetchPrIssueComments(cmdRunner, projectRoot, prUrl, onLog);
|
|
109195
|
+
const ghMentionGate = latestIso(lastRalphPickup, findLastMentionAckISO(ghComments));
|
|
108608
109196
|
const prMatch = /^https:\/\/github\.com\/([^/]+)\/([^/]+)\/pull\/\d+/.exec(prUrl);
|
|
108609
109197
|
for (const c of ghComments) {
|
|
109198
|
+
if (isRalphComment(c.body))
|
|
109199
|
+
continue;
|
|
108610
109200
|
if (!containsHandle(c.body, handle))
|
|
108611
109201
|
continue;
|
|
108612
|
-
if (
|
|
109202
|
+
if (ghMentionGate && c.createdAt <= ghMentionGate)
|
|
108613
109203
|
continue;
|
|
108614
109204
|
out.push({
|
|
108615
109205
|
issue: issue2,
|
|
@@ -108626,7 +109216,9 @@ function createMentionScanner(input) {
|
|
|
108626
109216
|
try {
|
|
108627
109217
|
await addGithubReactionToComment(cmdRunner, projectRoot, { owner, repo, kind: "issue" }, c.id, "\uD83D\uDC40");
|
|
108628
109218
|
} catch (err) {
|
|
108629
|
-
|
|
109219
|
+
if (!isAlreadyReactedError(err)) {
|
|
109220
|
+
diag("mention", `! mention scan: GitHub reaction failed for ${prUrl}: ${formatLinearError(err)}`, "yellow");
|
|
109221
|
+
}
|
|
108630
109222
|
}
|
|
108631
109223
|
if (cfg.linear.postComments !== false) {
|
|
108632
109224
|
await postGithubPrComment(cmdRunner, projectRoot, prUrl, buildMentionAckComment(c.body, c.author), onLog);
|
|
@@ -108680,7 +109272,7 @@ var init_mention_scan = __esm(() => {
|
|
|
108680
109272
|
init_detections();
|
|
108681
109273
|
init_scaffold();
|
|
108682
109274
|
init_scan();
|
|
108683
|
-
|
|
109275
|
+
init_github2();
|
|
108684
109276
|
init_task_bodies();
|
|
108685
109277
|
});
|
|
108686
109278
|
|
|
@@ -109688,7 +110280,12 @@ async function readTasksMd(changeDir, log3) {
|
|
|
109688
110280
|
}
|
|
109689
110281
|
}
|
|
109690
110282
|
function renderTasksCommentBody(tasksMd, changeName, iteration) {
|
|
109691
|
-
return
|
|
110283
|
+
return buildRalphyComment({
|
|
110284
|
+
type: "tasks",
|
|
110285
|
+
action: "task progress",
|
|
110286
|
+
body: renderTasksBlock(tasksMd, { changeName, iteration }),
|
|
110287
|
+
fields: { change: changeName, iter: iteration }
|
|
110288
|
+
});
|
|
109692
110289
|
}
|
|
109693
110290
|
async function postOrUpdateTasksComment(deps) {
|
|
109694
110291
|
const tasksMd = await readTasksMd(deps.changeDir, deps.log);
|
|
@@ -109799,8 +110396,13 @@ async function postPlanCommentOnce(deps) {
|
|
|
109799
110396
|
if (designSummary) {
|
|
109800
110397
|
parts.push("", "**Design**", "", designSummary);
|
|
109801
110398
|
}
|
|
109802
|
-
const body =
|
|
109803
|
-
|
|
110399
|
+
const body = buildRalphyComment({
|
|
110400
|
+
type: "plan",
|
|
110401
|
+
action: "plan",
|
|
110402
|
+
body: parts.join(`
|
|
110403
|
+
`),
|
|
110404
|
+
fields: { change: deps.changeName }
|
|
110405
|
+
});
|
|
109804
110406
|
let id;
|
|
109805
110407
|
try {
|
|
109806
110408
|
id = await deps.mutations.createIssueComment(deps.apiKey, deps.issueId, body);
|
|
@@ -109817,9 +110419,14 @@ async function postPlanCommentOnce(deps) {
|
|
|
109817
110419
|
}
|
|
109818
110420
|
async function postSteeringAndRefreshTasks(deps) {
|
|
109819
110421
|
const firstLine = deps.message.split(/\r?\n/, 1)[0].trim() || deps.message.trim();
|
|
109820
|
-
const steeringBody =
|
|
110422
|
+
const steeringBody = buildRalphyComment({
|
|
110423
|
+
type: "steering",
|
|
110424
|
+
action: "steering",
|
|
110425
|
+
body: `### ${STEERING_COMMENT_TITLE}
|
|
109821
110426
|
|
|
109822
|
-
${deps.message.trim()}
|
|
110427
|
+
${deps.message.trim()}`,
|
|
110428
|
+
fields: { change: deps.changeName }
|
|
110429
|
+
});
|
|
109823
110430
|
try {
|
|
109824
110431
|
await deps.mutations.createIssueComment(deps.apiKey, deps.issueId, steeringBody);
|
|
109825
110432
|
deps.log(` comment-sync: posted steering comment (${firstLine})`, "gray");
|
|
@@ -109852,6 +110459,7 @@ ${deps.message.trim()}`;
|
|
|
109852
110459
|
var PLAN_COMMENT_TITLE = "\uD83D\uDCCB Ralph plan", STEERING_COMMENT_TITLE = "\uD83E\uDDED Ralph steering";
|
|
109853
110460
|
var init_comment_sync = __esm(() => {
|
|
109854
110461
|
init_store();
|
|
110462
|
+
init_src8();
|
|
109855
110463
|
init_linear_sync();
|
|
109856
110464
|
});
|
|
109857
110465
|
|
|
@@ -262718,11 +263326,12 @@ function buildAgentCoordinator(input) {
|
|
|
262718
263326
|
};
|
|
262719
263327
|
const concurrency = args.concurrency || cfg.concurrency;
|
|
262720
263328
|
const pollInterval = args.pollInterval || cfg.pollIntervalSeconds;
|
|
262721
|
-
const
|
|
263329
|
+
const isGithubTracker = cfg.tracker.kind === "github";
|
|
263330
|
+
const indicators = isGithubTracker ? githubIndicators(cfg.github?.issues) : mergeIndicators(cfg.linear.indicators, args.indicators);
|
|
262722
263331
|
const team = args.linearTeam || cfg.linear.team;
|
|
262723
263332
|
const { assignee, anyAssignee, requireAllLabels } = resolveLinearFilter(applyAssigneeOverride(cfg.linear.filter, args.linearAssignee));
|
|
262724
263333
|
const ticketNumbers = resolveTicketNumbers(args.ticketTokens, team);
|
|
262725
|
-
const excludeFromTodo = unionMarkers(indicators.setDone, indicators.setError);
|
|
263334
|
+
const excludeFromTodo = isGithubTracker ? unionMarkers(indicators.setDone, indicators.setError, indicators.setInProgress) : unionMarkers(indicators.setDone, indicators.setError);
|
|
262726
263335
|
const gitRunner = input.runners?.git ?? bunGitRunner;
|
|
262727
263336
|
const cmdRunner = input.runners?.cmd ?? bunCmdRunner;
|
|
262728
263337
|
const cwdByChange = new Map;
|
|
@@ -262757,7 +263366,7 @@ function buildAgentCoordinator(input) {
|
|
|
262757
263366
|
}
|
|
262758
263367
|
return code;
|
|
262759
263368
|
});
|
|
262760
|
-
const resolvers = createLinearResolvers({
|
|
263369
|
+
const resolvers = isGithubTracker ? null : createLinearResolvers({
|
|
262761
263370
|
apiKey,
|
|
262762
263371
|
team,
|
|
262763
263372
|
assignee,
|
|
@@ -262766,6 +263375,15 @@ function buildAgentCoordinator(input) {
|
|
|
262766
263375
|
diag,
|
|
262767
263376
|
...ticketNumbers.length > 0 ? { ticketNumbers } : {}
|
|
262768
263377
|
});
|
|
263378
|
+
const provider = isGithubTracker ? createGithubTrackerProvider({
|
|
263379
|
+
issues: cfg.github?.issues,
|
|
263380
|
+
cmdRunner,
|
|
263381
|
+
projectRoot,
|
|
263382
|
+
diag
|
|
263383
|
+
}) : {
|
|
263384
|
+
...resolvers,
|
|
263385
|
+
fetchDoneCandidates: () => fetchDoneCandidatesWith(apiKey, team, assignee, anyAssignee, requireAllLabels, indicators, ticketNumbers.length > 0 ? ticketNumbers : undefined)
|
|
263386
|
+
};
|
|
262769
263387
|
if (ticketNumbers.length > 0) {
|
|
262770
263388
|
const hasGetIndicator = [indicators.getTodo, indicators.getInProgress].some((ind) => ind && ind.filter.length > 0);
|
|
262771
263389
|
if (!hasGetIndicator) {
|
|
@@ -262796,7 +263414,7 @@ function buildAgentCoordinator(input) {
|
|
|
262796
263414
|
scriptRunner,
|
|
262797
263415
|
...input.runners?.worktree ? { worktreeProvider: input.runners.worktree } : {}
|
|
262798
263416
|
});
|
|
262799
|
-
const fetchMentions = createMentionScanner({
|
|
263417
|
+
const fetchMentions = isGithubTracker ? async () => [] : createMentionScanner({
|
|
262800
263418
|
apiKey,
|
|
262801
263419
|
args,
|
|
262802
263420
|
cfg,
|
|
@@ -262816,6 +263434,27 @@ function buildAgentCoordinator(input) {
|
|
|
262816
263434
|
lastHandledReviewActivity,
|
|
262817
263435
|
resolvePrUrlForIssue: prDiscovery.resolvePrUrlForIssue
|
|
262818
263436
|
});
|
|
263437
|
+
const tracker = isGithubTracker ? {
|
|
263438
|
+
fetchTodo: () => provider.fetchByGet(indicators.getTodo, excludeFromTodo),
|
|
263439
|
+
fetchInProgress: () => provider.fetchByGet(indicators.getInProgress, unionMarkers(indicators.setError)),
|
|
263440
|
+
fetchReview: async () => [],
|
|
263441
|
+
fetchMentions,
|
|
263442
|
+
fetchDoneCandidates: () => provider.fetchDoneCandidates(),
|
|
263443
|
+
applyIndicator: provider.applyIndicator,
|
|
263444
|
+
removeIndicator: provider.removeIndicator,
|
|
263445
|
+
postComment: (issue2, body) => provider.applyMarker(issue2, { type: "comment", value: body }),
|
|
263446
|
+
fetchComments: async () => []
|
|
263447
|
+
} : createLinearTrackerProvider({
|
|
263448
|
+
apiKey,
|
|
263449
|
+
team,
|
|
263450
|
+
assignee,
|
|
263451
|
+
anyAssignee,
|
|
263452
|
+
requireAllLabels,
|
|
263453
|
+
indicators,
|
|
263454
|
+
resolvers,
|
|
263455
|
+
fetchMentions,
|
|
263456
|
+
...ticketNumbers.length > 0 ? { ticketNumbers } : {}
|
|
263457
|
+
});
|
|
262819
263458
|
const spawnWorker = createSpawnWorker({
|
|
262820
263459
|
args,
|
|
262821
263460
|
cfg,
|
|
@@ -262827,7 +263466,7 @@ function buildAgentCoordinator(input) {
|
|
|
262827
263466
|
indicators,
|
|
262828
263467
|
cmdRunner,
|
|
262829
263468
|
gitRunner,
|
|
262830
|
-
applyIndicator:
|
|
263469
|
+
applyIndicator: provider.applyIndicator,
|
|
262831
263470
|
bus,
|
|
262832
263471
|
onLog,
|
|
262833
263472
|
diag,
|
|
@@ -262871,8 +263510,8 @@ function buildAgentCoordinator(input) {
|
|
|
262871
263510
|
cwdOf: (cn) => cwdByChange.get(cn),
|
|
262872
263511
|
awaitingChangeSet,
|
|
262873
263512
|
reapForAwaiting: (cn) => coordRef.current?.reapForAwaiting(cn),
|
|
262874
|
-
applyIndicator:
|
|
262875
|
-
applyMarker:
|
|
263513
|
+
applyIndicator: provider.applyIndicator,
|
|
263514
|
+
applyMarker: provider.applyMarker,
|
|
262876
263515
|
openDraftPr,
|
|
262877
263516
|
...onAwaitingTicket ? { onAwaitingTicket } : {},
|
|
262878
263517
|
onLog
|
|
@@ -262902,7 +263541,7 @@ function buildAgentCoordinator(input) {
|
|
|
262902
263541
|
projectRoot,
|
|
262903
263542
|
maxRecoveryAttempts: cfg.prRecovery.maxRecoverySessions
|
|
262904
263543
|
}) : null;
|
|
262905
|
-
const commentSync = createCommentSyncHooks({
|
|
263544
|
+
const commentSync = isGithubTracker ? { enabled: false } : createCommentSyncHooks({
|
|
262906
263545
|
apiKey,
|
|
262907
263546
|
cfg,
|
|
262908
263547
|
projectRoot,
|
|
@@ -262915,20 +263554,18 @@ function buildAgentCoordinator(input) {
|
|
|
262915
263554
|
beforePoll: () => {
|
|
262916
263555
|
pollContext = new PollContext;
|
|
262917
263556
|
},
|
|
262918
|
-
fetchTodo:
|
|
262919
|
-
fetchInProgress:
|
|
262920
|
-
fetchMentions,
|
|
262921
|
-
fetchDoneCandidates:
|
|
263557
|
+
fetchTodo: tracker.fetchTodo,
|
|
263558
|
+
fetchInProgress: tracker.fetchInProgress,
|
|
263559
|
+
fetchMentions: tracker.fetchMentions,
|
|
263560
|
+
fetchDoneCandidates: tracker.fetchDoneCandidates,
|
|
263561
|
+
fetchReview: tracker.fetchReview,
|
|
262922
263562
|
prepare: prep.prepare,
|
|
262923
263563
|
prepareTaskForTrigger: prep.prepareTaskForTrigger,
|
|
262924
263564
|
spawnWorker,
|
|
262925
|
-
applyIndicator:
|
|
262926
|
-
removeIndicator:
|
|
262927
|
-
postComment:
|
|
262928
|
-
fetchComments:
|
|
262929
|
-
const c = await fetchIssueComments(apiKey, issueId);
|
|
262930
|
-
return c.map((x2) => ({ body: x2.body }));
|
|
262931
|
-
},
|
|
263565
|
+
applyIndicator: tracker.applyIndicator,
|
|
263566
|
+
removeIndicator: tracker.removeIndicator,
|
|
263567
|
+
postComment: tracker.postComment,
|
|
263568
|
+
fetchComments: tracker.fetchComments,
|
|
262932
263569
|
checkPrStatus: prDiscovery.checkPrStatus,
|
|
262933
263570
|
hasPrForChange: (changeName) => prByChange.has(changeName),
|
|
262934
263571
|
isChangeArchivedForIssue: (issue2) => isChangeArchivedForIssue(issue2, cwdByChange, projectRoot),
|
|
@@ -262976,7 +263613,7 @@ function buildAgentCoordinator(input) {
|
|
|
262976
263613
|
}
|
|
262977
263614
|
});
|
|
262978
263615
|
coordRef.current = coord;
|
|
262979
|
-
const filterDesc = describeIndicators(indicators, team, assignee, anyAssignee);
|
|
263616
|
+
const filterDesc = describeIndicators(indicators, team, assignee, anyAssignee, requireAllLabels);
|
|
262980
263617
|
const runBaselineGateOnce = createBaselineGateRunner({
|
|
262981
263618
|
args,
|
|
262982
263619
|
cfg,
|
|
@@ -262987,7 +263624,7 @@ function buildAgentCoordinator(input) {
|
|
|
262987
263624
|
gitRunner,
|
|
262988
263625
|
coord,
|
|
262989
263626
|
onLog,
|
|
262990
|
-
resolveLabelIdForTeam:
|
|
263627
|
+
resolveLabelIdForTeam: provider.resolveLabelIdForTeam
|
|
262991
263628
|
});
|
|
262992
263629
|
return {
|
|
262993
263630
|
coord,
|
|
@@ -263015,7 +263652,6 @@ var init_wire = __esm(() => {
|
|
|
263015
263652
|
init_workflow();
|
|
263016
263653
|
init_src2();
|
|
263017
263654
|
init_coordinator2();
|
|
263018
|
-
init_linear();
|
|
263019
263655
|
init_layout();
|
|
263020
263656
|
init_scaffold();
|
|
263021
263657
|
init_awaiting();
|
|
@@ -263024,6 +263660,8 @@ var init_wire = __esm(() => {
|
|
|
263024
263660
|
init_indicators();
|
|
263025
263661
|
init_task_bodies();
|
|
263026
263662
|
init_linear_resolvers();
|
|
263663
|
+
init_github();
|
|
263664
|
+
init_linear_tracker_provider();
|
|
263027
263665
|
init_linear_client();
|
|
263028
263666
|
init_prepare();
|
|
263029
263667
|
init_pr_discovery();
|
|
@@ -266187,7 +266825,7 @@ async function main3(argv) {
|
|
|
266187
266825
|
return typeof process.exitCode === "number" ? process.exitCode : 0;
|
|
266188
266826
|
}
|
|
266189
266827
|
var import_react64;
|
|
266190
|
-
var
|
|
266828
|
+
var init_src9 = __esm(async () => {
|
|
266191
266829
|
init_context();
|
|
266192
266830
|
init_layout();
|
|
266193
266831
|
init_paths();
|
|
@@ -266245,7 +266883,7 @@ async function dispatch(subcommand, rest2) {
|
|
|
266245
266883
|
return main4(rest2);
|
|
266246
266884
|
}
|
|
266247
266885
|
if (subcommand === "agent") {
|
|
266248
|
-
const { main: main4 } = await
|
|
266886
|
+
const { main: main4 } = await init_src9().then(() => exports_src3);
|
|
266249
266887
|
return main4(rest2);
|
|
266250
266888
|
}
|
|
266251
266889
|
if (subcommand === "task") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neriros/ralphy",
|
|
3
|
-
"version": "3.10.
|
|
3
|
+
"version": "3.10.17",
|
|
4
4
|
"description": "An iterative AI task execution framework. Orchestrates multi-phase autonomous work using Claude or Codex engines.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"check:circular:ci": "depcruise packages/*/src apps/*/src --config .dependency-cruiser.cjs",
|
|
52
52
|
"check:unused:ci": "knip",
|
|
53
53
|
"check:outdated:ci": "bun scripts/check-outdated.ts",
|
|
54
|
-
"check:structure": "bun scripts/check-prop-drilling.ts && bun scripts/check-hooks-location.ts && bun scripts/check-folder-size.ts && bun scripts/check-filename-case.ts && bun scripts/check-single-component.ts && bun scripts/check-no-reexport-tsx.ts && bun scripts/check-test-location.ts && bun scripts/check-static-error-messages.ts && bash scripts/check-no-unsafe-casts.sh",
|
|
54
|
+
"check:structure": "bun scripts/check-prop-drilling.ts && bun scripts/check-hooks-location.ts && bun scripts/check-folder-size.ts && bun scripts/check-filename-case.ts && bun scripts/check-single-component.ts && bun scripts/check-no-reexport-tsx.ts && bun scripts/check-test-location.ts && bun scripts/check-static-error-messages.ts && bash scripts/check-no-unsafe-casts.sh && bun scripts/check-tracker-seam.ts",
|
|
55
55
|
"check:shell": "bash scripts/check-shell.sh",
|
|
56
56
|
"check:duplicates": "bun scripts/check-duplicate-declarations.ts --diff",
|
|
57
57
|
"check:duplicates:all": "bun scripts/check-duplicate-declarations.ts --all",
|