@neriros/ralphy 3.10.4 → 3.10.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/shell/index.js +504 -104
- package/package.json +1 -1
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.6")
|
|
18932
|
+
return "3.10.6";
|
|
18933
18933
|
} catch {}
|
|
18934
18934
|
const dirsToTry = [];
|
|
18935
18935
|
try {
|
|
@@ -80505,7 +80505,21 @@ var init_zod = __esm(() => {
|
|
|
80505
80505
|
});
|
|
80506
80506
|
|
|
80507
80507
|
// packages/workflow/src/schema.ts
|
|
80508
|
-
|
|
80508
|
+
function foldLegacyAssignee(v) {
|
|
80509
|
+
if (!v || typeof v !== "object" || Array.isArray(v))
|
|
80510
|
+
return v;
|
|
80511
|
+
const obj = v;
|
|
80512
|
+
if (!("assignee" in obj))
|
|
80513
|
+
return v;
|
|
80514
|
+
const { assignee, ...rest2 } = obj;
|
|
80515
|
+
if (rest2["filter"] === undefined) {
|
|
80516
|
+
const raw = typeof assignee === "string" ? assignee.trim() : "";
|
|
80517
|
+
const value = raw === "" || raw.toLowerCase() === "unassigned" ? "unassigned" : raw;
|
|
80518
|
+
rest2["filter"] = `assignee = ${value}`;
|
|
80519
|
+
}
|
|
80520
|
+
return rest2;
|
|
80521
|
+
}
|
|
80522
|
+
var CURRENT_WORKFLOW_VERSION = 3, MarkerSchema, SET_INDICATOR_KEYS, GetIndicatorSchema, SetIndicatorSchema, IndicatorsSchema, ProjectSchema, CommandsSchema, DEFAULT_META_ONLY_FILES, BoundariesSchema, WorkflowConfigSchema;
|
|
80509
80523
|
var init_schema = __esm(() => {
|
|
80510
80524
|
init_zod();
|
|
80511
80525
|
MarkerSchema = exports_external.discriminatedUnion("type", [
|
|
@@ -80635,9 +80649,9 @@ var init_schema = __esm(() => {
|
|
|
80635
80649
|
ignoreCiChecks: exports_external.array(exports_external.string()).default([]),
|
|
80636
80650
|
engine: exports_external.enum(["claude", "codex"]).default("claude"),
|
|
80637
80651
|
model: exports_external.enum(["haiku", "sonnet", "opus"]).default("opus"),
|
|
80638
|
-
linear: exports_external.object({
|
|
80652
|
+
linear: exports_external.preprocess(foldLegacyAssignee, exports_external.object({
|
|
80639
80653
|
team: exports_external.string().optional(),
|
|
80640
|
-
|
|
80654
|
+
filter: exports_external.string().default("assignee = me"),
|
|
80641
80655
|
postComments: exports_external.boolean().default(true),
|
|
80642
80656
|
updateEveryIterations: exports_external.number().int().nonnegative().default(10),
|
|
80643
80657
|
mentionTrigger: exports_external.boolean().default(true),
|
|
@@ -80657,7 +80671,8 @@ var init_schema = __esm(() => {
|
|
|
80657
80671
|
maxConfirmationRounds: 3
|
|
80658
80672
|
}),
|
|
80659
80673
|
indicators: IndicatorsSchema.default({})
|
|
80660
|
-
}).strict().default({
|
|
80674
|
+
}).strict()).default({
|
|
80675
|
+
filter: "assignee = me",
|
|
80661
80676
|
postComments: true,
|
|
80662
80677
|
updateEveryIterations: 10,
|
|
80663
80678
|
mentionTrigger: true,
|
|
@@ -80746,7 +80761,7 @@ var init_schema = __esm(() => {
|
|
|
80746
80761
|
var FRONTMATTER_RE, DEFAULT_WORKFLOW_MD = `---
|
|
80747
80762
|
# WORKFLOW.md schema version \u2014 managed by \`ralphy init\`. When a newer version
|
|
80748
80763
|
# ships, re-running init migrates this file and fills in the new settings.
|
|
80749
|
-
version:
|
|
80764
|
+
version: 2
|
|
80750
80765
|
|
|
80751
80766
|
project:
|
|
80752
80767
|
name: ralphy
|
|
@@ -80810,6 +80825,7 @@ preExistingErrorCheck:
|
|
|
80810
80825
|
outputCharLimit: 4000
|
|
80811
80826
|
|
|
80812
80827
|
linear:
|
|
80828
|
+
filter: assignee = me
|
|
80813
80829
|
postComments: true
|
|
80814
80830
|
updateEveryIterations: 10
|
|
80815
80831
|
mentionTrigger: true
|
|
@@ -81056,7 +81072,7 @@ function modelOptionValues() {
|
|
|
81056
81072
|
const field = findField("model");
|
|
81057
81073
|
return field && field.spec.kind === "select" ? field.spec.options.map((o) => o.value) : [];
|
|
81058
81074
|
}
|
|
81059
|
-
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,
|
|
81075
|
+
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_FILTER, QUICK_FIELDS, isOn = (id) => (answers) => answers[id] === true, CUSTOMIZED_FIELDS, COMMON_CLI_OPTIONS, FIELD_DESCRIPTIONS;
|
|
81060
81076
|
var init_fields = __esm(() => {
|
|
81061
81077
|
PROJECT_NAME = {
|
|
81062
81078
|
id: "project.name",
|
|
@@ -81079,15 +81095,15 @@ var init_fields = __esm(() => {
|
|
|
81079
81095
|
spec: yes(),
|
|
81080
81096
|
when: (answers) => typeof answers["repo.name"] === "string" && answers["repo.name"] !== ""
|
|
81081
81097
|
};
|
|
81082
|
-
|
|
81083
|
-
id: "linear.
|
|
81084
|
-
label: "Linear
|
|
81085
|
-
hint: "
|
|
81086
|
-
description: "
|
|
81087
|
-
emptyLabel: "
|
|
81088
|
-
spec: { kind: "text" }
|
|
81098
|
+
LINEAR_FILTER = {
|
|
81099
|
+
id: "linear.filter",
|
|
81100
|
+
label: "Linear filter",
|
|
81101
|
+
hint: "e.g. 'assignee = me', 'assignee = any', 'assignee = unassigned', or an email/user-id",
|
|
81102
|
+
description: "Global filter applied to every Linear ticket fetch. The only clause today is 'assignee = <value>', where <value> is 'me' (issues assigned to you), 'any' (regardless of assignee), 'unassigned', a Linear user id, or an email. Blank defaults to 'assignee = me'.",
|
|
81103
|
+
emptyLabel: "assignee = me",
|
|
81104
|
+
spec: { kind: "text", placeholder: "assignee = me" }
|
|
81089
81105
|
};
|
|
81090
|
-
QUICK_FIELDS = [PROJECT_NAME, LINEAR_TEAM, REPO_LINK,
|
|
81106
|
+
QUICK_FIELDS = [PROJECT_NAME, LINEAR_TEAM, REPO_LINK, LINEAR_FILTER];
|
|
81091
81107
|
CUSTOMIZED_FIELDS = [
|
|
81092
81108
|
PROJECT_NAME,
|
|
81093
81109
|
{
|
|
@@ -81336,7 +81352,7 @@ var init_fields = __esm(() => {
|
|
|
81336
81352
|
},
|
|
81337
81353
|
LINEAR_TEAM,
|
|
81338
81354
|
REPO_LINK,
|
|
81339
|
-
|
|
81355
|
+
LINEAR_FILTER,
|
|
81340
81356
|
{
|
|
81341
81357
|
id: "linear.postComments",
|
|
81342
81358
|
label: "Post progress comments on the Linear issue?",
|
|
@@ -81725,6 +81741,34 @@ function describeApprovalMarker(indicator) {
|
|
|
81725
81741
|
return `${phrases.slice(0, -1).join(", ")}, or ${phrases[phrases.length - 1]}`;
|
|
81726
81742
|
}
|
|
81727
81743
|
|
|
81744
|
+
// packages/workflow/src/linear-filter.ts
|
|
81745
|
+
function parseLinearFilter(filter2) {
|
|
81746
|
+
const trimmed = filter2.trim();
|
|
81747
|
+
if (trimmed === "")
|
|
81748
|
+
return { assignee: "me" };
|
|
81749
|
+
const eq = trimmed.indexOf("=");
|
|
81750
|
+
if (eq < 0) {
|
|
81751
|
+
throw new Error(`Invalid linear.filter "${filter2}": expected "<key> = <value>" (e.g. "assignee = me").`);
|
|
81752
|
+
}
|
|
81753
|
+
const key = trimmed.slice(0, eq).trim().toLowerCase();
|
|
81754
|
+
const value = trimmed.slice(eq + 1).trim();
|
|
81755
|
+
if (!SUPPORTED_KEYS.has(key)) {
|
|
81756
|
+
throw new Error(`Unrecognized linear.filter key "${key}" in "${filter2}". Supported keys: ${[...SUPPORTED_KEYS].join(", ")}.`);
|
|
81757
|
+
}
|
|
81758
|
+
const lower = value.toLowerCase();
|
|
81759
|
+
if (lower === "any")
|
|
81760
|
+
return { anyAssignee: true };
|
|
81761
|
+
if (lower === "" || lower === "unassigned")
|
|
81762
|
+
return { assignee: "unassigned" };
|
|
81763
|
+
if (lower === "me")
|
|
81764
|
+
return { assignee: "me" };
|
|
81765
|
+
return { assignee: value };
|
|
81766
|
+
}
|
|
81767
|
+
var SUPPORTED_KEYS;
|
|
81768
|
+
var init_linear_filter = __esm(() => {
|
|
81769
|
+
SUPPORTED_KEYS = new Set(["assignee"]);
|
|
81770
|
+
});
|
|
81771
|
+
|
|
81728
81772
|
// packages/workflow/src/workflow.ts
|
|
81729
81773
|
var exports_workflow = {};
|
|
81730
81774
|
__export(exports_workflow, {
|
|
@@ -81733,6 +81777,7 @@ __export(exports_workflow, {
|
|
|
81733
81777
|
renderWorkflowPrompt: () => renderWorkflowPrompt,
|
|
81734
81778
|
renderTemplate: () => renderTemplate,
|
|
81735
81779
|
parseWorkflow: () => parseWorkflow,
|
|
81780
|
+
parseLinearFilter: () => parseLinearFilter,
|
|
81736
81781
|
matchesIndicator: () => matchesIndicator,
|
|
81737
81782
|
loadWorkflow: () => loadWorkflow,
|
|
81738
81783
|
ensureWorkflow: () => ensureWorkflow,
|
|
@@ -81896,6 +81941,7 @@ var init_workflow = __esm(() => {
|
|
|
81896
81941
|
init_wizard();
|
|
81897
81942
|
init_schema();
|
|
81898
81943
|
init_default();
|
|
81944
|
+
init_linear_filter();
|
|
81899
81945
|
import_yaml2 = __toESM(require_dist(), 1);
|
|
81900
81946
|
});
|
|
81901
81947
|
|
|
@@ -83968,6 +84014,11 @@ var init_migrations = __esm(() => {
|
|
|
83968
84014
|
version: 2,
|
|
83969
84015
|
description: "Ralphy now detects the current git repo and records it in WORKFLOW.md, " + "linking it to your Linear team. Confirm the detected repo to adopt it.",
|
|
83970
84016
|
fields: ["repo.link"]
|
|
84017
|
+
},
|
|
84018
|
+
{
|
|
84019
|
+
version: 3,
|
|
84020
|
+
description: "The per-workflow `linear.assignee` setting is replaced by a global " + "`linear.filter` expression (e.g. `assignee = me`) applied to every " + "ticket fetch. Existing `assignee` values are folded in automatically; " + "note that an empty filter now defaults to `assignee = me` (it previously " + "meant unassigned-only).",
|
|
84021
|
+
fields: ["linear.filter"]
|
|
83971
84022
|
}
|
|
83972
84023
|
];
|
|
83973
84024
|
LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max2, migration) => Math.max(max2, migration.version), 0);
|
|
@@ -84055,8 +84106,8 @@ function initialValuesFromConfig(config2) {
|
|
|
84055
84106
|
values2["useWorktree"] = config2.useWorktree;
|
|
84056
84107
|
if (config2.linear.team)
|
|
84057
84108
|
values2["linear.team"] = config2.linear.team;
|
|
84058
|
-
if (config2.linear.
|
|
84059
|
-
values2["linear.
|
|
84109
|
+
if (config2.linear.filter)
|
|
84110
|
+
values2["linear.filter"] = config2.linear.filter;
|
|
84060
84111
|
return values2;
|
|
84061
84112
|
}
|
|
84062
84113
|
async function promptEditOrExit() {
|
|
@@ -101403,6 +101454,7 @@ async function parseAgentArgs(argv) {
|
|
|
101403
101454
|
...common2,
|
|
101404
101455
|
mode: "agent",
|
|
101405
101456
|
linearTeam: "",
|
|
101457
|
+
linearFilter: "",
|
|
101406
101458
|
linearAssignee: "",
|
|
101407
101459
|
pollInterval: 0,
|
|
101408
101460
|
concurrency: 0,
|
|
@@ -101419,10 +101471,12 @@ async function parseAgentArgs(argv) {
|
|
|
101419
101471
|
noTmux: false,
|
|
101420
101472
|
checks: false,
|
|
101421
101473
|
review: false,
|
|
101474
|
+
agentDebug: false,
|
|
101422
101475
|
ticketTokens: []
|
|
101423
101476
|
};
|
|
101424
101477
|
const state = emptyParseState();
|
|
101425
101478
|
let expectLinearTeam = false;
|
|
101479
|
+
let expectLinearFilter = false;
|
|
101426
101480
|
let expectLinearAssignee = false;
|
|
101427
101481
|
let expectPollInterval = false;
|
|
101428
101482
|
let expectConcurrency = false;
|
|
@@ -101436,6 +101490,11 @@ async function parseAgentArgs(argv) {
|
|
|
101436
101490
|
expectLinearTeam = false;
|
|
101437
101491
|
continue;
|
|
101438
101492
|
}
|
|
101493
|
+
if (expectLinearFilter) {
|
|
101494
|
+
result2.linearFilter = arg;
|
|
101495
|
+
expectLinearFilter = false;
|
|
101496
|
+
continue;
|
|
101497
|
+
}
|
|
101439
101498
|
if (expectLinearAssignee) {
|
|
101440
101499
|
result2.linearAssignee = arg;
|
|
101441
101500
|
expectLinearAssignee = false;
|
|
@@ -101481,6 +101540,9 @@ async function parseAgentArgs(argv) {
|
|
|
101481
101540
|
case "--linear-team":
|
|
101482
101541
|
expectLinearTeam = true;
|
|
101483
101542
|
break;
|
|
101543
|
+
case "--linear-filter":
|
|
101544
|
+
expectLinearFilter = true;
|
|
101545
|
+
break;
|
|
101484
101546
|
case "--linear-assignee":
|
|
101485
101547
|
expectLinearAssignee = true;
|
|
101486
101548
|
break;
|
|
@@ -101532,6 +101594,9 @@ async function parseAgentArgs(argv) {
|
|
|
101532
101594
|
case "--debug":
|
|
101533
101595
|
result2.debug = true;
|
|
101534
101596
|
break;
|
|
101597
|
+
case "--agent-debug":
|
|
101598
|
+
result2.agentDebug = true;
|
|
101599
|
+
break;
|
|
101535
101600
|
case "--pre-existing-error-check":
|
|
101536
101601
|
result2.preExistingErrorCheck = true;
|
|
101537
101602
|
break;
|
|
@@ -101605,7 +101670,8 @@ var init_cli2 = __esm(() => {
|
|
|
101605
101670
|
" --log Log raw engine stream",
|
|
101606
101671
|
" --verbose Verbose output",
|
|
101607
101672
|
" --linear-team <key> Linear team key (e.g. ENG)",
|
|
101608
|
-
" --linear-
|
|
101673
|
+
" --linear-filter <expr> Global Linear filter (e.g. 'assignee = me', 'assignee = any')",
|
|
101674
|
+
" --linear-assignee <id> [deprecated] Filter by assignee; use --linear-filter instead",
|
|
101609
101675
|
" --poll-interval <s> Seconds between Linear polls (default: 60)",
|
|
101610
101676
|
" --concurrency <n> Max concurrent task loops (default: 1)",
|
|
101611
101677
|
" --worktree Run each task in its own git worktree",
|
|
@@ -101630,6 +101696,7 @@ var init_cli2 = __esm(() => {
|
|
|
101630
101696
|
" --checks List mode: show failing CI check names per PR",
|
|
101631
101697
|
" --review List mode: show unresolved review comment count per PR",
|
|
101632
101698
|
" --debug List mode: explain why a Linear ticket was not picked up (use with --name)",
|
|
101699
|
+
" --agent-debug After each ticket finishes, run a one-shot self-review and write a report to ~/.ralph/retro/",
|
|
101633
101700
|
" --help, -h Show this help message",
|
|
101634
101701
|
"",
|
|
101635
101702
|
"Examples:",
|
|
@@ -102290,9 +102357,11 @@ async function fetchMentionScanIssues(apiKey, spec) {
|
|
|
102290
102357
|
const where = branches.length === 1 ? { ...branches[0] } : { or: branches };
|
|
102291
102358
|
if (spec.team)
|
|
102292
102359
|
where.team = { key: { eq: spec.team } };
|
|
102293
|
-
if (spec.assignee) {
|
|
102360
|
+
if (spec.anyAssignee || spec.assignee === "any") {} else if (spec.assignee) {
|
|
102294
102361
|
if (spec.assignee === "me")
|
|
102295
102362
|
where.assignee = { isMe: { eq: true } };
|
|
102363
|
+
else if (spec.assignee === "unassigned")
|
|
102364
|
+
where.assignee = { null: true };
|
|
102296
102365
|
else if (spec.assignee.includes("@"))
|
|
102297
102366
|
where.assignee = { email: { eq: spec.assignee } };
|
|
102298
102367
|
else
|
|
@@ -104487,6 +104556,15 @@ async function runPostTask(input, deps) {
|
|
|
104487
104556
|
emit3(succeeded ? "done" : "gave-up", succeeded ? undefined : `exit ${effectiveCode}`);
|
|
104488
104557
|
if (!succeeded)
|
|
104489
104558
|
await recordGaveUp(stateFilePath, log3, changeName);
|
|
104559
|
+
await deps.runRetrospective?.({
|
|
104560
|
+
changeName,
|
|
104561
|
+
cwd: cwd2,
|
|
104562
|
+
changeDir,
|
|
104563
|
+
stateFilePath,
|
|
104564
|
+
branch,
|
|
104565
|
+
issue: issue2,
|
|
104566
|
+
effectiveCode
|
|
104567
|
+
});
|
|
104490
104568
|
await runWorktreeCleanupPhase({ changeName, cwd: cwd2, projectRoot, useWorktree, effectiveCode, cfg }, { git: git2, log: log3, emit: emit3 });
|
|
104491
104569
|
await runTeardownPhase({ cwd: cwd2, teardownScript: cfg.teardownScript }, { runScript, log: log3, emit: emit3 });
|
|
104492
104570
|
return effectiveCode;
|
|
@@ -104893,6 +104971,7 @@ class AgentCoordinator {
|
|
|
104893
104971
|
ciFailed: 0,
|
|
104894
104972
|
review: 0,
|
|
104895
104973
|
mentions: mentions.length,
|
|
104974
|
+
quarantined: 0,
|
|
104896
104975
|
awaiting: awaitingCount
|
|
104897
104976
|
};
|
|
104898
104977
|
const found2 = buckets2.todo + buckets2.inProgress + buckets2.mentions + buckets2.awaiting;
|
|
@@ -104987,6 +105066,7 @@ class AgentCoordinator {
|
|
|
104987
105066
|
ciFailed: prStatus.ciFailed,
|
|
104988
105067
|
review: 0,
|
|
104989
105068
|
mentions: mentions.length,
|
|
105069
|
+
quarantined: prStatus.quarantined,
|
|
104990
105070
|
awaiting: awaitingCount
|
|
104991
105071
|
};
|
|
104992
105072
|
const found = buckets.todo + buckets.inProgress + buckets.conflicted + buckets.ciFailed + buckets.mentions + buckets.awaiting;
|
|
@@ -105152,6 +105232,32 @@ class AgentCoordinator {
|
|
|
105152
105232
|
if (!this.deps.syncTasks || !this.deps.getIterationCount)
|
|
105153
105233
|
return;
|
|
105154
105234
|
for (const w of this.workers) {
|
|
105235
|
+
if (this.deps.getTasksFingerprint) {
|
|
105236
|
+
let fingerprint;
|
|
105237
|
+
try {
|
|
105238
|
+
fingerprint = await this.deps.getTasksFingerprint(w.changeName);
|
|
105239
|
+
} catch (err) {
|
|
105240
|
+
this.deps.onLog(`! tasks fingerprint read failed for ${w.issueIdentifier}: ${err.message}`, "yellow");
|
|
105241
|
+
continue;
|
|
105242
|
+
}
|
|
105243
|
+
if (fingerprint === null || fingerprint === w.lastSyncedTasksFingerprint) {
|
|
105244
|
+
continue;
|
|
105245
|
+
}
|
|
105246
|
+
let iteration;
|
|
105247
|
+
try {
|
|
105248
|
+
iteration = await this.deps.getIterationCount(w.changeName);
|
|
105249
|
+
} catch (err) {
|
|
105250
|
+
this.deps.onLog(`! iteration count read failed for ${w.issueIdentifier}: ${err.message}`, "yellow");
|
|
105251
|
+
continue;
|
|
105252
|
+
}
|
|
105253
|
+
try {
|
|
105254
|
+
await this.deps.syncTasks(w, iteration);
|
|
105255
|
+
w.lastSyncedTasksFingerprint = fingerprint;
|
|
105256
|
+
} catch (err) {
|
|
105257
|
+
this.deps.onLog(`! sync-tasks (poll) failed for ${w.issueIdentifier}: ${err.message}`, "yellow");
|
|
105258
|
+
}
|
|
105259
|
+
continue;
|
|
105260
|
+
}
|
|
105155
105261
|
let count;
|
|
105156
105262
|
try {
|
|
105157
105263
|
count = await this.deps.getIterationCount(w.changeName);
|
|
@@ -105182,6 +105288,7 @@ class AgentCoordinator {
|
|
|
105182
105288
|
return counts;
|
|
105183
105289
|
const preQueue = this.queue.map((q) => ({ id: q.issue.id, trigger: q.trigger }));
|
|
105184
105290
|
const preWorkers = this.workers.map((w) => ({ id: w.issueId, trigger: w.trigger }));
|
|
105291
|
+
const tracker = this.opts.prTracker;
|
|
105185
105292
|
for (const issue2 of candidates) {
|
|
105186
105293
|
if (this.workers.some((w) => w.issueId === issue2.id))
|
|
105187
105294
|
continue;
|
|
@@ -105189,6 +105296,13 @@ class AgentCoordinator {
|
|
|
105189
105296
|
continue;
|
|
105190
105297
|
if (this.queue.some((q) => q.issue.id === issue2.id))
|
|
105191
105298
|
continue;
|
|
105299
|
+
if (tracker?.isBailed(issue2.identifier) && this.errorMarkerCleared(issue2)) {
|
|
105300
|
+
await tracker.clear(issue2.identifier).catch(() => {});
|
|
105301
|
+
this.conflictNotified.delete(issue2.id);
|
|
105302
|
+
this.ciFailedNotified.delete(issue2.id);
|
|
105303
|
+
this.conflictPromoted.delete(issue2.id);
|
|
105304
|
+
this.deps.onLog(` ${issue2.identifier}: pr-tracker bail cleared (ticket back in Todo) \u2014 retrying recovery`, "cyan");
|
|
105305
|
+
}
|
|
105192
105306
|
let pr;
|
|
105193
105307
|
try {
|
|
105194
105308
|
pr = await this.deps.checkPrStatus(issue2);
|
|
@@ -105208,10 +105322,18 @@ class AgentCoordinator {
|
|
|
105208
105322
|
}
|
|
105209
105323
|
}
|
|
105210
105324
|
if (pr.status === "conflicted") {
|
|
105325
|
+
if (tracker?.isBailed(issue2.identifier)) {
|
|
105326
|
+
counts.quarantined += 1;
|
|
105327
|
+
continue;
|
|
105328
|
+
}
|
|
105329
|
+
counts.conflicted += 1;
|
|
105211
105330
|
if (this.conflictNotified.has(issue2.id))
|
|
105212
105331
|
continue;
|
|
105213
|
-
if (await this.prTrackerBail(issue2, pr.url, "conflicting"))
|
|
105332
|
+
if (await this.prTrackerBail(issue2, pr.url, "conflicting")) {
|
|
105333
|
+
counts.conflicted -= 1;
|
|
105334
|
+
counts.quarantined += 1;
|
|
105214
105335
|
continue;
|
|
105336
|
+
}
|
|
105215
105337
|
emitCapture(this.bus, "agent_conflict_detected", { issue_identifier: issue2.identifier });
|
|
105216
105338
|
this.conflictNotified.add(issue2.id);
|
|
105217
105339
|
this.deps.onLog(` ${issue2.identifier}: PR ${pr.url} conflicting \u2014 queued (conflict-fix)`, "yellow");
|
|
@@ -105230,14 +105352,21 @@ class AgentCoordinator {
|
|
|
105230
105352
|
trigger: "conflict-fix",
|
|
105231
105353
|
priority: defaultPriorityFor("conflict-fix")
|
|
105232
105354
|
});
|
|
105233
|
-
counts.conflicted += 1;
|
|
105234
105355
|
continue;
|
|
105235
105356
|
}
|
|
105236
105357
|
if (pr.status === "ci_failed") {
|
|
105358
|
+
if (tracker?.isBailed(issue2.identifier)) {
|
|
105359
|
+
counts.quarantined += 1;
|
|
105360
|
+
continue;
|
|
105361
|
+
}
|
|
105362
|
+
counts.ciFailed += 1;
|
|
105237
105363
|
if (this.ciFailedNotified.has(issue2.id))
|
|
105238
105364
|
continue;
|
|
105239
|
-
if (await this.prTrackerBail(issue2, pr.url, "ci_failed"))
|
|
105365
|
+
if (await this.prTrackerBail(issue2, pr.url, "ci_failed")) {
|
|
105366
|
+
counts.ciFailed -= 1;
|
|
105367
|
+
counts.quarantined += 1;
|
|
105240
105368
|
continue;
|
|
105369
|
+
}
|
|
105241
105370
|
emitCapture(this.bus, "agent_ci_failed_detected", { issue_identifier: issue2.identifier });
|
|
105242
105371
|
this.ciFailedNotified.add(issue2.id);
|
|
105243
105372
|
this.deps.onLog(` ${issue2.identifier}: PR ${pr.url} CI failing \u2014 queued (ci-fix)`, "yellow");
|
|
@@ -105256,7 +105385,6 @@ class AgentCoordinator {
|
|
|
105256
105385
|
trigger: "ci-fix",
|
|
105257
105386
|
priority: defaultPriorityFor("ci-fix")
|
|
105258
105387
|
});
|
|
105259
|
-
counts.ciFailed += 1;
|
|
105260
105388
|
}
|
|
105261
105389
|
}
|
|
105262
105390
|
for (const q of preQueue) {
|
|
@@ -105273,6 +105401,16 @@ class AgentCoordinator {
|
|
|
105273
105401
|
}
|
|
105274
105402
|
return counts;
|
|
105275
105403
|
}
|
|
105404
|
+
errorMarkerCleared(issue2) {
|
|
105405
|
+
const se = this.opts.setError;
|
|
105406
|
+
if (!se)
|
|
105407
|
+
return false;
|
|
105408
|
+
const wantLabels = markersOf(se).filter((m) => m.type === "label").map((m) => m.value.toLowerCase());
|
|
105409
|
+
if (wantLabels.length === 0)
|
|
105410
|
+
return false;
|
|
105411
|
+
const have = new Set(issue2.labels.map((l) => l.toLowerCase()));
|
|
105412
|
+
return !wantLabels.some((v) => have.has(v));
|
|
105413
|
+
}
|
|
105276
105414
|
async prTrackerBail(issue2, prUrl, reason) {
|
|
105277
105415
|
const tracker = this.opts.prTracker;
|
|
105278
105416
|
if (!tracker)
|
|
@@ -105399,6 +105537,7 @@ class AgentCoordinator {
|
|
|
105399
105537
|
kill: handle.kill,
|
|
105400
105538
|
lastReportedIteration: 0,
|
|
105401
105539
|
lastSyncedIteration: 0,
|
|
105540
|
+
lastSyncedTasksFingerprint: null,
|
|
105402
105541
|
restarting: false,
|
|
105403
105542
|
reapedForAwaiting: false
|
|
105404
105543
|
};
|
|
@@ -105562,6 +105701,7 @@ class AgentCoordinator {
|
|
|
105562
105701
|
kill: () => {},
|
|
105563
105702
|
lastReportedIteration: 0,
|
|
105564
105703
|
lastSyncedIteration: 0,
|
|
105704
|
+
lastSyncedTasksFingerprint: null,
|
|
105565
105705
|
restarting: false,
|
|
105566
105706
|
reapedForAwaiting: false
|
|
105567
105707
|
};
|
|
@@ -105652,7 +105792,12 @@ function triggerToFlowId(trigger) {
|
|
|
105652
105792
|
return "review-followup";
|
|
105653
105793
|
return "implement";
|
|
105654
105794
|
}
|
|
105655
|
-
var emptyPrStatus = () => ({
|
|
105795
|
+
var emptyPrStatus = () => ({
|
|
105796
|
+
mergeable: 0,
|
|
105797
|
+
conflicted: 0,
|
|
105798
|
+
ciFailed: 0,
|
|
105799
|
+
quarantined: 0
|
|
105800
|
+
}), emptyPollResult = () => ({
|
|
105656
105801
|
found: 0,
|
|
105657
105802
|
added: 0,
|
|
105658
105803
|
buckets: {
|
|
@@ -105662,6 +105807,7 @@ var emptyPrStatus = () => ({ mergeable: 0, conflicted: 0, ciFailed: 0 }), emptyP
|
|
|
105662
105807
|
ciFailed: 0,
|
|
105663
105808
|
review: 0,
|
|
105664
105809
|
mentions: 0,
|
|
105810
|
+
quarantined: 0,
|
|
105665
105811
|
awaiting: 0
|
|
105666
105812
|
},
|
|
105667
105813
|
prStatus: emptyPrStatus(),
|
|
@@ -105669,6 +105815,7 @@ var emptyPrStatus = () => ({ mergeable: 0, conflicted: 0, ciFailed: 0 }), emptyP
|
|
|
105669
105815
|
flow: {}
|
|
105670
105816
|
});
|
|
105671
105817
|
var init_coordinator = __esm(() => {
|
|
105818
|
+
init_types2();
|
|
105672
105819
|
init_post_task();
|
|
105673
105820
|
init_queue_order();
|
|
105674
105821
|
init_src();
|
|
@@ -106550,10 +106697,10 @@ function unionMarkers(...sets) {
|
|
|
106550
106697
|
}
|
|
106551
106698
|
return out;
|
|
106552
106699
|
}
|
|
106553
|
-
function describeIndicators(indicators, team, assignee) {
|
|
106700
|
+
function describeIndicators(indicators, team, assignee, anyAssignee) {
|
|
106554
106701
|
const parts = [];
|
|
106555
106702
|
parts.push(`team=${team ?? "*"}`);
|
|
106556
|
-
parts.push(`assignee=${assignee ?? "*"}`);
|
|
106703
|
+
parts.push(`assignee=${anyAssignee ? "any" : assignee ?? "*"}`);
|
|
106557
106704
|
if (indicators.getTodo) {
|
|
106558
106705
|
parts.push(`todo=[${indicators.getTodo.filter.map((m) => `${m.type}:${m.value}`).join(",")}]`);
|
|
106559
106706
|
}
|
|
@@ -106568,7 +106715,7 @@ var init_indicators = __esm(() => {
|
|
|
106568
106715
|
|
|
106569
106716
|
// apps/agent/src/agent/wire/linear-resolvers.ts
|
|
106570
106717
|
function createLinearResolvers(input) {
|
|
106571
|
-
const { apiKey, team, assignee, diag } = input;
|
|
106718
|
+
const { apiKey, team, assignee, anyAssignee, diag } = input;
|
|
106572
106719
|
const ticketNumbers = input.ticketNumbers ?? [];
|
|
106573
106720
|
const stateCache = new Map;
|
|
106574
106721
|
const labelCache = new Map;
|
|
@@ -106684,6 +106831,7 @@ function createLinearResolvers(input) {
|
|
|
106684
106831
|
const spec = {
|
|
106685
106832
|
team,
|
|
106686
106833
|
assignee,
|
|
106834
|
+
anyAssignee,
|
|
106687
106835
|
include,
|
|
106688
106836
|
exclude: excl,
|
|
106689
106837
|
...ticketNumbers.length > 0 ? { numbers: ticketNumbers } : {}
|
|
@@ -107417,6 +107565,7 @@ function createMentionScanner(input) {
|
|
|
107417
107565
|
cfg,
|
|
107418
107566
|
team,
|
|
107419
107567
|
assignee,
|
|
107568
|
+
anyAssignee,
|
|
107420
107569
|
indicators,
|
|
107421
107570
|
projectRoot,
|
|
107422
107571
|
useWorktree,
|
|
@@ -107440,6 +107589,7 @@ function createMentionScanner(input) {
|
|
|
107440
107589
|
candidates = await fetchMentionScanIssues(apiKey, {
|
|
107441
107590
|
team,
|
|
107442
107591
|
assignee,
|
|
107592
|
+
anyAssignee,
|
|
107443
107593
|
...ticketNumbers && ticketNumbers.length > 0 ? { numbers: ticketNumbers } : {},
|
|
107444
107594
|
indicators: {
|
|
107445
107595
|
...indicators.getTodo !== undefined ? { getTodo: indicators.getTodo } : {},
|
|
@@ -107659,8 +107809,190 @@ var init_default2 = __esm(() => {
|
|
|
107659
107809
|
init_log();
|
|
107660
107810
|
});
|
|
107661
107811
|
|
|
107812
|
+
// apps/agent/src/agent/state/agent-run-state.ts
|
|
107813
|
+
import { basename as basename3, join as join29 } from "path";
|
|
107814
|
+
import { homedir as homedir6 } from "os";
|
|
107815
|
+
import { mkdir as mkdir11, writeFile } from "fs/promises";
|
|
107816
|
+
function agentRunStatePath(projectRoot) {
|
|
107817
|
+
return join29(homedir6(), ".ralph", basename3(projectRoot), "agent-state.json");
|
|
107818
|
+
}
|
|
107819
|
+
async function writeAgentRunState(state) {
|
|
107820
|
+
const path = agentRunStatePath(state.projectRoot);
|
|
107821
|
+
try {
|
|
107822
|
+
await mkdir11(join29(homedir6(), ".ralph", basename3(state.projectRoot)), { recursive: true });
|
|
107823
|
+
await writeFile(path, JSON.stringify(state, null, 2) + `
|
|
107824
|
+
`, "utf-8");
|
|
107825
|
+
} catch {}
|
|
107826
|
+
}
|
|
107827
|
+
var init_agent_run_state = () => {};
|
|
107828
|
+
|
|
107829
|
+
// packages/retro/src/disposition.ts
|
|
107830
|
+
function dispositionFromExitCode(code) {
|
|
107831
|
+
switch (code) {
|
|
107832
|
+
case 0:
|
|
107833
|
+
return "done";
|
|
107834
|
+
case NO_CHANGES_EXIT2:
|
|
107835
|
+
return "no-changes";
|
|
107836
|
+
case CI_FAILED_EXIT2:
|
|
107837
|
+
return "ci-failed";
|
|
107838
|
+
case PR_FAILED_EXIT2:
|
|
107839
|
+
return "pr-failed";
|
|
107840
|
+
default:
|
|
107841
|
+
return "error";
|
|
107842
|
+
}
|
|
107843
|
+
}
|
|
107844
|
+
var CI_FAILED_EXIT2 = 70, PR_FAILED_EXIT2 = 71, NO_CHANGES_EXIT2 = 72;
|
|
107845
|
+
|
|
107846
|
+
// packages/retro/src/paths.ts
|
|
107847
|
+
import { homedir as homedir7 } from "os";
|
|
107848
|
+
import { mkdir as mkdir12 } from "fs/promises";
|
|
107849
|
+
import { join as join30 } from "path";
|
|
107850
|
+
function retroDir() {
|
|
107851
|
+
return join30(homedir7(), ".ralph", "retro");
|
|
107852
|
+
}
|
|
107853
|
+
async function resolveRetroOutputPath(identifier, date5, dir = retroDir()) {
|
|
107854
|
+
await mkdir12(dir, { recursive: true });
|
|
107855
|
+
const base2 = join30(dir, `${identifier}-${date5}.md`);
|
|
107856
|
+
if (!await Bun.file(base2).exists())
|
|
107857
|
+
return base2;
|
|
107858
|
+
for (let n = 2;; n++) {
|
|
107859
|
+
const candidate = join30(dir, `${identifier}-${date5}-${n}.md`);
|
|
107860
|
+
if (!await Bun.file(candidate).exists())
|
|
107861
|
+
return candidate;
|
|
107862
|
+
}
|
|
107863
|
+
}
|
|
107864
|
+
var init_paths2 = () => {};
|
|
107865
|
+
|
|
107866
|
+
// packages/retro/src/prompt.ts
|
|
107867
|
+
function buildRetroPrompt(ctx, outputPath) {
|
|
107868
|
+
const disposition = dispositionFromExitCode(ctx.exitCode);
|
|
107869
|
+
const { paths } = ctx;
|
|
107870
|
+
const line = (label, value) => value ? `- ${label}: ${value}` : `- ${label}: (unavailable \u2014 note this in the report)`;
|
|
107871
|
+
return [
|
|
107872
|
+
`You are a retrospective analysis agent reviewing a finished automated ticket run.`,
|
|
107873
|
+
`Your job is to read the run's artifacts and write a thorough, honest self-review`,
|
|
107874
|
+
`to a markdown file. You are NOT fixing anything \u2014 this is analysis only.`,
|
|
107875
|
+
``,
|
|
107876
|
+
`## Ticket`,
|
|
107877
|
+
``,
|
|
107878
|
+
`- Identifier: ${ctx.identifier}`,
|
|
107879
|
+
`- Change name: ${ctx.changeName}`,
|
|
107880
|
+
`- Terminal disposition: ${disposition} (worker exit code ${ctx.exitCode})`,
|
|
107881
|
+
ctx.prUrl ? `- Pull request: ${ctx.prUrl}` : `- Pull request: none was opened`,
|
|
107882
|
+
`- Date: ${ctx.date}`,
|
|
107883
|
+
``,
|
|
107884
|
+
`### Ticket details`,
|
|
107885
|
+
``,
|
|
107886
|
+
ctx.ticketDigest,
|
|
107887
|
+
``,
|
|
107888
|
+
`## Data sources`,
|
|
107889
|
+
``,
|
|
107890
|
+
`Read whatever of the following exist. If a path is missing or empty, say so`,
|
|
107891
|
+
`explicitly in the report rather than guessing.`,
|
|
107892
|
+
``,
|
|
107893
|
+
line("Change directory (proposal/design/tasks/specs)", paths.changeDir),
|
|
107894
|
+
line("Loop state file", paths.stateFilePath),
|
|
107895
|
+
line("Worker log", paths.logFile),
|
|
107896
|
+
line("JSON event log", paths.jsonLogFile),
|
|
107897
|
+
line("Agent run state", paths.agentStateFile),
|
|
107898
|
+
ctx.prUrl ? `- You may inspect the PR read-only with \`gh pr view ${ctx.prUrl}\` and \`gh pr diff ${ctx.prUrl}\`.` : `- No PR exists; skip the PR section and note "no PR".`,
|
|
107899
|
+
``,
|
|
107900
|
+
`## Required report structure`,
|
|
107901
|
+
``,
|
|
107902
|
+
`Write GitHub-flavored markdown with these sections:`,
|
|
107903
|
+
`1. **Summary** \u2014 what the ticket asked for and how the run ended.`,
|
|
107904
|
+
`2. **What went well** \u2014 concrete things the run did right.`,
|
|
107905
|
+
`3. **What went wrong / friction** \u2014 failures, retries, wasted iterations,`,
|
|
107906
|
+
` wrong turns, anything that cost time or quality.`,
|
|
107907
|
+
`4. **Root-cause analysis** \u2014 for each problem, why it happened.`,
|
|
107908
|
+
`5. **Recommendations** \u2014 specific, actionable improvements (to the prompt,`,
|
|
107909
|
+
` the tasks, the codebase, or the workflow).`,
|
|
107910
|
+
`6. **Data gaps** \u2014 which data sources were unavailable or unread.`,
|
|
107911
|
+
``,
|
|
107912
|
+
`## Output`,
|
|
107913
|
+
``,
|
|
107914
|
+
`Write the complete report to this exact path using your file-write tool:`,
|
|
107915
|
+
``,
|
|
107916
|
+
` ${outputPath}`,
|
|
107917
|
+
``,
|
|
107918
|
+
`## Hard rules`,
|
|
107919
|
+
``,
|
|
107920
|
+
`- Do NOT run any git mutation: no commit, add, push, rebase, reset, checkout,`,
|
|
107921
|
+
` branch, merge, tag, or stash.`,
|
|
107922
|
+
`- Do NOT create, edit, comment on, close, or merge any pull request or issue.`,
|
|
107923
|
+
`- Do NOT modify any source file. The ONLY file you may write is the report at`,
|
|
107924
|
+
` the path above.`,
|
|
107925
|
+
`- Read-only inspection commands (\`git log\`, \`git diff\`, \`gh pr view\`,`,
|
|
107926
|
+
` \`gh pr diff\`, reading files) are allowed.`
|
|
107927
|
+
].join(`
|
|
107928
|
+
`);
|
|
107929
|
+
}
|
|
107930
|
+
var init_prompt = () => {};
|
|
107931
|
+
|
|
107932
|
+
// packages/retro/src/retro.ts
|
|
107933
|
+
async function runRetrospective(ctx, deps) {
|
|
107934
|
+
const { log: log3, runEngine: runEngine2, seen } = deps;
|
|
107935
|
+
const disposition = dispositionFromExitCode(ctx.exitCode);
|
|
107936
|
+
const key = `${ctx.identifier}:${disposition}:${ctx.date}`;
|
|
107937
|
+
if (seen.has(key)) {
|
|
107938
|
+
log3(` retrospective skipped for ${ctx.identifier} (already generated this run)`, "gray");
|
|
107939
|
+
return { written: false, skipped: "duplicate", disposition };
|
|
107940
|
+
}
|
|
107941
|
+
seen.add(key);
|
|
107942
|
+
try {
|
|
107943
|
+
const outputPath = await resolveRetroOutputPath(ctx.identifier, ctx.date);
|
|
107944
|
+
const prompt = buildRetroPrompt(ctx, outputPath);
|
|
107945
|
+
log3(` running retrospective for ${ctx.identifier} (${disposition}) \u2192 ${outputPath}`, "cyan");
|
|
107946
|
+
await runEngine2({
|
|
107947
|
+
engine: ctx.engine,
|
|
107948
|
+
model: ctx.model,
|
|
107949
|
+
prompt,
|
|
107950
|
+
cwd: ctx.cwd,
|
|
107951
|
+
onOutput: (l) => log3(l, "gray")
|
|
107952
|
+
});
|
|
107953
|
+
const written = await Bun.file(outputPath).exists();
|
|
107954
|
+
if (written) {
|
|
107955
|
+
log3(` retrospective written: ${outputPath}`, "green");
|
|
107956
|
+
} else {
|
|
107957
|
+
log3(`! retrospective engine finished but no report was written at ${outputPath}`, "yellow");
|
|
107958
|
+
}
|
|
107959
|
+
return { written, outputPath, disposition };
|
|
107960
|
+
} catch (err) {
|
|
107961
|
+
log3(`! retrospective failed for ${ctx.identifier}: ${err.message}`, "yellow");
|
|
107962
|
+
return { written: false, disposition };
|
|
107963
|
+
}
|
|
107964
|
+
}
|
|
107965
|
+
var init_retro = __esm(() => {
|
|
107966
|
+
init_paths2();
|
|
107967
|
+
init_prompt();
|
|
107968
|
+
init_paths2();
|
|
107969
|
+
init_prompt();
|
|
107970
|
+
});
|
|
107971
|
+
|
|
107662
107972
|
// apps/agent/src/agent/wire/spawn/worker.ts
|
|
107663
|
-
import { join as
|
|
107973
|
+
import { join as join31 } from "path";
|
|
107974
|
+
function localDateStamp(d) {
|
|
107975
|
+
const y = d.getFullYear();
|
|
107976
|
+
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
107977
|
+
const day = String(d.getDate()).padStart(2, "0");
|
|
107978
|
+
return `${y}-${m}-${day}`;
|
|
107979
|
+
}
|
|
107980
|
+
function buildTicketDigest(issue2, comments) {
|
|
107981
|
+
if (!issue2)
|
|
107982
|
+
return "(ticket details unavailable)";
|
|
107983
|
+
const lines = [`Title: ${issue2.title}`, "", issue2.description?.trim() || "(no description)"];
|
|
107984
|
+
if (comments.length > 0) {
|
|
107985
|
+
lines.push("", "Comments:");
|
|
107986
|
+
for (const c of comments) {
|
|
107987
|
+
lines.push(`- ${c.user?.name ?? "unknown"}: ${c.body}`);
|
|
107988
|
+
}
|
|
107989
|
+
}
|
|
107990
|
+
return lines.join(`
|
|
107991
|
+
`);
|
|
107992
|
+
}
|
|
107993
|
+
function retroDepEntry(agentDebug, hook) {
|
|
107994
|
+
return agentDebug ? { runRetrospective: hook } : {};
|
|
107995
|
+
}
|
|
107664
107996
|
function createSpawnWorker(input) {
|
|
107665
107997
|
const {
|
|
107666
107998
|
args,
|
|
@@ -107739,10 +108071,52 @@ function createSpawnWorker(input) {
|
|
|
107739
108071
|
c.push("--from-agent");
|
|
107740
108072
|
return c;
|
|
107741
108073
|
}
|
|
108074
|
+
const retroSeen = new Set;
|
|
108075
|
+
const runRetrospectiveHook = async (info) => {
|
|
108076
|
+
try {
|
|
108077
|
+
const identifier = info.issue?.identifier ?? info.changeName;
|
|
108078
|
+
const prUrl = prByChange?.get(info.changeName) ?? null;
|
|
108079
|
+
let digest = "(ticket details unavailable)";
|
|
108080
|
+
if (info.issue) {
|
|
108081
|
+
let comments = [];
|
|
108082
|
+
try {
|
|
108083
|
+
comments = await fetchIssueComments(apiKey, info.issue.id);
|
|
108084
|
+
} catch {}
|
|
108085
|
+
digest = buildTicketDigest(info.issue, comments);
|
|
108086
|
+
}
|
|
108087
|
+
const engine = args.engineSet ? args.engine : cfg.engine;
|
|
108088
|
+
const model = args.engineSet ? args.model : cfg.model;
|
|
108089
|
+
const ctx = {
|
|
108090
|
+
identifier,
|
|
108091
|
+
changeName: info.changeName,
|
|
108092
|
+
cwd: info.cwd,
|
|
108093
|
+
engine,
|
|
108094
|
+
model,
|
|
108095
|
+
exitCode: info.effectiveCode,
|
|
108096
|
+
prUrl,
|
|
108097
|
+
date: localDateStamp(new Date),
|
|
108098
|
+
ticketDigest: digest,
|
|
108099
|
+
paths: {
|
|
108100
|
+
changeDir: info.changeDir,
|
|
108101
|
+
stateFilePath: info.stateFilePath,
|
|
108102
|
+
logFile: join31(logsDir, `${info.changeName}.log`),
|
|
108103
|
+
jsonLogFile: args.jsonLogFile ?? null,
|
|
108104
|
+
agentStateFile: agentRunStatePath(projectRoot)
|
|
108105
|
+
}
|
|
108106
|
+
};
|
|
108107
|
+
await runRetrospective(ctx, {
|
|
108108
|
+
runEngine: (opts) => runEngine(opts),
|
|
108109
|
+
log: onLog,
|
|
108110
|
+
seen: retroSeen
|
|
108111
|
+
});
|
|
108112
|
+
} catch (err) {
|
|
108113
|
+
onLog(`! retrospective failed: ${err.message}`, "yellow");
|
|
108114
|
+
}
|
|
108115
|
+
};
|
|
107742
108116
|
return function spawnWorker(changeName, _issue, trigger) {
|
|
107743
108117
|
const cwd2 = cwdByChange.get(changeName) ?? projectRoot;
|
|
107744
108118
|
const injected = runners?.spawnWorker;
|
|
107745
|
-
const missionTasksPath =
|
|
108119
|
+
const missionTasksPath = join31(projectLayout(cwd2).changeDir(changeName), MISSION_TASKS_FILENAME);
|
|
107746
108120
|
const prevTasksPromise = (async () => {
|
|
107747
108121
|
const f2 = Bun.file(missionTasksPath);
|
|
107748
108122
|
return await f2.exists() ? await f2.text() : "";
|
|
@@ -107750,7 +108124,7 @@ function createSpawnWorker(input) {
|
|
|
107750
108124
|
let logFilePath;
|
|
107751
108125
|
let handle;
|
|
107752
108126
|
if (injected) {
|
|
107753
|
-
logFilePath =
|
|
108127
|
+
logFilePath = join31(logsDir, `${changeName}.log`);
|
|
107754
108128
|
handle = injected(buildTaskCmdFor(changeName), cwd2);
|
|
107755
108129
|
} else {
|
|
107756
108130
|
const r = defaultSpawn(changeName, buildTaskCmdFor(changeName), cwd2, logsDir, onWorkerOutput, `spawn at ${new Date().toISOString()}`);
|
|
@@ -107772,7 +108146,7 @@ function createSpawnWorker(input) {
|
|
|
107772
108146
|
const wantAutoMerge = issueForChange ? issueMatchesGetIndicator(issueForChange, indicators.getAutoMerge) : false;
|
|
107773
108147
|
const wrapped = handle.exited.then(async (code) => {
|
|
107774
108148
|
const workerLayout = projectLayout(cwd2);
|
|
107775
|
-
const validateSpecPath =
|
|
108149
|
+
const validateSpecPath = join31(workerLayout.changeDir(changeName), "specs", "validate.md");
|
|
107776
108150
|
const hasValidateSpec = await Bun.file(validateSpecPath).exists();
|
|
107777
108151
|
const wantValidateOnly = hasValidateSpec && !wantPrBase;
|
|
107778
108152
|
if (hasValidateSpec) {
|
|
@@ -107857,6 +108231,7 @@ function createSpawnWorker(input) {
|
|
|
107857
108231
|
git: gitRunner,
|
|
107858
108232
|
log: onLog,
|
|
107859
108233
|
runScript,
|
|
108234
|
+
...retroDepEntry(args.agentDebug, runRetrospectiveHook),
|
|
107860
108235
|
registerPr: (cn, url2) => onPrRegistered(cn, url2),
|
|
107861
108236
|
...onWorkerPhase && {
|
|
107862
108237
|
onPhase: (phase2, detail) => onWorkerPhase(changeName, phase2, detail)
|
|
@@ -107892,6 +108267,9 @@ var init_worker = __esm(() => {
|
|
|
107892
108267
|
init_runners();
|
|
107893
108268
|
init_pr_helpers();
|
|
107894
108269
|
init_wait_for_mergeability();
|
|
108270
|
+
init_agent_run_state();
|
|
108271
|
+
init_retro();
|
|
108272
|
+
init_engine();
|
|
107895
108273
|
});
|
|
107896
108274
|
|
|
107897
108275
|
// apps/agent/src/agent/baseline/runner.ts
|
|
@@ -108234,8 +108612,8 @@ var init_linear_sync = __esm(() => {
|
|
|
108234
108612
|
});
|
|
108235
108613
|
|
|
108236
108614
|
// apps/agent/src/agent/linear-sync/comment-sync.ts
|
|
108237
|
-
import { dirname as dirname13, join as
|
|
108238
|
-
import { mkdir as
|
|
108615
|
+
import { dirname as dirname13, join as join32 } from "path";
|
|
108616
|
+
import { mkdir as mkdir13, rename, unlink as unlink2 } from "fs/promises";
|
|
108239
108617
|
async function readStateJson(statePath) {
|
|
108240
108618
|
const file2 = Bun.file(statePath);
|
|
108241
108619
|
if (!await file2.exists())
|
|
@@ -108247,7 +108625,7 @@ async function readStateJson(statePath) {
|
|
|
108247
108625
|
}
|
|
108248
108626
|
}
|
|
108249
108627
|
async function writeStateJson(statePath, state) {
|
|
108250
|
-
await
|
|
108628
|
+
await mkdir13(dirname13(statePath), { recursive: true });
|
|
108251
108629
|
const tmp = `${statePath}.tmp-${process.pid}-${writeStateSeq++}`;
|
|
108252
108630
|
try {
|
|
108253
108631
|
await Bun.write(tmp, JSON.stringify(state, null, 2) + `
|
|
@@ -108286,7 +108664,7 @@ function isCommentNotFoundError(err) {
|
|
|
108286
108664
|
return text.includes("not found") || text.includes("could not find") || text.includes("entity not found");
|
|
108287
108665
|
}
|
|
108288
108666
|
async function readTasksMd(changeDir, log3) {
|
|
108289
|
-
const file2 = Bun.file(
|
|
108667
|
+
const file2 = Bun.file(join32(changeDir, "tasks.md"));
|
|
108290
108668
|
if (!await file2.exists()) {
|
|
108291
108669
|
log3(` comment-sync: tasks.md missing in ${changeDir}, skipping`, "gray");
|
|
108292
108670
|
return null;
|
|
@@ -108394,14 +108772,14 @@ async function postPlanCommentOnce(deps) {
|
|
|
108394
108772
|
const check2 = parsePlanningSection(tasksMd);
|
|
108395
108773
|
if (!check2.allChecked)
|
|
108396
108774
|
return null;
|
|
108397
|
-
const proposalPath =
|
|
108775
|
+
const proposalPath = join32(deps.changeDir, "proposal.md");
|
|
108398
108776
|
const why = await readSection(proposalPath, "Why");
|
|
108399
108777
|
const whatChanges = await readSection(proposalPath, "What Changes");
|
|
108400
108778
|
if (!why && !whatChanges) {
|
|
108401
108779
|
deps.log(` comment-sync: proposal.md has no Why/What Changes, skipping plan comment`, "gray");
|
|
108402
108780
|
return null;
|
|
108403
108781
|
}
|
|
108404
|
-
const designSummary = await readFirstParagraph(
|
|
108782
|
+
const designSummary = await readFirstParagraph(join32(deps.changeDir, "design.md"));
|
|
108405
108783
|
const parts = [`### ${PLAN_COMMENT_TITLE} \u2014 \`${deps.changeName}\``];
|
|
108406
108784
|
if (why) {
|
|
108407
108785
|
parts.push("", "**Why**", "", why);
|
|
@@ -260677,7 +261055,7 @@ var init_render_pdf = __esm(() => {
|
|
|
260677
261055
|
});
|
|
260678
261056
|
|
|
260679
261057
|
// apps/agent/src/agent/linear-sync/spec-attachments.ts
|
|
260680
|
-
import { dirname as dirname14, join as
|
|
261058
|
+
import { dirname as dirname14, join as join33 } from "path";
|
|
260681
261059
|
function describeLinearError(err) {
|
|
260682
261060
|
const e = err;
|
|
260683
261061
|
const parts = [e.message ?? String(err)];
|
|
@@ -260769,7 +261147,7 @@ async function syncSlot(deps, slot) {
|
|
|
260769
261147
|
const [primaryName, ...trailingNames] = spec.sourceFiles;
|
|
260770
261148
|
if (!primaryName)
|
|
260771
261149
|
return;
|
|
260772
|
-
const primary = Bun.file(
|
|
261150
|
+
const primary = Bun.file(join33(deps.changeDir, primaryName));
|
|
260773
261151
|
if (!await primary.exists()) {
|
|
260774
261152
|
deps.log(` spec-attachments: ${primaryName} missing, skipping`, "gray");
|
|
260775
261153
|
return;
|
|
@@ -260788,7 +261166,7 @@ async function syncSlot(deps, slot) {
|
|
|
260788
261166
|
const parts = [primaryBytes];
|
|
260789
261167
|
const enc = new TextEncoder;
|
|
260790
261168
|
for (const name of trailingNames) {
|
|
260791
|
-
const f2 = Bun.file(
|
|
261169
|
+
const f2 = Bun.file(join33(deps.changeDir, name));
|
|
260792
261170
|
if (!await f2.exists())
|
|
260793
261171
|
continue;
|
|
260794
261172
|
try {
|
|
@@ -261053,9 +261431,9 @@ var init_comment_sync2 = __esm(() => {
|
|
|
261053
261431
|
});
|
|
261054
261432
|
|
|
261055
261433
|
// apps/agent/src/features/pr-tracker/state.ts
|
|
261056
|
-
import { join as
|
|
261434
|
+
import { join as join34 } from "path";
|
|
261057
261435
|
async function readState2(projectRoot) {
|
|
261058
|
-
const path =
|
|
261436
|
+
const path = join34(projectRoot, PR_TRACKER_STATE_RELPATH);
|
|
261059
261437
|
const file2 = Bun.file(path);
|
|
261060
261438
|
if (!await file2.exists())
|
|
261061
261439
|
return {};
|
|
@@ -261071,7 +261449,7 @@ async function readState2(projectRoot) {
|
|
|
261071
261449
|
}
|
|
261072
261450
|
}
|
|
261073
261451
|
async function writeState2(projectRoot, state) {
|
|
261074
|
-
const path =
|
|
261452
|
+
const path = join34(projectRoot, PR_TRACKER_STATE_RELPATH);
|
|
261075
261453
|
await Bun.write(path, JSON.stringify(state, null, 2));
|
|
261076
261454
|
}
|
|
261077
261455
|
var PR_TRACKER_STATE_RELPATH = ".ralph/pr-tracker-state.json";
|
|
@@ -261150,7 +261528,7 @@ var init_pr_tracker = __esm(() => {
|
|
|
261150
261528
|
});
|
|
261151
261529
|
|
|
261152
261530
|
// apps/agent/src/agent/wire.ts
|
|
261153
|
-
import { join as
|
|
261531
|
+
import { join as join35 } from "path";
|
|
261154
261532
|
function buildAgentCoordinator(input) {
|
|
261155
261533
|
const {
|
|
261156
261534
|
args,
|
|
@@ -261169,7 +261547,7 @@ function buildAgentCoordinator(input) {
|
|
|
261169
261547
|
onWorkerCmd,
|
|
261170
261548
|
onAwaitingTicket
|
|
261171
261549
|
} = input;
|
|
261172
|
-
const logsDir =
|
|
261550
|
+
const logsDir = join35(projectRoot, ".ralph", "logs");
|
|
261173
261551
|
const bus = createBus();
|
|
261174
261552
|
subscribeAgentDiag(bus, onLog);
|
|
261175
261553
|
const diag = (area, message, color) => {
|
|
@@ -261179,7 +261557,8 @@ function buildAgentCoordinator(input) {
|
|
|
261179
261557
|
const pollInterval = args.pollInterval || cfg.pollIntervalSeconds;
|
|
261180
261558
|
const indicators = mergeIndicators(cfg.linear.indicators, args.indicators);
|
|
261181
261559
|
const team = args.linearTeam || cfg.linear.team;
|
|
261182
|
-
const assignee = args.linearAssignee || cfg.linear.
|
|
261560
|
+
const effectiveFilter = args.linearFilter || (args.linearAssignee ? `assignee = ${args.linearAssignee}` : "") || cfg.linear.filter;
|
|
261561
|
+
const { assignee, anyAssignee } = parseLinearFilter(effectiveFilter);
|
|
261183
261562
|
const ticketNumbers = resolveTicketNumbers(args.ticketTokens, team);
|
|
261184
261563
|
const excludeFromTodo = unionMarkers(indicators.setDone, indicators.setError);
|
|
261185
261564
|
const gitRunner = input.runners?.git ?? bunGitRunner;
|
|
@@ -261216,6 +261595,7 @@ function buildAgentCoordinator(input) {
|
|
|
261216
261595
|
apiKey,
|
|
261217
261596
|
team,
|
|
261218
261597
|
assignee,
|
|
261598
|
+
anyAssignee,
|
|
261219
261599
|
diag,
|
|
261220
261600
|
...ticketNumbers.length > 0 ? { ticketNumbers } : {}
|
|
261221
261601
|
});
|
|
@@ -261253,6 +261633,7 @@ function buildAgentCoordinator(input) {
|
|
|
261253
261633
|
cfg,
|
|
261254
261634
|
team,
|
|
261255
261635
|
assignee,
|
|
261636
|
+
anyAssignee,
|
|
261256
261637
|
indicators,
|
|
261257
261638
|
projectRoot,
|
|
261258
261639
|
useWorktree,
|
|
@@ -261382,6 +261763,18 @@ function buildAgentCoordinator(input) {
|
|
|
261382
261763
|
const json2 = await file2.json();
|
|
261383
261764
|
return json2.iteration ?? 0;
|
|
261384
261765
|
},
|
|
261766
|
+
getTasksFingerprint: async (changeName) => {
|
|
261767
|
+
const root = cwdByChange.get(changeName) ?? projectRoot;
|
|
261768
|
+
const changeDir = projectLayout(root).changeDir(changeName);
|
|
261769
|
+
const parts = [];
|
|
261770
|
+
for (const name of ["tasks.md", "proposal.md", "design.md"]) {
|
|
261771
|
+
const file2 = Bun.file(join35(changeDir, name));
|
|
261772
|
+
if (!await file2.exists())
|
|
261773
|
+
continue;
|
|
261774
|
+
parts.push(`${name}:${file2.lastModified}:${file2.size}`);
|
|
261775
|
+
}
|
|
261776
|
+
return parts.length > 0 ? parts.join("|") : null;
|
|
261777
|
+
},
|
|
261385
261778
|
...commentSync.enabled && commentSync.syncTasks ? { syncTasks: commentSync.syncTasks } : {},
|
|
261386
261779
|
...commentSync.enabled && commentSync.onSteeringAppended ? { onSteeringAppended: commentSync.onSteeringAppended } : {}
|
|
261387
261780
|
}, {
|
|
@@ -261396,7 +261789,7 @@ function buildAgentCoordinator(input) {
|
|
|
261396
261789
|
...prTracker ? { prTracker } : {}
|
|
261397
261790
|
});
|
|
261398
261791
|
coordRef.current = coord;
|
|
261399
|
-
const filterDesc = describeIndicators(indicators, team, assignee);
|
|
261792
|
+
const filterDesc = describeIndicators(indicators, team, assignee, anyAssignee);
|
|
261400
261793
|
const runBaselineGateOnce = createBaselineGateRunner({
|
|
261401
261794
|
args,
|
|
261402
261795
|
cfg,
|
|
@@ -261420,7 +261813,7 @@ function buildAgentCoordinator(input) {
|
|
|
261420
261813
|
getGaveUpTotal: async () => {
|
|
261421
261814
|
let total = 0;
|
|
261422
261815
|
for (const [changeName, root] of cwdByChange) {
|
|
261423
|
-
const file2 = Bun.file(
|
|
261816
|
+
const file2 = Bun.file(join35(projectLayout(root).taskStateDir(changeName), GAVEUP_COUNT_FILE));
|
|
261424
261817
|
if (!await file2.exists())
|
|
261425
261818
|
continue;
|
|
261426
261819
|
try {
|
|
@@ -261432,6 +261825,7 @@ function buildAgentCoordinator(input) {
|
|
|
261432
261825
|
};
|
|
261433
261826
|
}
|
|
261434
261827
|
var init_wire = __esm(() => {
|
|
261828
|
+
init_workflow();
|
|
261435
261829
|
init_src2();
|
|
261436
261830
|
init_coordinator2();
|
|
261437
261831
|
init_linear();
|
|
@@ -261454,14 +261848,14 @@ var init_wire = __esm(() => {
|
|
|
261454
261848
|
});
|
|
261455
261849
|
|
|
261456
261850
|
// apps/agent/src/agent/json-log/json-log-file.ts
|
|
261457
|
-
import { mkdir as
|
|
261851
|
+
import { mkdir as mkdir14, appendFile as appendFile2 } from "fs/promises";
|
|
261458
261852
|
import { dirname as dirname15 } from "path";
|
|
261459
261853
|
function createJsonLogFileSink(path) {
|
|
261460
261854
|
if (!path)
|
|
261461
261855
|
return { emit: () => {} };
|
|
261462
261856
|
let chain = (async () => {
|
|
261463
261857
|
try {
|
|
261464
|
-
await
|
|
261858
|
+
await mkdir14(dirname15(path), { recursive: true });
|
|
261465
261859
|
await Bun.write(path, "");
|
|
261466
261860
|
} catch {}
|
|
261467
261861
|
})();
|
|
@@ -261707,7 +262101,7 @@ var init_output_utils = __esm(() => {
|
|
|
261707
262101
|
});
|
|
261708
262102
|
|
|
261709
262103
|
// apps/agent/src/agent/state/worker-state-poll.ts
|
|
261710
|
-
import { join as
|
|
262104
|
+
import { join as join36 } from "path";
|
|
261711
262105
|
function parseSubtasks(tasksMd) {
|
|
261712
262106
|
const out = [];
|
|
261713
262107
|
let skipSection = false;
|
|
@@ -261740,7 +262134,7 @@ function initialWorkerSnapshot() {
|
|
|
261740
262134
|
async function readWorkerSnapshot(input) {
|
|
261741
262135
|
const next = { ...input.prev };
|
|
261742
262136
|
try {
|
|
261743
|
-
const file2 = Bun.file(
|
|
262137
|
+
const file2 = Bun.file(join36(input.statesDir, input.changeName, ".ralph-state.json"));
|
|
261744
262138
|
if (await file2.exists()) {
|
|
261745
262139
|
const json2 = await file2.json();
|
|
261746
262140
|
next.iter = json2.iteration ?? next.iter;
|
|
@@ -261749,10 +262143,10 @@ async function readWorkerSnapshot(input) {
|
|
|
261749
262143
|
} catch {}
|
|
261750
262144
|
if (input.changeDir) {
|
|
261751
262145
|
try {
|
|
261752
|
-
const tasksFile = Bun.file(
|
|
261753
|
-
const proposalFile = Bun.file(
|
|
261754
|
-
const designFile = Bun.file(
|
|
261755
|
-
const reviewFindingsFile = Bun.file(
|
|
262146
|
+
const tasksFile = Bun.file(join36(input.changeDir, "tasks.md"));
|
|
262147
|
+
const proposalFile = Bun.file(join36(input.changeDir, "proposal.md"));
|
|
262148
|
+
const designFile = Bun.file(join36(input.changeDir, "design.md"));
|
|
262149
|
+
const reviewFindingsFile = Bun.file(join36(input.changeDir, "review-findings.md"));
|
|
261756
262150
|
const [tasksText, proposalText, designText, reviewFindingsText] = await Promise.all([
|
|
261757
262151
|
tasksFile.exists().then((ok) => ok ? tasksFile.text() : null),
|
|
261758
262152
|
proposalFile.exists().then((ok) => ok ? proposalFile.text() : null),
|
|
@@ -261815,7 +262209,7 @@ var init_worker_state_poll = __esm(() => {
|
|
|
261815
262209
|
});
|
|
261816
262210
|
|
|
261817
262211
|
// apps/agent/src/components/AgentMode.tsx
|
|
261818
|
-
import { join as
|
|
262212
|
+
import { join as join37 } from "path";
|
|
261819
262213
|
async function appendSteeringImpl(changeDir, message) {
|
|
261820
262214
|
await runWithContext(createDefaultContext(), async () => {
|
|
261821
262215
|
appendSteeringMessage(changeDir, message);
|
|
@@ -262753,6 +263147,19 @@ function AgentMode({
|
|
|
262753
263147
|
/* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
|
|
262754
263148
|
color: pollStatus.lastPrStatus.ciFailed > 0 ? "red" : "white",
|
|
262755
263149
|
children: pollStatus.lastPrStatus.ciFailed
|
|
263150
|
+
}, undefined, false, undefined, this),
|
|
263151
|
+
/* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
|
|
263152
|
+
dimColor: true,
|
|
263153
|
+
children: "\xB7"
|
|
263154
|
+
}, undefined, false, undefined, this),
|
|
263155
|
+
/* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
|
|
263156
|
+
dimColor: true,
|
|
263157
|
+
children: "quarantined"
|
|
263158
|
+
}, undefined, false, undefined, this),
|
|
263159
|
+
/* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
|
|
263160
|
+
color: pollStatus.lastPrStatus.quarantined > 0 ? "magenta" : "white",
|
|
263161
|
+
bold: true,
|
|
263162
|
+
children: pollStatus.lastPrStatus.quarantined
|
|
262756
263163
|
}, undefined, false, undefined, this)
|
|
262757
263164
|
]
|
|
262758
263165
|
}, undefined, true, undefined, this)
|
|
@@ -263375,7 +263782,7 @@ function AgentMode({
|
|
|
263375
263782
|
},
|
|
263376
263783
|
onSubmit: async (message) => {
|
|
263377
263784
|
try {
|
|
263378
|
-
await appendSteering2(
|
|
263785
|
+
await appendSteering2(join37(tasksDir, w2.changeName), message);
|
|
263379
263786
|
fileEmit({ type: "steering_submitted", changeName: w2.changeName, message });
|
|
263380
263787
|
} catch (err) {
|
|
263381
263788
|
const text = err.message;
|
|
@@ -263489,7 +263896,7 @@ function shouldFallbackToJsonOutput(args, stdinIsTty) {
|
|
|
263489
263896
|
}
|
|
263490
263897
|
|
|
263491
263898
|
// apps/agent/src/runtime/tmux.ts
|
|
263492
|
-
import { basename as
|
|
263899
|
+
import { basename as basename4 } from "path";
|
|
263493
263900
|
function tmuxAvailable() {
|
|
263494
263901
|
const result2 = Bun.spawnSync({ cmd: ["tmux", "-V"], stderr: "pipe" });
|
|
263495
263902
|
return result2.exitCode === 0;
|
|
@@ -263498,7 +263905,7 @@ function sessionName(projectRoot) {
|
|
|
263498
263905
|
const override = process.env["RALPH_SESSION_NAME"];
|
|
263499
263906
|
if (override)
|
|
263500
263907
|
return override;
|
|
263501
|
-
return `ralphy-agent-${
|
|
263908
|
+
return `ralphy-agent-${basename4(projectRoot)}`;
|
|
263502
263909
|
}
|
|
263503
263910
|
function sessionExists(name) {
|
|
263504
263911
|
const result2 = Bun.spawnSync({ cmd: ["tmux", "has-session", "-t", name], stderr: "pipe" });
|
|
@@ -263679,7 +264086,7 @@ __export(exports_list, {
|
|
|
263679
264086
|
buildBuckets: () => buildBuckets,
|
|
263680
264087
|
backlogRankByIssueId: () => backlogRankByIssueId
|
|
263681
264088
|
});
|
|
263682
|
-
import { join as
|
|
264089
|
+
import { join as join38 } from "path";
|
|
263683
264090
|
function countTaskItems(content) {
|
|
263684
264091
|
const checked = (content.match(/^- \[x\]/gm) ?? []).length;
|
|
263685
264092
|
const unchecked = (content.match(/^- \[ \]/gm) ?? []).length;
|
|
@@ -263695,13 +264102,13 @@ function buildLocalRows() {
|
|
|
263695
264102
|
const sources = [{ dir: statesDir, label: "main" }];
|
|
263696
264103
|
const worktreesRoot = worktreesDir2(projectRoot);
|
|
263697
264104
|
for (const wt of storage.list(worktreesRoot)) {
|
|
263698
|
-
sources.push({ dir:
|
|
264105
|
+
sources.push({ dir: join38(worktreesRoot, wt, ".ralph", "tasks"), label: `wt:${wt}` });
|
|
263699
264106
|
}
|
|
263700
264107
|
for (const { dir, label } of sources) {
|
|
263701
264108
|
for (const entry of storage.list(dir)) {
|
|
263702
264109
|
if (seen.has(entry))
|
|
263703
264110
|
continue;
|
|
263704
|
-
const raw = storage.read(
|
|
264111
|
+
const raw = storage.read(join38(dir, entry, ".ralph-state.json"));
|
|
263705
264112
|
if (raw === null)
|
|
263706
264113
|
continue;
|
|
263707
264114
|
let state;
|
|
@@ -263716,7 +264123,7 @@ function buildLocalRows() {
|
|
|
263716
264123
|
const firstLine = promptRaw.split(`
|
|
263717
264124
|
`).find((l3) => l3.trim() !== "") ?? "";
|
|
263718
264125
|
let progress = "\u2014";
|
|
263719
|
-
const tasksContent = storage.read(
|
|
264126
|
+
const tasksContent = storage.read(join38(dir, entry, "tasks.md"));
|
|
263720
264127
|
if (tasksContent !== null) {
|
|
263721
264128
|
const { checked, unchecked } = countTaskItems(tasksContent);
|
|
263722
264129
|
const total = checked + unchecked;
|
|
@@ -263779,18 +264186,23 @@ function buildBuckets(indicators) {
|
|
|
263779
264186
|
{ label: "auto-merge", indicator: indicators.getAutoMerge, exclude: [] }
|
|
263780
264187
|
];
|
|
263781
264188
|
}
|
|
263782
|
-
async function fetchBucketIssues(apiKey, bucket, team, assignee, ticketNumbers) {
|
|
264189
|
+
async function fetchBucketIssues(apiKey, bucket, team, assignee, anyAssignee, ticketNumbers) {
|
|
263783
264190
|
if (!bucket.indicator || bucket.indicator.filter.length === 0)
|
|
263784
264191
|
return [];
|
|
263785
264192
|
const spec = {
|
|
263786
264193
|
team,
|
|
263787
264194
|
assignee,
|
|
264195
|
+
anyAssignee,
|
|
263788
264196
|
include: bucket.indicator.filter,
|
|
263789
264197
|
exclude: bucket.exclude,
|
|
263790
264198
|
...ticketNumbers.length > 0 ? { numbers: ticketNumbers } : {}
|
|
263791
264199
|
};
|
|
263792
264200
|
return fetchOpenIssues(apiKey, spec);
|
|
263793
264201
|
}
|
|
264202
|
+
function resolveLinearFilter(filterOverride, assigneeOverride, configFilter) {
|
|
264203
|
+
const effective = filterOverride || (assigneeOverride ? `assignee = ${assigneeOverride}` : "") || configFilter;
|
|
264204
|
+
return parseLinearFilter(effective);
|
|
264205
|
+
}
|
|
263794
264206
|
function formatReviewCell(prUrl, count) {
|
|
263795
264207
|
if (!prUrl)
|
|
263796
264208
|
return "-";
|
|
@@ -263837,13 +264249,13 @@ function backlogRankByIssueId(issues) {
|
|
|
263837
264249
|
ordered.forEach((o, i) => rankById.set(o.id, i));
|
|
263838
264250
|
return rankById;
|
|
263839
264251
|
}
|
|
263840
|
-
async function fetchAndPrintLinear(apiKey, buckets, team, assignee, cwd2, runner, ignoreCiChecks = [], checks3 = false, review = false, ticketNumbers = []) {
|
|
264252
|
+
async function fetchAndPrintLinear(apiKey, buckets, team, assignee, anyAssignee, cwd2, runner, ignoreCiChecks = [], checks3 = false, review = false, ticketNumbers = []) {
|
|
263841
264253
|
const bucketResults = await Promise.all(buckets.map(async (bucket) => {
|
|
263842
264254
|
if (!bucket.indicator || bucket.indicator.filter.length === 0) {
|
|
263843
264255
|
return { bucket, issues: [], error: null };
|
|
263844
264256
|
}
|
|
263845
264257
|
try {
|
|
263846
|
-
const issues = await fetchBucketIssues(apiKey, bucket, team, assignee, ticketNumbers);
|
|
264258
|
+
const issues = await fetchBucketIssues(apiKey, bucket, team, assignee, anyAssignee, ticketNumbers);
|
|
263847
264259
|
return { bucket, issues, error: null };
|
|
263848
264260
|
} catch (err) {
|
|
263849
264261
|
return {
|
|
@@ -263969,6 +264381,7 @@ async function runList(input) {
|
|
|
263969
264381
|
identifier: name,
|
|
263970
264382
|
projectRoot,
|
|
263971
264383
|
linearTeamOverride: input.linearTeamOverride,
|
|
264384
|
+
linearFilterOverride: input.linearFilterOverride,
|
|
263972
264385
|
linearAssigneeOverride: input.linearAssigneeOverride
|
|
263973
264386
|
});
|
|
263974
264387
|
return;
|
|
@@ -263979,7 +264392,7 @@ async function runList(input) {
|
|
|
263979
264392
|
const apiKey = process.env["LINEAR_API_KEY"];
|
|
263980
264393
|
const indicators = cfg.linear.indicators;
|
|
263981
264394
|
const team = input.linearTeamOverride || cfg.linear.team;
|
|
263982
|
-
const assignee = input.linearAssigneeOverride
|
|
264395
|
+
const { assignee, anyAssignee } = resolveLinearFilter(input.linearFilterOverride, input.linearAssigneeOverride, cfg.linear.filter);
|
|
263983
264396
|
const buckets = buildBuckets(indicators);
|
|
263984
264397
|
const anyConfigured = buckets.some((b2) => b2.indicator && b2.indicator.filter.length > 0);
|
|
263985
264398
|
if (!anyConfigured) {
|
|
@@ -264014,13 +264427,12 @@ Linear: LINEAR_API_KEY not set \u2014 cannot fetch tickets. Configured buckets:
|
|
|
264014
264427
|
process.stdout.write(`
|
|
264015
264428
|
team: ${team}
|
|
264016
264429
|
`);
|
|
264017
|
-
|
|
264018
|
-
process.stdout.write(`assignee: ${assignee}
|
|
264430
|
+
process.stdout.write(`assignee: ${anyAssignee ? "any" : assignee ?? "*"}
|
|
264019
264431
|
`);
|
|
264020
264432
|
if (ticketNumbers.length > 0)
|
|
264021
264433
|
process.stdout.write(`ticket: ${ticketNumbers.join(", ")}
|
|
264022
264434
|
`);
|
|
264023
|
-
await fetchAndPrintLinear(apiKey, buckets, team, assignee, projectRoot, localCmdRunner, cfg.ignoreCiChecks, input.checks, input.review, ticketNumbers);
|
|
264435
|
+
await fetchAndPrintLinear(apiKey, buckets, team, assignee, anyAssignee, projectRoot, localCmdRunner, cfg.ignoreCiChecks, input.checks, input.review, ticketNumbers);
|
|
264024
264436
|
}
|
|
264025
264437
|
function normalizeIdentifier(input) {
|
|
264026
264438
|
let parsed;
|
|
@@ -264077,8 +264489,10 @@ function markerMatches(issue2, marker) {
|
|
|
264077
264489
|
}
|
|
264078
264490
|
return false;
|
|
264079
264491
|
}
|
|
264080
|
-
function assigneeMatches(issue2, assignee) {
|
|
264081
|
-
if (
|
|
264492
|
+
function assigneeMatches(issue2, assignee, anyAssignee) {
|
|
264493
|
+
if (anyAssignee)
|
|
264494
|
+
return true;
|
|
264495
|
+
if (!assignee || assignee === "unassigned")
|
|
264082
264496
|
return issue2.assignee === null;
|
|
264083
264497
|
const a = issue2.assignee;
|
|
264084
264498
|
if (!a)
|
|
@@ -264101,7 +264515,8 @@ async function runListDebug(input) {
|
|
|
264101
264515
|
const cfg = await loadRalphyConfig(projectRoot);
|
|
264102
264516
|
const indicators = cfg.linear.indicators;
|
|
264103
264517
|
const team = input.linearTeamOverride || cfg.linear.team;
|
|
264104
|
-
const assignee = input.linearAssigneeOverride
|
|
264518
|
+
const { assignee, anyAssignee } = resolveLinearFilter(input.linearFilterOverride, input.linearAssigneeOverride, cfg.linear.filter);
|
|
264519
|
+
const assigneeLabel = anyAssignee ? "any" : assignee ?? "*";
|
|
264105
264520
|
const normalized = normalizeIdentifier(identifier);
|
|
264106
264521
|
if (!normalized) {
|
|
264107
264522
|
process.stdout.write(`Error: '${identifier}' does not look like a Linear identifier (expected e.g. DOO-6, or a local change name beginning with one).
|
|
@@ -264141,8 +264556,8 @@ Per-bucket diagnostics:
|
|
|
264141
264556
|
if (team && issue2.team?.key && issue2.team.key !== team) {
|
|
264142
264557
|
reasons.push(`team mismatch: issue=${issue2.team.key}, config=${team}`);
|
|
264143
264558
|
}
|
|
264144
|
-
if (!assigneeMatches(issue2, assignee)) {
|
|
264145
|
-
reasons.push(`assignee mismatch: issue=${issue2.assignee ? issue2.assignee.email ?? issue2.assignee.id : "unassigned"}, config=${
|
|
264559
|
+
if (!assigneeMatches(issue2, assignee, anyAssignee)) {
|
|
264560
|
+
reasons.push(`assignee mismatch: issue=${issue2.assignee ? issue2.assignee.email ?? issue2.assignee.id : "unassigned"}, config=${assigneeLabel}`);
|
|
264146
264561
|
}
|
|
264147
264562
|
const includeMatches = bucket.indicator.filter.some((m2) => markerMatches(issue2, m2));
|
|
264148
264563
|
if (!includeMatches) {
|
|
@@ -264175,6 +264590,7 @@ Per-bucket diagnostics:
|
|
|
264175
264590
|
var localCmdRunner;
|
|
264176
264591
|
var init_list = __esm(() => {
|
|
264177
264592
|
init_context();
|
|
264593
|
+
init_workflow();
|
|
264178
264594
|
init_worktree();
|
|
264179
264595
|
init_config();
|
|
264180
264596
|
init_linear();
|
|
@@ -264202,31 +264618,14 @@ var init_list = __esm(() => {
|
|
|
264202
264618
|
};
|
|
264203
264619
|
});
|
|
264204
264620
|
|
|
264205
|
-
// apps/agent/src/agent/state/agent-run-state.ts
|
|
264206
|
-
import { basename as basename4, join as join37 } from "path";
|
|
264207
|
-
import { homedir as homedir6 } from "os";
|
|
264208
|
-
import { mkdir as mkdir13, writeFile } from "fs/promises";
|
|
264209
|
-
function agentRunStatePath(projectRoot) {
|
|
264210
|
-
return join37(homedir6(), ".ralph", basename4(projectRoot), "agent-state.json");
|
|
264211
|
-
}
|
|
264212
|
-
async function writeAgentRunState(state) {
|
|
264213
|
-
const path = agentRunStatePath(state.projectRoot);
|
|
264214
|
-
try {
|
|
264215
|
-
await mkdir13(join37(homedir6(), ".ralph", basename4(state.projectRoot)), { recursive: true });
|
|
264216
|
-
await writeFile(path, JSON.stringify(state, null, 2) + `
|
|
264217
|
-
`, "utf-8");
|
|
264218
|
-
} catch {}
|
|
264219
|
-
}
|
|
264220
|
-
var init_agent_run_state = () => {};
|
|
264221
|
-
|
|
264222
264621
|
// apps/agent/src/agent/json-runner.ts
|
|
264223
264622
|
var exports_json_runner = {};
|
|
264224
264623
|
__export(exports_json_runner, {
|
|
264225
264624
|
runAgentJson: () => runAgentJson
|
|
264226
264625
|
});
|
|
264227
|
-
import { join as
|
|
264228
|
-
import { mkdir as
|
|
264229
|
-
import { homedir as
|
|
264626
|
+
import { join as join39 } from "path";
|
|
264627
|
+
import { mkdir as mkdir15 } from "fs/promises";
|
|
264628
|
+
import { homedir as homedir8 } from "os";
|
|
264230
264629
|
function makeEmit(fileSink) {
|
|
264231
264630
|
return (event) => {
|
|
264232
264631
|
const payload = { ts: Date.now(), ...event };
|
|
@@ -264246,7 +264645,7 @@ async function runAgentJson({
|
|
|
264246
264645
|
tasksDir,
|
|
264247
264646
|
runPreflight: runPreflight2 = runPreflight
|
|
264248
264647
|
}) {
|
|
264249
|
-
await
|
|
264648
|
+
await mkdir15(join39(homedir8(), ".ralph"), { recursive: true }).catch(() => {
|
|
264250
264649
|
return;
|
|
264251
264650
|
});
|
|
264252
264651
|
const fileSink = createJsonLogFileSink(args.jsonLogFile);
|
|
@@ -264462,8 +264861,8 @@ var exports_src3 = {};
|
|
|
264462
264861
|
__export(exports_src3, {
|
|
264463
264862
|
main: () => main3
|
|
264464
264863
|
});
|
|
264465
|
-
import { mkdir as
|
|
264466
|
-
import { join as
|
|
264864
|
+
import { mkdir as mkdir16 } from "fs/promises";
|
|
264865
|
+
import { join as join40 } from "path";
|
|
264467
264866
|
async function main3(argv) {
|
|
264468
264867
|
if (argv.includes("--help") || argv.includes("-h")) {
|
|
264469
264868
|
printAgentHelp();
|
|
@@ -264488,6 +264887,7 @@ async function main3(argv) {
|
|
|
264488
264887
|
await runWithContext(createDefaultContext({ layout, args }), async () => {
|
|
264489
264888
|
await runList2({
|
|
264490
264889
|
linearTeamOverride: args.linearTeam,
|
|
264890
|
+
linearFilterOverride: args.linearFilter,
|
|
264491
264891
|
linearAssigneeOverride: args.linearAssignee,
|
|
264492
264892
|
debug: args.debug,
|
|
264493
264893
|
name: args.name,
|
|
@@ -264526,9 +264926,9 @@ async function main3(argv) {
|
|
|
264526
264926
|
return 1;
|
|
264527
264927
|
}
|
|
264528
264928
|
}
|
|
264529
|
-
await
|
|
264530
|
-
await
|
|
264531
|
-
await
|
|
264929
|
+
await mkdir16(statesDir, { recursive: true });
|
|
264930
|
+
await mkdir16(tasksDir, { recursive: true });
|
|
264931
|
+
await mkdir16(join40(projectRoot, ".ralph"), { recursive: true });
|
|
264532
264932
|
if (shouldFallbackToJsonOutput(args, process.stdin.isTTY)) {
|
|
264533
264933
|
process.stderr.write(`agent: stdin is not a TTY \u2014 falling back to --json-output mode.
|
|
264534
264934
|
`);
|