@neriros/ralphy 3.10.3 → 3.10.4
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 +772 -76
- 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.4")
|
|
18932
|
+
return "3.10.4";
|
|
18933
18933
|
} catch {}
|
|
18934
18934
|
const dirsToTry = [];
|
|
18935
18935
|
try {
|
|
@@ -80505,7 +80505,7 @@ var init_zod = __esm(() => {
|
|
|
80505
80505
|
});
|
|
80506
80506
|
|
|
80507
80507
|
// packages/workflow/src/schema.ts
|
|
80508
|
-
var CURRENT_WORKFLOW_VERSION =
|
|
80508
|
+
var CURRENT_WORKFLOW_VERSION = 2, MarkerSchema, SET_INDICATOR_KEYS, GetIndicatorSchema, SetIndicatorSchema, IndicatorsSchema, ProjectSchema, CommandsSchema, DEFAULT_META_ONLY_FILES, BoundariesSchema, WorkflowConfigSchema;
|
|
80509
80509
|
var init_schema = __esm(() => {
|
|
80510
80510
|
init_zod();
|
|
80511
80511
|
MarkerSchema = exports_external.discriminatedUnion("type", [
|
|
@@ -80598,6 +80598,12 @@ var init_schema = __esm(() => {
|
|
|
80598
80598
|
WorkflowConfigSchema = exports_external.object({
|
|
80599
80599
|
version: exports_external.number().int().nonnegative().default(0),
|
|
80600
80600
|
project: ProjectSchema,
|
|
80601
|
+
repo: exports_external.object({
|
|
80602
|
+
remote: exports_external.string().optional(),
|
|
80603
|
+
host: exports_external.string().optional(),
|
|
80604
|
+
owner: exports_external.string().optional(),
|
|
80605
|
+
name: exports_external.string().optional()
|
|
80606
|
+
}).strict().optional(),
|
|
80601
80607
|
commands: CommandsSchema,
|
|
80602
80608
|
rules: exports_external.array(exports_external.string()).default([]),
|
|
80603
80609
|
boundaries: BoundariesSchema,
|
|
@@ -80712,8 +80718,9 @@ var init_schema = __esm(() => {
|
|
|
80712
80718
|
advanceMergedToDone: false
|
|
80713
80719
|
}),
|
|
80714
80720
|
metaPrompt: exports_external.object({
|
|
80715
|
-
enabled: exports_external.boolean().default(true)
|
|
80716
|
-
|
|
80721
|
+
enabled: exports_external.boolean().default(true),
|
|
80722
|
+
effort: exports_external.enum(["auto", "light", "standard", "heavy"]).default("auto")
|
|
80723
|
+
}).strict().default({ enabled: true, effort: "auto" }),
|
|
80717
80724
|
openspec: exports_external.object({
|
|
80718
80725
|
reviewPhase: exports_external.object({
|
|
80719
80726
|
enabled: exports_external.boolean().default(false),
|
|
@@ -81049,7 +81056,7 @@ function modelOptionValues() {
|
|
|
81049
81056
|
const field = findField("model");
|
|
81050
81057
|
return field && field.spec.kind === "select" ? field.spec.options.map((o) => o.value) : [];
|
|
81051
81058
|
}
|
|
81052
|
-
var PROMPT_BODY_FIELD_ID = "promptBody", yes = () => ({ kind: "confirm", defaultChoice: "confirm" }), no = () => ({ kind: "confirm", defaultChoice: "cancel" }), PROJECT_NAME, LINEAR_TEAM, LINEAR_ASSIGNEE, QUICK_FIELDS, isOn = (id) => (answers) => answers[id] === true, CUSTOMIZED_FIELDS, COMMON_CLI_OPTIONS, FIELD_DESCRIPTIONS;
|
|
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, LINEAR_ASSIGNEE, QUICK_FIELDS, isOn = (id) => (answers) => answers[id] === true, CUSTOMIZED_FIELDS, COMMON_CLI_OPTIONS, FIELD_DESCRIPTIONS;
|
|
81053
81060
|
var init_fields = __esm(() => {
|
|
81054
81061
|
PROJECT_NAME = {
|
|
81055
81062
|
id: "project.name",
|
|
@@ -81061,10 +81068,17 @@ var init_fields = __esm(() => {
|
|
|
81061
81068
|
id: "linear.team",
|
|
81062
81069
|
label: "Linear team key",
|
|
81063
81070
|
hint: "e.g. ENG \u2014 leave blank to match all teams",
|
|
81064
|
-
description: "
|
|
81071
|
+
description: "The Linear team this repository is linked to, given by its key (e.g. ENG). Ralphy only picks up issues from this team. Leave blank to watch every team.",
|
|
81065
81072
|
emptyLabel: "all teams",
|
|
81066
81073
|
spec: { kind: "text" }
|
|
81067
81074
|
};
|
|
81075
|
+
REPO_LINK = {
|
|
81076
|
+
id: "repo.link",
|
|
81077
|
+
label: "Link this repository to the team?",
|
|
81078
|
+
description: "Record the detected git repository in WORKFLOW.md and link it to the Linear team above. Confirm to adopt the detected repo; decline to leave it out.",
|
|
81079
|
+
spec: yes(),
|
|
81080
|
+
when: (answers) => typeof answers["repo.name"] === "string" && answers["repo.name"] !== ""
|
|
81081
|
+
};
|
|
81068
81082
|
LINEAR_ASSIGNEE = {
|
|
81069
81083
|
id: "linear.assignee",
|
|
81070
81084
|
label: "Linear assignee",
|
|
@@ -81073,7 +81087,7 @@ var init_fields = __esm(() => {
|
|
|
81073
81087
|
emptyLabel: "unassigned",
|
|
81074
81088
|
spec: { kind: "text" }
|
|
81075
81089
|
};
|
|
81076
|
-
QUICK_FIELDS = [PROJECT_NAME, LINEAR_TEAM, LINEAR_ASSIGNEE];
|
|
81090
|
+
QUICK_FIELDS = [PROJECT_NAME, LINEAR_TEAM, REPO_LINK, LINEAR_ASSIGNEE];
|
|
81077
81091
|
CUSTOMIZED_FIELDS = [
|
|
81078
81092
|
PROJECT_NAME,
|
|
81079
81093
|
{
|
|
@@ -81321,6 +81335,7 @@ var init_fields = __esm(() => {
|
|
|
81321
81335
|
spec: { kind: "list", placeholder: "dist/**" }
|
|
81322
81336
|
},
|
|
81323
81337
|
LINEAR_TEAM,
|
|
81338
|
+
REPO_LINK,
|
|
81324
81339
|
LINEAR_ASSIGNEE,
|
|
81325
81340
|
{
|
|
81326
81341
|
id: "linear.postComments",
|
|
@@ -81464,6 +81479,21 @@ var init_fields = __esm(() => {
|
|
|
81464
81479
|
description: "Add Ralphy's task-level 'meta-prompt' layer (extra framing instructions) to each phase. Leave on unless you want raw prompts.",
|
|
81465
81480
|
spec: yes()
|
|
81466
81481
|
},
|
|
81482
|
+
{
|
|
81483
|
+
id: "metaPrompt.effort",
|
|
81484
|
+
label: "Per-ticket effort tier",
|
|
81485
|
+
description: "How much effort the meta-prompt nudges the agent toward per ticket. 'auto' detects it from the ticket; 'light'/'standard'/'heavy' pin every ticket to that tier.",
|
|
81486
|
+
spec: {
|
|
81487
|
+
kind: "select",
|
|
81488
|
+
options: [
|
|
81489
|
+
{ label: "auto", value: "auto" },
|
|
81490
|
+
{ label: "light", value: "light" },
|
|
81491
|
+
{ label: "standard", value: "standard" },
|
|
81492
|
+
{ label: "heavy", value: "heavy" }
|
|
81493
|
+
]
|
|
81494
|
+
},
|
|
81495
|
+
when: isOn("metaPrompt.enabled")
|
|
81496
|
+
},
|
|
81467
81497
|
{
|
|
81468
81498
|
id: "openspec.reviewPhase.enabled",
|
|
81469
81499
|
label: "Enable the OpenSpec review phase?",
|
|
@@ -81869,6 +81899,51 @@ var init_workflow = __esm(() => {
|
|
|
81869
81899
|
import_yaml2 = __toESM(require_dist(), 1);
|
|
81870
81900
|
});
|
|
81871
81901
|
|
|
81902
|
+
// packages/core/src/repo/index.ts
|
|
81903
|
+
function parseRepoIdentity(remoteUrl) {
|
|
81904
|
+
const remote = remoteUrl.trim();
|
|
81905
|
+
if (!remote)
|
|
81906
|
+
return null;
|
|
81907
|
+
const match = remote.includes("://") ? URL_RE.exec(remote) : SCP_RE.exec(remote);
|
|
81908
|
+
if (!match)
|
|
81909
|
+
return null;
|
|
81910
|
+
const host = match[1];
|
|
81911
|
+
let path = match[2];
|
|
81912
|
+
if (!host || !path)
|
|
81913
|
+
return null;
|
|
81914
|
+
path = path.replace(/\/+$/, "").replace(/\.git$/i, "").replace(/\/+$/, "");
|
|
81915
|
+
const segments = path.split("/").filter((segment) => segment.length > 0);
|
|
81916
|
+
if (segments.length < 2)
|
|
81917
|
+
return null;
|
|
81918
|
+
const name = segments[segments.length - 1];
|
|
81919
|
+
const owner = segments.slice(0, -1).join("/");
|
|
81920
|
+
if (!owner || !name)
|
|
81921
|
+
return null;
|
|
81922
|
+
return { remote, host, owner, name };
|
|
81923
|
+
}
|
|
81924
|
+
async function detectRepoIdentity(cwd2) {
|
|
81925
|
+
try {
|
|
81926
|
+
const proc = Bun.spawn({
|
|
81927
|
+
cmd: ["git", "remote", "get-url", "origin"],
|
|
81928
|
+
...cwd2 ? { cwd: cwd2 } : {},
|
|
81929
|
+
stdout: "pipe",
|
|
81930
|
+
stderr: "ignore",
|
|
81931
|
+
timeout: GIT_DETECT_TIMEOUT_MS
|
|
81932
|
+
});
|
|
81933
|
+
const [stdout, exitCode] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
|
|
81934
|
+
if (exitCode !== 0)
|
|
81935
|
+
return null;
|
|
81936
|
+
return parseRepoIdentity(stdout.trim());
|
|
81937
|
+
} catch {
|
|
81938
|
+
return null;
|
|
81939
|
+
}
|
|
81940
|
+
}
|
|
81941
|
+
var GIT_DETECT_TIMEOUT_MS = 5000, SCP_RE, URL_RE;
|
|
81942
|
+
var init_repo = __esm(() => {
|
|
81943
|
+
SCP_RE = /^(?:[^@/]+@)?([^/:]+):(.+)$/;
|
|
81944
|
+
URL_RE = /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\/(?:[^@/]+@)?([^/:]+)(?::\d+)?\/(.+)$/;
|
|
81945
|
+
});
|
|
81946
|
+
|
|
81872
81947
|
// node_modules/.bun/react@18.3.1/node_modules/react/cjs/react-jsx-dev-runtime.development.js
|
|
81873
81948
|
var require_react_jsx_dev_runtime_development = __commonJS((exports) => {
|
|
81874
81949
|
var React10 = __toESM(require_react());
|
|
@@ -82787,6 +82862,12 @@ function buildFromAnswers(mode, answers, build = buildWorkflowMarkdown) {
|
|
|
82787
82862
|
values2["linear.indicators"] = map3;
|
|
82788
82863
|
}
|
|
82789
82864
|
}
|
|
82865
|
+
const linkRepo = values2[REPO_LINK_FIELD_ID] === true;
|
|
82866
|
+
delete values2[REPO_LINK_FIELD_ID];
|
|
82867
|
+
if (!linkRepo) {
|
|
82868
|
+
for (const id of REPO_ANSWER_IDS)
|
|
82869
|
+
delete values2[id];
|
|
82870
|
+
}
|
|
82790
82871
|
let bodyOverride;
|
|
82791
82872
|
if (PROMPT_BODY_FIELD_ID in values2) {
|
|
82792
82873
|
const body = values2[PROMPT_BODY_FIELD_ID];
|
|
@@ -82868,7 +82949,8 @@ function SetupWizard({
|
|
|
82868
82949
|
initialValues,
|
|
82869
82950
|
buildMarkdown,
|
|
82870
82951
|
onlyFields,
|
|
82871
|
-
initialBody
|
|
82952
|
+
initialBody,
|
|
82953
|
+
detectedRepo
|
|
82872
82954
|
}) {
|
|
82873
82955
|
const { exit } = use_app_default();
|
|
82874
82956
|
const startValues = initialValues ?? {};
|
|
@@ -82944,7 +83026,15 @@ function SetupWizard({
|
|
|
82944
83026
|
setIndex(target);
|
|
82945
83027
|
initEditing(fieldsFor(mode, source)[target], source);
|
|
82946
83028
|
};
|
|
82947
|
-
const valuesToWrite = (source) =>
|
|
83029
|
+
const valuesToWrite = (source) => {
|
|
83030
|
+
if (!onlyFields)
|
|
83031
|
+
return source;
|
|
83032
|
+
const allowed = new Set(onlyFields);
|
|
83033
|
+
if (allowed.has(REPO_LINK_FIELD_ID))
|
|
83034
|
+
for (const id of REPO_ANSWER_IDS)
|
|
83035
|
+
allowed.add(id);
|
|
83036
|
+
return Object.fromEntries(Object.entries(source).filter(([id]) => allowed.has(id)));
|
|
83037
|
+
};
|
|
82948
83038
|
const advance = (source) => {
|
|
82949
83039
|
const nextFields = fieldsFor(mode, source);
|
|
82950
83040
|
if (index >= nextFields.length - 1) {
|
|
@@ -83152,6 +83242,22 @@ ${draft.slice(at2)}`, at2 + 1);
|
|
|
83152
83242
|
marginTop: 1,
|
|
83153
83243
|
flexDirection: "column",
|
|
83154
83244
|
children: [
|
|
83245
|
+
field.id === REPO_LINK_FIELD_ID && detectedRepo ? /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
83246
|
+
children: [
|
|
83247
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
83248
|
+
dimColor: true,
|
|
83249
|
+
children: "Detected repo: "
|
|
83250
|
+
}, undefined, false, undefined, this),
|
|
83251
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
83252
|
+
color: "cyan",
|
|
83253
|
+
children: [
|
|
83254
|
+
detectedRepo.owner,
|
|
83255
|
+
"/",
|
|
83256
|
+
detectedRepo.name
|
|
83257
|
+
]
|
|
83258
|
+
}, undefined, true, undefined, this)
|
|
83259
|
+
]
|
|
83260
|
+
}, undefined, true, undefined, this) : null,
|
|
83155
83261
|
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
83156
83262
|
children: [
|
|
83157
83263
|
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
@@ -83724,13 +83830,14 @@ function IndicatorBuilder({
|
|
|
83724
83830
|
]
|
|
83725
83831
|
}, undefined, true, undefined, this);
|
|
83726
83832
|
}
|
|
83727
|
-
var import_react22, jsx_dev_runtime, MODE_OPTIONS, INDICATOR_OPTIONS, CONFIRM_OPTIONS, EDIT_EXIT_OPTIONS, RECREATE_EXIT_OPTIONS, MIGRATE_OPTIONS, CORE_STATES, CONFIRMATION_STATES, ALL_TYPES;
|
|
83833
|
+
var import_react22, jsx_dev_runtime, REPO_ANSWER_IDS, MODE_OPTIONS, INDICATOR_OPTIONS, CONFIRM_OPTIONS, EDIT_EXIT_OPTIONS, RECREATE_EXIT_OPTIONS, MIGRATE_OPTIONS, CORE_STATES, CONFIRMATION_STATES, ALL_TYPES;
|
|
83728
83834
|
var init_SetupWizard = __esm(async () => {
|
|
83729
83835
|
init_wizard();
|
|
83730
83836
|
init_fields();
|
|
83731
83837
|
await init_build2();
|
|
83732
83838
|
import_react22 = __toESM(require_react(), 1);
|
|
83733
83839
|
jsx_dev_runtime = __toESM(require_jsx_dev_runtime(), 1);
|
|
83840
|
+
REPO_ANSWER_IDS = ["repo.remote", "repo.host", "repo.owner", "repo.name"];
|
|
83734
83841
|
MODE_OPTIONS = [
|
|
83735
83842
|
{ label: "Quick \u2014 sensible defaults, only a few questions", value: "quick" },
|
|
83736
83843
|
{ label: "Permissive \u2014 defaults + auto-PR / auto-merge / CI auto-fix", value: "permissive" },
|
|
@@ -83856,6 +83963,11 @@ var init_migrations = __esm(() => {
|
|
|
83856
83963
|
"openspec.reviewPhase.reviewerModel",
|
|
83857
83964
|
"openspec.reviewPhase.reviewerContextStrategy"
|
|
83858
83965
|
]
|
|
83966
|
+
},
|
|
83967
|
+
{
|
|
83968
|
+
version: 2,
|
|
83969
|
+
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
|
+
fields: ["repo.link"]
|
|
83859
83971
|
}
|
|
83860
83972
|
];
|
|
83861
83973
|
LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max2, migration) => Math.max(max2, migration.version), 0);
|
|
@@ -83868,6 +83980,18 @@ __export(exports_src, {
|
|
|
83868
83980
|
maybeRunSetupWizard: () => maybeRunSetupWizard,
|
|
83869
83981
|
main: () => main
|
|
83870
83982
|
});
|
|
83983
|
+
function withDetectedRepo(initial2, repo) {
|
|
83984
|
+
if (!repo)
|
|
83985
|
+
return initial2;
|
|
83986
|
+
const values2 = { ...initial2 };
|
|
83987
|
+
values2["repo.remote"] = repo.remote;
|
|
83988
|
+
values2["repo.host"] = repo.host;
|
|
83989
|
+
values2["repo.owner"] = repo.owner;
|
|
83990
|
+
values2["repo.name"] = repo.name;
|
|
83991
|
+
if (!values2["project.name"])
|
|
83992
|
+
values2["project.name"] = repo.name;
|
|
83993
|
+
return values2;
|
|
83994
|
+
}
|
|
83871
83995
|
function clearScreen2() {
|
|
83872
83996
|
if (process.stdout.isTTY)
|
|
83873
83997
|
process.stdout.write("\x1B[2J\x1B[3J\x1B[H");
|
|
@@ -83876,6 +84000,7 @@ async function runSetupWizard(projectRoot, options = {}) {
|
|
|
83876
84000
|
let markdown = null;
|
|
83877
84001
|
const buildMarkdown = options.existing ? (answers, bodyOverride) => applyAnswersToWorkflow(options.existing, answers, bodyOverride) : undefined;
|
|
83878
84002
|
const initialBody = workflowBody(options.existing ?? DEFAULT_WORKFLOW_MD);
|
|
84003
|
+
const initialValues = withDetectedRepo(options.initialValues, options.detectedRepo);
|
|
83879
84004
|
clearScreen2();
|
|
83880
84005
|
const { waitUntilExit } = render_default(import_react23.createElement(SetupWizard, {
|
|
83881
84006
|
onComplete: (md) => {
|
|
@@ -83886,8 +84011,9 @@ async function runSetupWizard(projectRoot, options = {}) {
|
|
|
83886
84011
|
},
|
|
83887
84012
|
initialBody,
|
|
83888
84013
|
...options.initialMode ? { initialMode: options.initialMode } : {},
|
|
83889
|
-
...
|
|
84014
|
+
...initialValues ? { initialValues } : {},
|
|
83890
84015
|
...options.onlyFields ? { onlyFields: options.onlyFields } : {},
|
|
84016
|
+
...options.detectedRepo ? { detectedRepo: { owner: options.detectedRepo.owner, name: options.detectedRepo.name } } : {},
|
|
83891
84017
|
...buildMarkdown ? { buildMarkdown } : {}
|
|
83892
84018
|
}));
|
|
83893
84019
|
await waitUntilExit();
|
|
@@ -83971,10 +84097,12 @@ async function promptMigrate(fromVersion) {
|
|
|
83971
84097
|
}
|
|
83972
84098
|
async function editExisting(projectRoot, path, config2, onlyFields) {
|
|
83973
84099
|
const existing = await Bun.file(path).text();
|
|
84100
|
+
const detectedRepo = await detectRepoIdentity(projectRoot);
|
|
83974
84101
|
const wrote = await runSetupWizard(projectRoot, {
|
|
83975
84102
|
existing,
|
|
83976
84103
|
initialMode: "customized",
|
|
83977
84104
|
initialValues: initialValuesFromConfig(config2),
|
|
84105
|
+
...detectedRepo ? { detectedRepo } : {},
|
|
83978
84106
|
...onlyFields ? { onlyFields } : {}
|
|
83979
84107
|
});
|
|
83980
84108
|
process.stdout.write(wrote ? `
|
|
@@ -84010,7 +84138,8 @@ async function main(argv) {
|
|
|
84010
84138
|
`);
|
|
84011
84139
|
return 0;
|
|
84012
84140
|
}
|
|
84013
|
-
const
|
|
84141
|
+
const detectedRepo2 = await detectRepoIdentity(projectRoot);
|
|
84142
|
+
const wrote2 = await runSetupWizard(projectRoot, detectedRepo2 ? { detectedRepo: detectedRepo2 } : {});
|
|
84014
84143
|
process.stdout.write(wrote2 ? `
|
|
84015
84144
|
\u2713 Recreated ${path}
|
|
84016
84145
|
` : `
|
|
@@ -84043,7 +84172,8 @@ Setup cancelled \u2014 no file written.
|
|
|
84043
84172
|
`);
|
|
84044
84173
|
return 0;
|
|
84045
84174
|
}
|
|
84046
|
-
const
|
|
84175
|
+
const detectedRepo = await detectRepoIdentity(projectRoot);
|
|
84176
|
+
const wrote = await runSetupWizard(projectRoot, detectedRepo ? { detectedRepo } : {});
|
|
84047
84177
|
process.stdout.write(wrote ? `
|
|
84048
84178
|
\u2713 Created ${path}
|
|
84049
84179
|
` : `
|
|
@@ -84056,6 +84186,7 @@ var init_src4 = __esm(async () => {
|
|
|
84056
84186
|
init_paths();
|
|
84057
84187
|
init_workflow();
|
|
84058
84188
|
init_wizard();
|
|
84189
|
+
init_repo();
|
|
84059
84190
|
init_migrations();
|
|
84060
84191
|
await __promiseAll([
|
|
84061
84192
|
init_build2(),
|
|
@@ -99164,10 +99295,112 @@ var init_tasks_md = __esm(() => {
|
|
|
99164
99295
|
];
|
|
99165
99296
|
});
|
|
99166
99297
|
|
|
99298
|
+
// packages/core/src/prompt/effort.ts
|
|
99299
|
+
function countUnchecked2(tasksContent) {
|
|
99300
|
+
const matches2 = tasksContent.match(/^\s*[-*]\s+\[ \]/gm);
|
|
99301
|
+
return matches2 ? matches2.length : 0;
|
|
99302
|
+
}
|
|
99303
|
+
function clamp2(value, min2, max2) {
|
|
99304
|
+
return Math.max(min2, Math.min(max2, value));
|
|
99305
|
+
}
|
|
99306
|
+
function detectEffort(state, options = {}) {
|
|
99307
|
+
if (options.override)
|
|
99308
|
+
return options.override;
|
|
99309
|
+
const prompt = (state.prompt ?? "").toLowerCase();
|
|
99310
|
+
let keywordScore = 0;
|
|
99311
|
+
for (const kw of HEAVY_KEYWORDS) {
|
|
99312
|
+
if (prompt.includes(kw))
|
|
99313
|
+
keywordScore += HEAVY_WEIGHT;
|
|
99314
|
+
}
|
|
99315
|
+
for (const kw of LIGHT_KEYWORDS) {
|
|
99316
|
+
if (prompt.includes(kw))
|
|
99317
|
+
keywordScore += LIGHT_WEIGHT;
|
|
99318
|
+
}
|
|
99319
|
+
keywordScore = clamp2(keywordScore, -KEYWORD_HIT_CAP, KEYWORD_HIT_CAP);
|
|
99320
|
+
let score = keywordScore;
|
|
99321
|
+
const hasHeavyKeyword = keywordScore > 0;
|
|
99322
|
+
if (prompt.length > 0 && prompt.length < SHORT_PROMPT_CHARS && !hasHeavyKeyword) {
|
|
99323
|
+
score -= 1;
|
|
99324
|
+
} else if (prompt.length > LONG_PROMPT_CHARS) {
|
|
99325
|
+
score += 1;
|
|
99326
|
+
}
|
|
99327
|
+
if (options.tasksContent) {
|
|
99328
|
+
const unchecked = countUnchecked2(options.tasksContent);
|
|
99329
|
+
if (unchecked > 0 && unchecked <= FEW_TASKS) {
|
|
99330
|
+
score -= 2;
|
|
99331
|
+
} else if (unchecked >= MANY_TASKS) {
|
|
99332
|
+
score += 2;
|
|
99333
|
+
}
|
|
99334
|
+
}
|
|
99335
|
+
if (score <= LIGHT_THRESHOLD)
|
|
99336
|
+
return "light";
|
|
99337
|
+
if (score >= HEAVY_THRESHOLD)
|
|
99338
|
+
return "heavy";
|
|
99339
|
+
return "standard";
|
|
99340
|
+
}
|
|
99341
|
+
var HEAVY_KEYWORDS, LIGHT_KEYWORDS, HEAVY_WEIGHT = 2, LIGHT_WEIGHT = -2, KEYWORD_HIT_CAP = 4, SHORT_PROMPT_CHARS = 120, LONG_PROMPT_CHARS = 600, FEW_TASKS = 2, MANY_TASKS = 8, LIGHT_THRESHOLD = -2, HEAVY_THRESHOLD = 2, EFFORT_GUIDANCE;
|
|
99342
|
+
var init_effort = __esm(() => {
|
|
99343
|
+
HEAVY_KEYWORDS = [
|
|
99344
|
+
"migrate",
|
|
99345
|
+
"refactor",
|
|
99346
|
+
"redesign",
|
|
99347
|
+
"re-architect",
|
|
99348
|
+
"architecture",
|
|
99349
|
+
"rewrite",
|
|
99350
|
+
"overhaul",
|
|
99351
|
+
"breaking change",
|
|
99352
|
+
"investigate",
|
|
99353
|
+
"spike",
|
|
99354
|
+
"cross-cutting",
|
|
99355
|
+
"end-to-end"
|
|
99356
|
+
];
|
|
99357
|
+
LIGHT_KEYWORDS = [
|
|
99358
|
+
"typo",
|
|
99359
|
+
"rename",
|
|
99360
|
+
"bump",
|
|
99361
|
+
"tweak",
|
|
99362
|
+
"wording",
|
|
99363
|
+
"copy",
|
|
99364
|
+
"comment",
|
|
99365
|
+
"lint",
|
|
99366
|
+
"docs",
|
|
99367
|
+
"one-liner",
|
|
99368
|
+
"revert",
|
|
99369
|
+
"whitespace"
|
|
99370
|
+
];
|
|
99371
|
+
EFFORT_GUIDANCE = {
|
|
99372
|
+
light: [
|
|
99373
|
+
"This ticket looks **light**. Make the smallest correct change.",
|
|
99374
|
+
"- Skip research/design ceremony \u2014 go straight to the fix",
|
|
99375
|
+
"- Avoid speculative abstraction; do not expand scope",
|
|
99376
|
+
"- Aim to finish in as few iterations as possible"
|
|
99377
|
+
].join(`
|
|
99378
|
+
`),
|
|
99379
|
+
standard: [
|
|
99380
|
+
"This ticket looks **standard**. Balance thoroughness with momentum.",
|
|
99381
|
+
"- Do enough investigation to be confident, but don't over-engineer",
|
|
99382
|
+
"- Keep changes focused on the stated scope",
|
|
99383
|
+
"- Verify with the project's lint and test gates before finishing"
|
|
99384
|
+
].join(`
|
|
99385
|
+
`),
|
|
99386
|
+
heavy: [
|
|
99387
|
+
"This ticket looks **heavy**. Invest up front before changing code.",
|
|
99388
|
+
"- Research the affected areas and write a real design",
|
|
99389
|
+
"- Break the work into small, independently-verifiable tasks",
|
|
99390
|
+
"- Watch for cross-cutting impact and regressions as you go"
|
|
99391
|
+
].join(`
|
|
99392
|
+
`)
|
|
99393
|
+
};
|
|
99394
|
+
});
|
|
99395
|
+
|
|
99167
99396
|
// packages/core/src/prompt/meta-prompt.ts
|
|
99168
99397
|
function buildMetaPrompt(state, phase, options = {}) {
|
|
99169
99398
|
if (options.enabled === false)
|
|
99170
99399
|
return "";
|
|
99400
|
+
const effort = detectEffort(state, {
|
|
99401
|
+
...options.effort !== undefined ? { override: options.effort } : {},
|
|
99402
|
+
...options.tasksContent !== undefined ? { tasksContent: options.tasksContent } : {}
|
|
99403
|
+
});
|
|
99171
99404
|
let out = `---
|
|
99172
99405
|
|
|
99173
99406
|
## Task Context
|
|
@@ -99178,6 +99411,8 @@ function buildMetaPrompt(state, phase, options = {}) {
|
|
|
99178
99411
|
out += `**Engine/Model:** ${state.engine} / ${state.model}
|
|
99179
99412
|
`;
|
|
99180
99413
|
out += `**Phase:** ${phase}
|
|
99414
|
+
`;
|
|
99415
|
+
out += `**Effort:** ${effort}
|
|
99181
99416
|
`;
|
|
99182
99417
|
out += `**Iteration:** ${state.iteration + 1}`;
|
|
99183
99418
|
if (options.maxIterations && options.maxIterations > 0) {
|
|
@@ -99201,6 +99436,12 @@ function buildMetaPrompt(state, phase, options = {}) {
|
|
|
99201
99436
|
`;
|
|
99202
99437
|
out += PHASE_GUIDANCE[phase] + `
|
|
99203
99438
|
|
|
99439
|
+
`;
|
|
99440
|
+
out += `### Effort Guidance
|
|
99441
|
+
|
|
99442
|
+
`;
|
|
99443
|
+
out += EFFORT_GUIDANCE[effort] + `
|
|
99444
|
+
|
|
99204
99445
|
`;
|
|
99205
99446
|
const flags = [];
|
|
99206
99447
|
if (options.useWorktree) {
|
|
@@ -99233,6 +99474,7 @@ function buildMetaPrompt(state, phase, options = {}) {
|
|
|
99233
99474
|
}
|
|
99234
99475
|
var PHASE_GUIDANCE;
|
|
99235
99476
|
var init_meta_prompt = __esm(() => {
|
|
99477
|
+
init_effort();
|
|
99236
99478
|
PHASE_GUIDANCE = {
|
|
99237
99479
|
research: [
|
|
99238
99480
|
"You are in the **research** phase. Your goal is to understand, not to implement.",
|
|
@@ -99706,6 +99948,7 @@ var init_loop = __esm(() => {
|
|
|
99706
99948
|
init_tasks_md();
|
|
99707
99949
|
init_phase();
|
|
99708
99950
|
init_meta_prompt();
|
|
99951
|
+
init_effort();
|
|
99709
99952
|
init_tasks_md();
|
|
99710
99953
|
});
|
|
99711
99954
|
|
|
@@ -99986,7 +100229,10 @@ function useLoop(opts) {
|
|
|
99986
100229
|
design: designContent,
|
|
99987
100230
|
tasks: tasksContent
|
|
99988
100231
|
});
|
|
99989
|
-
const prompt = buildPhasePrompt(routedPhase, currentState, tasksDir, opts.reviewPhase,
|
|
100232
|
+
const prompt = buildPhasePrompt(routedPhase, currentState, tasksDir, opts.reviewPhase, {
|
|
100233
|
+
...opts.metaPrompt,
|
|
100234
|
+
...tasksContent !== null ? { tasksContent } : {}
|
|
100235
|
+
});
|
|
99990
100236
|
const iterStart = new Date().toISOString();
|
|
99991
100237
|
try {
|
|
99992
100238
|
const controller = new AbortController;
|
|
@@ -101172,7 +101418,8 @@ async function parseAgentArgs(argv) {
|
|
|
101172
101418
|
debug: false,
|
|
101173
101419
|
noTmux: false,
|
|
101174
101420
|
checks: false,
|
|
101175
|
-
review: false
|
|
101421
|
+
review: false,
|
|
101422
|
+
ticketTokens: []
|
|
101176
101423
|
};
|
|
101177
101424
|
const state = emptyParseState();
|
|
101178
101425
|
let expectLinearTeam = false;
|
|
@@ -101182,6 +101429,7 @@ async function parseAgentArgs(argv) {
|
|
|
101182
101429
|
let expectMaxTickets = false;
|
|
101183
101430
|
let expectIndicator = false;
|
|
101184
101431
|
let expectJsonLogFile = false;
|
|
101432
|
+
let expectTicket = false;
|
|
101185
101433
|
for (const arg of argv) {
|
|
101186
101434
|
if (expectLinearTeam) {
|
|
101187
101435
|
result2.linearTeam = arg;
|
|
@@ -101219,6 +101467,14 @@ async function parseAgentArgs(argv) {
|
|
|
101219
101467
|
expectJsonLogFile = false;
|
|
101220
101468
|
continue;
|
|
101221
101469
|
}
|
|
101470
|
+
if (expectTicket) {
|
|
101471
|
+
for (const token of arg.split(",").map((t) => t.trim())) {
|
|
101472
|
+
if (token)
|
|
101473
|
+
result2.ticketTokens.push(token);
|
|
101474
|
+
}
|
|
101475
|
+
expectTicket = false;
|
|
101476
|
+
continue;
|
|
101477
|
+
}
|
|
101222
101478
|
if (parseCommonArg(arg, result2, state))
|
|
101223
101479
|
continue;
|
|
101224
101480
|
switch (arg) {
|
|
@@ -101237,6 +101493,9 @@ async function parseAgentArgs(argv) {
|
|
|
101237
101493
|
case "--max-tickets":
|
|
101238
101494
|
expectMaxTickets = true;
|
|
101239
101495
|
break;
|
|
101496
|
+
case "--ticket":
|
|
101497
|
+
expectTicket = true;
|
|
101498
|
+
break;
|
|
101240
101499
|
case "--worktree":
|
|
101241
101500
|
result2.worktree = true;
|
|
101242
101501
|
break;
|
|
@@ -101361,6 +101620,7 @@ var init_cli2 = __esm(() => {
|
|
|
101361
101620
|
" --stack-prs Base the PR on a blocker issue's open-PR head branch when present (needs --create-pr)",
|
|
101362
101621
|
" --code-review Watch open tracked PRs for unresolved review comments",
|
|
101363
101622
|
" --max-tickets <n> Stop picking up new issues after N have been started (0 = unlimited)",
|
|
101623
|
+
" --ticket <id> Restrict issue discovery to specific ticket(s); repeatable or comma-separated (e.g. RLF-208 or 208)",
|
|
101364
101624
|
" --no-tmux Disable tmux session management; run agent in the foreground directly",
|
|
101365
101625
|
" --no-pr-tracker Disable RLF-173 pr-tracker bail / recovery counter for this run",
|
|
101366
101626
|
" --json-output Emit JSONL to stdout instead of the Ink dashboard (for scripting/CI)",
|
|
@@ -101382,6 +101642,11 @@ var init_cli2 = __esm(() => {
|
|
|
101382
101642
|
});
|
|
101383
101643
|
|
|
101384
101644
|
// apps/agent/src/agent/config.ts
|
|
101645
|
+
var exports_config = {};
|
|
101646
|
+
__export(exports_config, {
|
|
101647
|
+
loadRalphyConfig: () => loadRalphyConfig,
|
|
101648
|
+
ensureRalphyConfig: () => ensureRalphyConfig
|
|
101649
|
+
});
|
|
101385
101650
|
async function loadRalphyConfig(projectRoot) {
|
|
101386
101651
|
const { config: config2 } = await loadWorkflow(projectRoot);
|
|
101387
101652
|
return config2;
|
|
@@ -101746,6 +102011,103 @@ function isRalphComment(body) {
|
|
|
101746
102011
|
}
|
|
101747
102012
|
|
|
101748
102013
|
// apps/agent/src/shared/capabilities/linear-client.ts
|
|
102014
|
+
var exports_linear_client = {};
|
|
102015
|
+
__export(exports_linear_client, {
|
|
102016
|
+
upsertRalphyAttachment: () => upsertRalphyAttachment,
|
|
102017
|
+
uploadFileToLinear: () => uploadFileToLinear,
|
|
102018
|
+
updateIssueState: () => updateIssueState,
|
|
102019
|
+
updateIssueDescription: () => updateIssueDescription,
|
|
102020
|
+
updateIssueComment: () => updateIssueComment,
|
|
102021
|
+
updateAttachmentSubtitle: () => updateAttachmentSubtitle,
|
|
102022
|
+
setIssueProject: () => setIssueProject,
|
|
102023
|
+
resolveTicketNumbers: () => resolveTicketNumbers,
|
|
102024
|
+
removeLabelFromIssue: () => removeLabelFromIssue,
|
|
102025
|
+
parseTicketIdentifier: () => parseTicketIdentifier,
|
|
102026
|
+
linearRequestInternals: () => linearRequestInternals,
|
|
102027
|
+
issueMatchesGetIndicator: () => issueMatchesGetIndicator,
|
|
102028
|
+
isRateLimitedError: () => isRateLimitedError,
|
|
102029
|
+
formatTicketError: () => formatTicketError,
|
|
102030
|
+
formatLinearError: () => formatLinearError,
|
|
102031
|
+
findOpenIssueByLabel: () => findOpenIssueByLabel,
|
|
102032
|
+
findIssueAttachmentByTitle: () => findIssueAttachmentByTitle,
|
|
102033
|
+
fetchWorkflowStates: () => fetchWorkflowStates,
|
|
102034
|
+
fetchTeamIdByKey: () => fetchTeamIdByKey,
|
|
102035
|
+
fetchProjectIdByName: () => fetchProjectIdByName,
|
|
102036
|
+
fetchOpenIssues: () => fetchOpenIssues,
|
|
102037
|
+
fetchMentionScanIssues: () => fetchMentionScanIssues,
|
|
102038
|
+
fetchIssueLabels: () => fetchIssueLabels,
|
|
102039
|
+
fetchIssueComments: () => fetchIssueComments,
|
|
102040
|
+
fetchIssueAttachments: () => fetchIssueAttachments,
|
|
102041
|
+
fetchAttachmentsForIssues: () => fetchAttachmentsForIssues,
|
|
102042
|
+
deleteIssueComment: () => deleteIssueComment,
|
|
102043
|
+
deleteAttachment: () => deleteAttachment,
|
|
102044
|
+
createRalphyAttachment: () => createRalphyAttachment,
|
|
102045
|
+
createIssueLabel: () => createIssueLabel,
|
|
102046
|
+
createIssueComment: () => createIssueComment,
|
|
102047
|
+
createIssue: () => createIssue,
|
|
102048
|
+
createAttachmentForUrl: () => createAttachmentForUrl,
|
|
102049
|
+
clauseFromMarkers: () => clauseFromMarkers,
|
|
102050
|
+
buildIssueFilter: () => buildIssueFilter,
|
|
102051
|
+
baseBranchFromLabels: () => baseBranchFromLabels,
|
|
102052
|
+
addReactionToComment: () => addReactionToComment,
|
|
102053
|
+
addLabelToIssue: () => addLabelToIssue,
|
|
102054
|
+
addIssueComment: () => addIssueComment,
|
|
102055
|
+
RALPHY_ATTACHMENT_TITLE: () => RALPHY_ATTACHMENT_TITLE
|
|
102056
|
+
});
|
|
102057
|
+
function parseTicketIdentifier(raw) {
|
|
102058
|
+
const trimmed = raw.trim();
|
|
102059
|
+
if (!trimmed) {
|
|
102060
|
+
throw new Error("--ticket value cannot be empty");
|
|
102061
|
+
}
|
|
102062
|
+
const bare = TICKET_BARE_NUMBER_RE.exec(trimmed);
|
|
102063
|
+
if (bare) {
|
|
102064
|
+
return { teamKey: null, number: Number(bare[1]) };
|
|
102065
|
+
}
|
|
102066
|
+
const match = TICKET_IDENTIFIER_RE.exec(trimmed);
|
|
102067
|
+
if (!match) {
|
|
102068
|
+
const err = new Error("--ticket value is not a Linear ticket (expected e.g. RLF-208 or 208)");
|
|
102069
|
+
err.value = raw;
|
|
102070
|
+
throw err;
|
|
102071
|
+
}
|
|
102072
|
+
return { teamKey: match[1].toUpperCase(), number: Number(match[2]) };
|
|
102073
|
+
}
|
|
102074
|
+
function resolveTicketNumbers(tokens, team) {
|
|
102075
|
+
const teamKey = team?.trim() ? team.trim().toUpperCase() : null;
|
|
102076
|
+
const seen = new Set;
|
|
102077
|
+
const out = [];
|
|
102078
|
+
for (const token of tokens) {
|
|
102079
|
+
const { teamKey: parsedTeam, number: number4 } = parseTicketIdentifier(token);
|
|
102080
|
+
if (parsedTeam !== null) {
|
|
102081
|
+
if (teamKey !== null && parsedTeam !== teamKey) {
|
|
102082
|
+
const err = new Error("--ticket identifier is not in the configured team");
|
|
102083
|
+
err.ticket = token;
|
|
102084
|
+
err.team = team;
|
|
102085
|
+
throw err;
|
|
102086
|
+
}
|
|
102087
|
+
} else if (teamKey === null) {
|
|
102088
|
+
const err = new Error("--ticket bare number needs a configured team; pass --linear-team or set linear.team in config");
|
|
102089
|
+
err.ticket = token;
|
|
102090
|
+
throw err;
|
|
102091
|
+
}
|
|
102092
|
+
if (!seen.has(number4)) {
|
|
102093
|
+
seen.add(number4);
|
|
102094
|
+
out.push(number4);
|
|
102095
|
+
}
|
|
102096
|
+
}
|
|
102097
|
+
return out;
|
|
102098
|
+
}
|
|
102099
|
+
function formatTicketError(err) {
|
|
102100
|
+
if (!(err instanceof Error))
|
|
102101
|
+
return String(err);
|
|
102102
|
+
const e = err;
|
|
102103
|
+
const detail = e.ticket ?? e.value;
|
|
102104
|
+
const parts = [];
|
|
102105
|
+
if (detail)
|
|
102106
|
+
parts.push(`ticket: ${detail}`);
|
|
102107
|
+
if (e.team)
|
|
102108
|
+
parts.push(`configured team: ${e.team}`);
|
|
102109
|
+
return parts.length > 0 ? `${e.message} (${parts.join(", ")})` : e.message;
|
|
102110
|
+
}
|
|
101749
102111
|
function partition2(markers) {
|
|
101750
102112
|
const statuses = [];
|
|
101751
102113
|
const labels = [];
|
|
@@ -101778,6 +102140,9 @@ function buildIssueFilter(spec) {
|
|
|
101778
102140
|
} else {
|
|
101779
102141
|
where.assignee = { null: true };
|
|
101780
102142
|
}
|
|
102143
|
+
if (spec.numbers && spec.numbers.length > 0) {
|
|
102144
|
+
where.number = { in: spec.numbers };
|
|
102145
|
+
}
|
|
101781
102146
|
const inc = spec.include ?? [];
|
|
101782
102147
|
if (inc.length > 0) {
|
|
101783
102148
|
const { statuses, labels, attachmentSubtitles, projects } = partition2(inc);
|
|
@@ -101881,6 +102246,30 @@ function clauseFromMarkers(markers) {
|
|
|
101881
102246
|
parts.project = { name: { in: projects } };
|
|
101882
102247
|
return Object.keys(parts).length > 0 ? parts : null;
|
|
101883
102248
|
}
|
|
102249
|
+
function mapNodeProject(node2) {
|
|
102250
|
+
if (!node2.project)
|
|
102251
|
+
return null;
|
|
102252
|
+
return {
|
|
102253
|
+
id: node2.project.id,
|
|
102254
|
+
name: node2.project.name,
|
|
102255
|
+
...node2.project.priority !== undefined && node2.project.priority !== null ? { priority: node2.project.priority } : {}
|
|
102256
|
+
};
|
|
102257
|
+
}
|
|
102258
|
+
function mapNodeMilestone(node2) {
|
|
102259
|
+
const m = node2.projectMilestone;
|
|
102260
|
+
if (!m)
|
|
102261
|
+
return;
|
|
102262
|
+
return {
|
|
102263
|
+
id: m.id,
|
|
102264
|
+
name: m.name,
|
|
102265
|
+
sortOrder: m.sortOrder,
|
|
102266
|
+
...m.targetDate != null ? { targetDate: m.targetDate } : {}
|
|
102267
|
+
};
|
|
102268
|
+
}
|
|
102269
|
+
function milestoneSpread(node2) {
|
|
102270
|
+
const m = mapNodeMilestone(node2);
|
|
102271
|
+
return m ? { milestone: m } : {};
|
|
102272
|
+
}
|
|
101884
102273
|
async function fetchMentionScanIssues(apiKey, spec) {
|
|
101885
102274
|
const branches = [];
|
|
101886
102275
|
const { getTodo, getInProgress, setDone } = spec.indicators;
|
|
@@ -101909,13 +102298,17 @@ async function fetchMentionScanIssues(apiKey, spec) {
|
|
|
101909
102298
|
else
|
|
101910
102299
|
where.assignee = { id: { eq: spec.assignee } };
|
|
101911
102300
|
}
|
|
102301
|
+
if (spec.numbers && spec.numbers.length > 0) {
|
|
102302
|
+
where.number = { in: spec.numbers };
|
|
102303
|
+
}
|
|
101912
102304
|
const query = `query MentionScanIssues($filter: IssueFilter) {
|
|
101913
102305
|
issues(filter: $filter, first: 50) {
|
|
101914
102306
|
nodes {
|
|
101915
102307
|
id identifier title description url priority createdAt
|
|
101916
102308
|
state { name type }
|
|
101917
102309
|
assignee { id email name }
|
|
101918
|
-
project { id name }
|
|
102310
|
+
project { id name priority }
|
|
102311
|
+
projectMilestone { id name sortOrder targetDate }
|
|
101919
102312
|
labels { nodes { name } }
|
|
101920
102313
|
relations(first: 50) {
|
|
101921
102314
|
nodes { type relatedIssue { id identifier state { type } } }
|
|
@@ -101938,7 +102331,8 @@ async function fetchMentionScanIssues(apiKey, spec) {
|
|
|
101938
102331
|
url: n.url,
|
|
101939
102332
|
state: n.state,
|
|
101940
102333
|
assignee: n.assignee,
|
|
101941
|
-
project: n
|
|
102334
|
+
project: mapNodeProject(n),
|
|
102335
|
+
...milestoneSpread(n),
|
|
101942
102336
|
labels: n.labels.nodes.map((l) => l.name),
|
|
101943
102337
|
priority: n.priority,
|
|
101944
102338
|
createdAt: n.createdAt ?? "",
|
|
@@ -101959,7 +102353,8 @@ async function fetchOpenIssues(apiKey, spec, options) {
|
|
|
101959
102353
|
id identifier title description url priority createdAt
|
|
101960
102354
|
state { name type }
|
|
101961
102355
|
assignee { id email name }
|
|
101962
|
-
project { id name }
|
|
102356
|
+
project { id name priority }
|
|
102357
|
+
projectMilestone { id name sortOrder targetDate }
|
|
101963
102358
|
labels { nodes { name } }
|
|
101964
102359
|
relations(first: 50) {
|
|
101965
102360
|
nodes {
|
|
@@ -101983,7 +102378,8 @@ async function fetchOpenIssues(apiKey, spec, options) {
|
|
|
101983
102378
|
url: n.url,
|
|
101984
102379
|
state: n.state,
|
|
101985
102380
|
assignee: n.assignee,
|
|
101986
|
-
project: n
|
|
102381
|
+
project: mapNodeProject(n),
|
|
102382
|
+
...milestoneSpread(n),
|
|
101987
102383
|
labels: n.labels.nodes.map((l) => l.name),
|
|
101988
102384
|
priority: n.priority,
|
|
101989
102385
|
createdAt: n.createdAt ?? "",
|
|
@@ -102496,9 +102892,11 @@ async function removeLabelFromIssue(apiKey, issueId, labelId) {
|
|
|
102496
102892
|
labelId
|
|
102497
102893
|
});
|
|
102498
102894
|
}
|
|
102499
|
-
var LINEAR_API = "https://api.linear.app/graphql", 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:";
|
|
102895
|
+
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:";
|
|
102500
102896
|
var init_linear_client = __esm(() => {
|
|
102501
102897
|
init_types2();
|
|
102898
|
+
TICKET_IDENTIFIER_RE = /^([A-Za-z]+)-(\d+)(?:-.*)?$/;
|
|
102899
|
+
TICKET_BARE_NUMBER_RE = /^(\d+)$/;
|
|
102502
102900
|
linearRequestInternals = {
|
|
102503
102901
|
sleep: (ms) => Bun.sleep(ms)
|
|
102504
102902
|
};
|
|
@@ -104121,17 +104519,183 @@ var init_post_task = __esm(() => {
|
|
|
104121
104519
|
repoAutoMergeCache = new Map;
|
|
104122
104520
|
});
|
|
104123
104521
|
|
|
104124
|
-
//
|
|
104125
|
-
function
|
|
104126
|
-
return
|
|
104127
|
-
|
|
104128
|
-
|
|
104129
|
-
|
|
104130
|
-
return r;
|
|
104131
|
-
}
|
|
104522
|
+
// packages/core/src/ordering/hierarchical-order.ts
|
|
104523
|
+
function rank(priority) {
|
|
104524
|
+
return !priority ? Number.POSITIVE_INFINITY : priority;
|
|
104525
|
+
}
|
|
104526
|
+
function cmpMaybe(a, b, cmp) {
|
|
104527
|
+
if (a === undefined && b === undefined)
|
|
104132
104528
|
return 0;
|
|
104133
|
-
|
|
104529
|
+
if (a === undefined)
|
|
104530
|
+
return 1;
|
|
104531
|
+
if (b === undefined)
|
|
104532
|
+
return -1;
|
|
104533
|
+
return cmp(a, b);
|
|
104534
|
+
}
|
|
104535
|
+
function cmpString(a, b) {
|
|
104536
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
104537
|
+
}
|
|
104538
|
+
function cmpNumber(a, b) {
|
|
104539
|
+
return a - b;
|
|
104540
|
+
}
|
|
104541
|
+
function cmpRank(a, b) {
|
|
104542
|
+
return a === b ? 0 : a < b ? -1 : 1;
|
|
104134
104543
|
}
|
|
104544
|
+
function orderIssuesHierarchically(issues, opts = {}) {
|
|
104545
|
+
const log3 = opts.log ?? ((message) => console.warn(message));
|
|
104546
|
+
if (issues.length <= 1)
|
|
104547
|
+
return issues.slice();
|
|
104548
|
+
const projectBuckets = new Map;
|
|
104549
|
+
for (const issue2 of issues) {
|
|
104550
|
+
const key = issue2.project?.id ?? NO_PROJECT;
|
|
104551
|
+
const bucket = projectBuckets.get(key);
|
|
104552
|
+
if (bucket)
|
|
104553
|
+
bucket.push(issue2);
|
|
104554
|
+
else
|
|
104555
|
+
projectBuckets.set(key, [issue2]);
|
|
104556
|
+
}
|
|
104557
|
+
const projectKeys = [...projectBuckets.keys()].sort((a, b) => {
|
|
104558
|
+
if (a === NO_PROJECT)
|
|
104559
|
+
return b === NO_PROJECT ? 0 : 1;
|
|
104560
|
+
if (b === NO_PROJECT)
|
|
104561
|
+
return -1;
|
|
104562
|
+
const ba = projectBuckets.get(a);
|
|
104563
|
+
const bb = projectBuckets.get(b);
|
|
104564
|
+
const ra = rank(ba[0]?.project?.priority);
|
|
104565
|
+
const rb = rank(bb[0]?.project?.priority);
|
|
104566
|
+
if (ra !== rb)
|
|
104567
|
+
return cmpRank(ra, rb);
|
|
104568
|
+
if (a !== b)
|
|
104569
|
+
return cmpString(a, b);
|
|
104570
|
+
return cmpString(earliestCreatedAt(ba), earliestCreatedAt(bb));
|
|
104571
|
+
});
|
|
104572
|
+
const ordered = [];
|
|
104573
|
+
for (const projectKey of projectKeys) {
|
|
104574
|
+
orderProjectBucket(projectBuckets.get(projectKey), ordered, log3);
|
|
104575
|
+
}
|
|
104576
|
+
return ordered;
|
|
104577
|
+
}
|
|
104578
|
+
function earliestCreatedAt(bucket) {
|
|
104579
|
+
let earliest = bucket[0].createdAt;
|
|
104580
|
+
for (const issue2 of bucket) {
|
|
104581
|
+
if (issue2.createdAt < earliest)
|
|
104582
|
+
earliest = issue2.createdAt;
|
|
104583
|
+
}
|
|
104584
|
+
return earliest;
|
|
104585
|
+
}
|
|
104586
|
+
function orderProjectBucket(bucket, ordered, log3) {
|
|
104587
|
+
const milestoneBuckets = new Map;
|
|
104588
|
+
for (const issue2 of bucket) {
|
|
104589
|
+
const key = issue2.milestone?.id ?? NO_MILESTONE;
|
|
104590
|
+
const b = milestoneBuckets.get(key);
|
|
104591
|
+
if (b)
|
|
104592
|
+
b.push(issue2);
|
|
104593
|
+
else
|
|
104594
|
+
milestoneBuckets.set(key, [issue2]);
|
|
104595
|
+
}
|
|
104596
|
+
const milestoneOf = new Map;
|
|
104597
|
+
for (const [key, b] of milestoneBuckets) {
|
|
104598
|
+
for (const issue2 of b)
|
|
104599
|
+
milestoneOf.set(issue2.id, key);
|
|
104600
|
+
}
|
|
104601
|
+
const milestonePrereqs = new Map;
|
|
104602
|
+
for (const key of milestoneBuckets.keys())
|
|
104603
|
+
milestonePrereqs.set(key, new Set);
|
|
104604
|
+
for (const issue2 of bucket) {
|
|
104605
|
+
const from = milestoneOf.get(issue2.id);
|
|
104606
|
+
for (const blockerId of issue2.blockedByIds) {
|
|
104607
|
+
const blockerMilestone = milestoneOf.get(blockerId);
|
|
104608
|
+
if (blockerMilestone === undefined)
|
|
104609
|
+
continue;
|
|
104610
|
+
if (blockerMilestone === from)
|
|
104611
|
+
continue;
|
|
104612
|
+
milestonePrereqs.get(from).add(blockerMilestone);
|
|
104613
|
+
}
|
|
104614
|
+
}
|
|
104615
|
+
const milestoneMeta = new Map;
|
|
104616
|
+
for (const [key, b] of milestoneBuckets) {
|
|
104617
|
+
milestoneMeta.set(key, key === NO_MILESTONE ? undefined : b[0].milestone);
|
|
104618
|
+
}
|
|
104619
|
+
const milestoneOrder = topoOrder([...milestoneBuckets.keys()], (key) => milestonePrereqs.get(key), (a, b) => {
|
|
104620
|
+
if (a === NO_MILESTONE)
|
|
104621
|
+
return b === NO_MILESTONE ? 0 : 1;
|
|
104622
|
+
if (b === NO_MILESTONE)
|
|
104623
|
+
return -1;
|
|
104624
|
+
const ma = milestoneMeta.get(a);
|
|
104625
|
+
const mb = milestoneMeta.get(b);
|
|
104626
|
+
const so = cmpNumber(ma?.sortOrder ?? 0, mb?.sortOrder ?? 0);
|
|
104627
|
+
if (so !== 0)
|
|
104628
|
+
return so;
|
|
104629
|
+
const td = cmpMaybe(ma?.targetDate, mb?.targetDate, cmpString);
|
|
104630
|
+
if (td !== 0)
|
|
104631
|
+
return td;
|
|
104632
|
+
return cmpString(a, b);
|
|
104633
|
+
}, (key) => log3(`hierarchical-order: milestone dependency cycle involving "${key}"; breaking by selection key`));
|
|
104634
|
+
for (const milestoneKey of milestoneOrder) {
|
|
104635
|
+
orderMilestoneBucket(milestoneBuckets.get(milestoneKey), ordered, log3);
|
|
104636
|
+
}
|
|
104637
|
+
}
|
|
104638
|
+
function orderMilestoneBucket(bucket, ordered, log3) {
|
|
104639
|
+
const inBucket = new Set(bucket.map((i) => i.id));
|
|
104640
|
+
const byId = new Map(bucket.map((i) => [i.id, i]));
|
|
104641
|
+
const order = topoOrder(bucket.map((i) => i.id), (id) => {
|
|
104642
|
+
const prereqs = new Set;
|
|
104643
|
+
for (const blockerId of byId.get(id).blockedByIds) {
|
|
104644
|
+
if (inBucket.has(blockerId))
|
|
104645
|
+
prereqs.add(blockerId);
|
|
104646
|
+
}
|
|
104647
|
+
return prereqs;
|
|
104648
|
+
}, (a, b) => {
|
|
104649
|
+
const ia = byId.get(a);
|
|
104650
|
+
const ib = byId.get(b);
|
|
104651
|
+
const rp = cmpRank(rank(ia.priority), rank(ib.priority));
|
|
104652
|
+
if (rp !== 0)
|
|
104653
|
+
return rp;
|
|
104654
|
+
const tb = cmpNumber(ia.tiebreak ?? 0, ib.tiebreak ?? 0);
|
|
104655
|
+
if (tb !== 0)
|
|
104656
|
+
return tb;
|
|
104657
|
+
const cc = cmpString(ia.createdAt, ib.createdAt);
|
|
104658
|
+
if (cc !== 0)
|
|
104659
|
+
return cc;
|
|
104660
|
+
return cmpString(a, b);
|
|
104661
|
+
}, (id) => log3(`hierarchical-order: item dependency cycle involving "${id}"; breaking by selection key`));
|
|
104662
|
+
for (const id of order)
|
|
104663
|
+
ordered.push(byId.get(id));
|
|
104664
|
+
}
|
|
104665
|
+
function topoOrder(nodes, prereqsOf, select2, onCycle) {
|
|
104666
|
+
const remaining = new Set(nodes);
|
|
104667
|
+
const placed = new Set;
|
|
104668
|
+
const result2 = [];
|
|
104669
|
+
while (remaining.size > 0) {
|
|
104670
|
+
const eligible = [];
|
|
104671
|
+
for (const node2 of remaining) {
|
|
104672
|
+
let ready = true;
|
|
104673
|
+
for (const prereq of prereqsOf(node2)) {
|
|
104674
|
+
if (remaining.has(prereq) && !placed.has(prereq)) {
|
|
104675
|
+
ready = false;
|
|
104676
|
+
break;
|
|
104677
|
+
}
|
|
104678
|
+
}
|
|
104679
|
+
if (ready)
|
|
104680
|
+
eligible.push(node2);
|
|
104681
|
+
}
|
|
104682
|
+
let pick3;
|
|
104683
|
+
if (eligible.length > 0) {
|
|
104684
|
+
pick3 = eligible.sort(select2)[0];
|
|
104685
|
+
} else {
|
|
104686
|
+
pick3 = [...remaining].sort(select2)[0];
|
|
104687
|
+
onCycle(pick3);
|
|
104688
|
+
}
|
|
104689
|
+
result2.push(pick3);
|
|
104690
|
+
placed.add(pick3);
|
|
104691
|
+
remaining.delete(pick3);
|
|
104692
|
+
}
|
|
104693
|
+
return result2;
|
|
104694
|
+
}
|
|
104695
|
+
var NO_PROJECT = "\x00no-project", NO_MILESTONE = "\x00no-milestone";
|
|
104696
|
+
|
|
104697
|
+
// packages/core/src/ordering/index.ts
|
|
104698
|
+
var init_ordering = () => {};
|
|
104135
104699
|
|
|
104136
104700
|
// apps/agent/src/queue/queue-order.ts
|
|
104137
104701
|
function defaultPriorityFor(trigger) {
|
|
@@ -104148,21 +104712,70 @@ function defaultPriorityFor(trigger) {
|
|
|
104148
104712
|
return 4;
|
|
104149
104713
|
}
|
|
104150
104714
|
}
|
|
104151
|
-
function
|
|
104152
|
-
|
|
104153
|
-
|
|
104154
|
-
|
|
104155
|
-
|
|
104156
|
-
|
|
104157
|
-
|
|
104158
|
-
|
|
104159
|
-
|
|
104160
|
-
|
|
104161
|
-
|
|
104162
|
-
|
|
104163
|
-
|
|
104715
|
+
function isAutoMergeBoost(e, getAutoMerge) {
|
|
104716
|
+
return e.trigger === "conflict-fix" && issueMatchesGetIndicator(e.issue, getAutoMerge);
|
|
104717
|
+
}
|
|
104718
|
+
function linearIssueToOrderable(issue2, tiebreak) {
|
|
104719
|
+
return {
|
|
104720
|
+
id: issue2.id,
|
|
104721
|
+
...issue2.project ? {
|
|
104722
|
+
project: {
|
|
104723
|
+
id: issue2.project.id,
|
|
104724
|
+
...issue2.project.priority !== undefined ? { priority: issue2.project.priority } : {}
|
|
104725
|
+
}
|
|
104726
|
+
} : {},
|
|
104727
|
+
...issue2.milestone ? {
|
|
104728
|
+
milestone: {
|
|
104729
|
+
id: issue2.milestone.id,
|
|
104730
|
+
sortOrder: issue2.milestone.sortOrder,
|
|
104731
|
+
...issue2.milestone.targetDate ? { targetDate: issue2.milestone.targetDate } : {}
|
|
104732
|
+
}
|
|
104733
|
+
} : {},
|
|
104734
|
+
priority: issue2.priority,
|
|
104735
|
+
...tiebreak !== undefined ? { tiebreak } : {},
|
|
104736
|
+
blockedByIds: issue2.blockedByIds,
|
|
104737
|
+
createdAt: issue2.createdAt
|
|
104738
|
+
};
|
|
104739
|
+
}
|
|
104740
|
+
function toOrderable(entry) {
|
|
104741
|
+
return { ...linearIssueToOrderable(entry.issue, entry.priority), entry };
|
|
104742
|
+
}
|
|
104743
|
+
function orderEntries(entries) {
|
|
104744
|
+
if (entries.length <= 1)
|
|
104745
|
+
return entries.slice();
|
|
104746
|
+
const repByIssue = new Map;
|
|
104747
|
+
for (const entry of entries) {
|
|
104748
|
+
const orderable = toOrderable(entry);
|
|
104749
|
+
const existing = repByIssue.get(orderable.id);
|
|
104750
|
+
if (!existing || orderable.tiebreak < existing.tiebreak) {
|
|
104751
|
+
repByIssue.set(orderable.id, orderable);
|
|
104752
|
+
}
|
|
104753
|
+
}
|
|
104754
|
+
const rankOf = new Map;
|
|
104755
|
+
orderIssuesHierarchically([...repByIssue.values()]).forEach((o, i) => rankOf.set(o.id, i));
|
|
104756
|
+
return entries.map((entry, index) => ({ entry, index })).sort((a, b) => {
|
|
104757
|
+
const ra = rankOf.get(a.entry.issue.id);
|
|
104758
|
+
const rb = rankOf.get(b.entry.issue.id);
|
|
104759
|
+
if (ra !== rb)
|
|
104760
|
+
return ra - rb;
|
|
104761
|
+
if (a.entry.priority !== b.entry.priority)
|
|
104762
|
+
return a.entry.priority - b.entry.priority;
|
|
104763
|
+
return a.index - b.index;
|
|
104764
|
+
}).map((x) => x.entry);
|
|
104765
|
+
}
|
|
104766
|
+
function orderQueueEntries(entries, getAutoMerge) {
|
|
104767
|
+
const boosted = [];
|
|
104768
|
+
const rest2 = [];
|
|
104769
|
+
for (const e of entries) {
|
|
104770
|
+
if (isAutoMergeBoost(e, getAutoMerge))
|
|
104771
|
+
boosted.push(e);
|
|
104772
|
+
else
|
|
104773
|
+
rest2.push(e);
|
|
104774
|
+
}
|
|
104775
|
+
return [...orderEntries(boosted), ...orderEntries(rest2)];
|
|
104164
104776
|
}
|
|
104165
104777
|
var init_queue_order = __esm(() => {
|
|
104778
|
+
init_ordering();
|
|
104166
104779
|
init_linear();
|
|
104167
104780
|
});
|
|
104168
104781
|
|
|
@@ -104362,7 +104975,7 @@ class AgentCoordinator {
|
|
|
104362
104975
|
}
|
|
104363
104976
|
const prStatus = await this.scanPrMergeStates();
|
|
104364
104977
|
if (this.queue.length > 0) {
|
|
104365
|
-
this.queue
|
|
104978
|
+
this.queue = orderQueueEntries(this.queue, this.opts.getAutoMerge);
|
|
104366
104979
|
}
|
|
104367
104980
|
this.spawnNext();
|
|
104368
104981
|
await this.reportProgress();
|
|
@@ -105956,6 +106569,7 @@ var init_indicators = __esm(() => {
|
|
|
105956
106569
|
// apps/agent/src/agent/wire/linear-resolvers.ts
|
|
105957
106570
|
function createLinearResolvers(input) {
|
|
105958
106571
|
const { apiKey, team, assignee, diag } = input;
|
|
106572
|
+
const ticketNumbers = input.ticketNumbers ?? [];
|
|
105959
106573
|
const stateCache = new Map;
|
|
105960
106574
|
const labelCache = new Map;
|
|
105961
106575
|
const teamIdCache = new Map;
|
|
@@ -106067,7 +106681,13 @@ function createLinearResolvers(input) {
|
|
|
106067
106681
|
if (include.length === 0)
|
|
106068
106682
|
return [];
|
|
106069
106683
|
const hasCommentMarker = include.some((m) => m.type === "comment");
|
|
106070
|
-
const spec = {
|
|
106684
|
+
const spec = {
|
|
106685
|
+
team,
|
|
106686
|
+
assignee,
|
|
106687
|
+
include,
|
|
106688
|
+
exclude: excl,
|
|
106689
|
+
...ticketNumbers.length > 0 ? { numbers: ticketNumbers } : {}
|
|
106690
|
+
};
|
|
106071
106691
|
const fetched = await fetchOpenIssues(apiKey, spec, hasCommentMarker ? { includeComments: true } : undefined);
|
|
106072
106692
|
if (!hasCommentMarker)
|
|
106073
106693
|
return fetched;
|
|
@@ -106086,7 +106706,7 @@ function createLinearResolvers(input) {
|
|
|
106086
106706
|
resolveLabelIdForTeam
|
|
106087
106707
|
};
|
|
106088
106708
|
}
|
|
106089
|
-
async function fetchDoneCandidatesWith(apiKey, team, _assignee, indicators) {
|
|
106709
|
+
async function fetchDoneCandidatesWith(apiKey, team, _assignee, indicators, ticketNumbers) {
|
|
106090
106710
|
const getIndicators = [
|
|
106091
106711
|
indicators.getTodo,
|
|
106092
106712
|
indicators.getInProgress,
|
|
@@ -106109,7 +106729,8 @@ async function fetchDoneCandidatesWith(apiKey, team, _assignee, indicators) {
|
|
|
106109
106729
|
team,
|
|
106110
106730
|
anyAssignee: true,
|
|
106111
106731
|
include,
|
|
106112
|
-
exclude: []
|
|
106732
|
+
exclude: [],
|
|
106733
|
+
...ticketNumbers && ticketNumbers.length > 0 ? { numbers: ticketNumbers } : {}
|
|
106113
106734
|
});
|
|
106114
106735
|
for (const issue2 of issues) {
|
|
106115
106736
|
if (!seen.has(issue2.id)) {
|
|
@@ -106598,7 +107219,7 @@ async function fetchPrReviewState(prUrl, cmdRunner, projectRoot, onLog) {
|
|
|
106598
107219
|
reviewRequests(first:5){nodes{requestedReviewer{... on User{login}}}}
|
|
106599
107220
|
latestReviews(first:5){nodes{author{login} state submittedAt}}
|
|
106600
107221
|
reviewThreads(first:50){nodes{
|
|
106601
|
-
isResolved path line
|
|
107222
|
+
isResolved subjectType path line
|
|
106602
107223
|
comments(first:20){nodes{body author{login} createdAt url}}
|
|
106603
107224
|
}}
|
|
106604
107225
|
}
|
|
@@ -106631,6 +107252,7 @@ async function fetchPrReviewState(prUrl, cmdRunner, projectRoot, onLog) {
|
|
|
106631
107252
|
approved: pr2.reviewDecision === "APPROVED",
|
|
106632
107253
|
threads: (pr2.reviewThreads?.nodes ?? []).map((t) => ({
|
|
106633
107254
|
isResolved: t.isResolved,
|
|
107255
|
+
isFileLevel: t.subjectType === "FILE",
|
|
106634
107256
|
...t.path ? { path: t.path } : {},
|
|
106635
107257
|
...t.line != null ? { line: t.line } : {},
|
|
106636
107258
|
comments: t.comments.nodes.map((c) => ({
|
|
@@ -106697,7 +107319,7 @@ async function scanCodeReview(issue2, prUrl, lastRalphPickup, deps) {
|
|
|
106697
107319
|
const effectiveLastHandled = lastRalphPickup && lastHandled ? lastRalphPickup > lastHandled ? lastRalphPickup : lastHandled : lastRalphPickup ?? lastHandled;
|
|
106698
107320
|
if (!effectiveLastHandled || newestReviewerActivity > effectiveLastHandled) {
|
|
106699
107321
|
const body = unresolved.map((t) => {
|
|
106700
|
-
const head3 = t.path ? `_${t.path}${t.line ? `:${t.line}` : ""}_` : "_(general)_";
|
|
107322
|
+
const head3 = t.path ? t.isFileLevel ? `_${t.path} (whole file)_` : `_${t.path}${t.line ? `:${t.line}` : ""}_` : "_(general)_";
|
|
106701
107323
|
const lines = t.comments.map((c) => `> **${c.author ?? "reviewer"}** (${c.createdAt})
|
|
106702
107324
|
>
|
|
106703
107325
|
> ${c.body.trim().replace(/\n/g, `
|
|
@@ -106802,6 +107424,7 @@ function createMentionScanner(input) {
|
|
|
106802
107424
|
onLog,
|
|
106803
107425
|
diag,
|
|
106804
107426
|
cwdByChange,
|
|
107427
|
+
ticketNumbers,
|
|
106805
107428
|
stalePingedAt,
|
|
106806
107429
|
lastHandledReviewActivity,
|
|
106807
107430
|
resolvePrUrlForIssue
|
|
@@ -106817,6 +107440,7 @@ function createMentionScanner(input) {
|
|
|
106817
107440
|
candidates = await fetchMentionScanIssues(apiKey, {
|
|
106818
107441
|
team,
|
|
106819
107442
|
assignee,
|
|
107443
|
+
...ticketNumbers && ticketNumbers.length > 0 ? { numbers: ticketNumbers } : {},
|
|
106820
107444
|
indicators: {
|
|
106821
107445
|
...indicators.getTodo !== undefined ? { getTodo: indicators.getTodo } : {},
|
|
106822
107446
|
...indicators.getInProgress !== undefined ? { getInProgress: indicators.getInProgress } : {},
|
|
@@ -239295,9 +239919,9 @@ class $50c7aac9316f2948$export$2e2bcd8739ae039 {
|
|
|
239295
239919
|
|
|
239296
239920
|
class $55f71433a605c87d$export$2e2bcd8739ae039 {
|
|
239297
239921
|
process(glyphs, features = {}) {
|
|
239298
|
-
for (let
|
|
239299
|
-
let flags =
|
|
239300
|
-
for (let feature of
|
|
239922
|
+
for (let chain of this.morx.chains) {
|
|
239923
|
+
let flags = chain.defaultFlags;
|
|
239924
|
+
for (let feature of chain.features) {
|
|
239301
239925
|
let f2;
|
|
239302
239926
|
if (f2 = features[feature.featureType]) {
|
|
239303
239927
|
if (f2[feature.featureSetting]) {
|
|
@@ -239309,7 +239933,7 @@ class $55f71433a605c87d$export$2e2bcd8739ae039 {
|
|
|
239309
239933
|
}
|
|
239310
239934
|
}
|
|
239311
239935
|
}
|
|
239312
|
-
for (let subtable of
|
|
239936
|
+
for (let subtable of chain.subtables)
|
|
239313
239937
|
if (subtable.subFeatureFlags & flags)
|
|
239314
239938
|
this.processSubtable(subtable, glyphs);
|
|
239315
239939
|
}
|
|
@@ -239456,8 +240080,8 @@ class $55f71433a605c87d$export$2e2bcd8739ae039 {
|
|
|
239456
240080
|
}
|
|
239457
240081
|
getSupportedFeatures() {
|
|
239458
240082
|
let features = [];
|
|
239459
|
-
for (let
|
|
239460
|
-
for (let feature of
|
|
240083
|
+
for (let chain of this.morx.chains)
|
|
240084
|
+
for (let feature of chain.features)
|
|
239461
240085
|
features.push([
|
|
239462
240086
|
feature.featureType,
|
|
239463
240087
|
feature.featureSetting
|
|
@@ -239471,9 +240095,9 @@ class $55f71433a605c87d$export$2e2bcd8739ae039 {
|
|
|
239471
240095
|
}
|
|
239472
240096
|
generateInputCache() {
|
|
239473
240097
|
this.inputCache = {};
|
|
239474
|
-
for (let
|
|
239475
|
-
let flags =
|
|
239476
|
-
for (let subtable of
|
|
240098
|
+
for (let chain of this.morx.chains) {
|
|
240099
|
+
let flags = chain.defaultFlags;
|
|
240100
|
+
for (let subtable of chain.subtables)
|
|
239477
240101
|
if (subtable.subFeatureFlags & flags)
|
|
239478
240102
|
this.generateInputsForSubtable(subtable);
|
|
239479
240103
|
}
|
|
@@ -260556,6 +261180,7 @@ function buildAgentCoordinator(input) {
|
|
|
260556
261180
|
const indicators = mergeIndicators(cfg.linear.indicators, args.indicators);
|
|
260557
261181
|
const team = args.linearTeam || cfg.linear.team;
|
|
260558
261182
|
const assignee = args.linearAssignee || cfg.linear.assignee;
|
|
261183
|
+
const ticketNumbers = resolveTicketNumbers(args.ticketTokens, team);
|
|
260559
261184
|
const excludeFromTodo = unionMarkers(indicators.setDone, indicators.setError);
|
|
260560
261185
|
const gitRunner = input.runners?.git ?? bunGitRunner;
|
|
260561
261186
|
const cmdRunner = input.runners?.cmd ?? bunCmdRunner;
|
|
@@ -260587,7 +261212,19 @@ function buildAgentCoordinator(input) {
|
|
|
260587
261212
|
}
|
|
260588
261213
|
return code;
|
|
260589
261214
|
});
|
|
260590
|
-
const resolvers = createLinearResolvers({
|
|
261215
|
+
const resolvers = createLinearResolvers({
|
|
261216
|
+
apiKey,
|
|
261217
|
+
team,
|
|
261218
|
+
assignee,
|
|
261219
|
+
diag,
|
|
261220
|
+
...ticketNumbers.length > 0 ? { ticketNumbers } : {}
|
|
261221
|
+
});
|
|
261222
|
+
if (ticketNumbers.length > 0) {
|
|
261223
|
+
const hasGetIndicator = [indicators.getTodo, indicators.getInProgress].some((ind) => ind && ind.filter.length > 0);
|
|
261224
|
+
if (!hasGetIndicator) {
|
|
261225
|
+
diag("ticket", `! --ticket set (${ticketNumbers.join(", ")}) but no getTodo/getInProgress indicator is configured \u2014 nothing will be picked up`, "yellow");
|
|
261226
|
+
}
|
|
261227
|
+
}
|
|
260591
261228
|
const prDiscovery = createPrDiscovery({
|
|
260592
261229
|
apiKey,
|
|
260593
261230
|
projectRoot,
|
|
@@ -260623,6 +261260,7 @@ function buildAgentCoordinator(input) {
|
|
|
260623
261260
|
onLog,
|
|
260624
261261
|
diag,
|
|
260625
261262
|
cwdByChange,
|
|
261263
|
+
...ticketNumbers.length > 0 ? { ticketNumbers } : {},
|
|
260626
261264
|
stalePingedAt,
|
|
260627
261265
|
lastHandledReviewActivity,
|
|
260628
261266
|
resolvePrUrlForIssue: prDiscovery.resolvePrUrlForIssue
|
|
@@ -260719,7 +261357,7 @@ function buildAgentCoordinator(input) {
|
|
|
260719
261357
|
fetchTodo: () => resolvers.fetchByGet(indicators.getTodo, excludeFromTodo),
|
|
260720
261358
|
fetchInProgress: () => resolvers.fetchByGet(indicators.getInProgress, unionMarkers(indicators.setError)),
|
|
260721
261359
|
fetchMentions,
|
|
260722
|
-
fetchDoneCandidates: () => fetchDoneCandidatesWith(apiKey, team, assignee, indicators),
|
|
261360
|
+
fetchDoneCandidates: () => fetchDoneCandidatesWith(apiKey, team, assignee, indicators, ticketNumbers.length > 0 ? ticketNumbers : undefined),
|
|
260723
261361
|
prepare: prep.prepare,
|
|
260724
261362
|
prepareTaskForTrigger: prep.prepareTaskForTrigger,
|
|
260725
261363
|
spawnWorker,
|
|
@@ -260805,6 +261443,7 @@ var init_wire = __esm(() => {
|
|
|
260805
261443
|
init_indicators();
|
|
260806
261444
|
init_task_bodies();
|
|
260807
261445
|
init_linear_resolvers();
|
|
261446
|
+
init_linear_client();
|
|
260808
261447
|
init_prepare();
|
|
260809
261448
|
init_pr_discovery();
|
|
260810
261449
|
init_mention_scan();
|
|
@@ -260820,7 +261459,7 @@ import { dirname as dirname15 } from "path";
|
|
|
260820
261459
|
function createJsonLogFileSink(path) {
|
|
260821
261460
|
if (!path)
|
|
260822
261461
|
return { emit: () => {} };
|
|
260823
|
-
let
|
|
261462
|
+
let chain = (async () => {
|
|
260824
261463
|
try {
|
|
260825
261464
|
await mkdir12(dirname15(path), { recursive: true });
|
|
260826
261465
|
await Bun.write(path, "");
|
|
@@ -260830,7 +261469,7 @@ function createJsonLogFileSink(path) {
|
|
|
260830
261469
|
emit(event) {
|
|
260831
261470
|
const line = JSON.stringify({ ts: Date.now(), v: VERSION, ...event }) + `
|
|
260832
261471
|
`;
|
|
260833
|
-
|
|
261472
|
+
chain = chain.then(async () => {
|
|
260834
261473
|
try {
|
|
260835
261474
|
await appendFile2(path, line);
|
|
260836
261475
|
} catch {}
|
|
@@ -262938,6 +263577,18 @@ var init_tmux = __esm(() => {
|
|
|
262938
263577
|
decoder = new TextDecoder;
|
|
262939
263578
|
});
|
|
262940
263579
|
|
|
263580
|
+
// apps/agent/src/sort/compare.ts
|
|
263581
|
+
function chain(...comparators) {
|
|
263582
|
+
return (a, b2) => {
|
|
263583
|
+
for (const c of comparators) {
|
|
263584
|
+
const r = c(a, b2);
|
|
263585
|
+
if (r !== 0)
|
|
263586
|
+
return r;
|
|
263587
|
+
}
|
|
263588
|
+
return 0;
|
|
263589
|
+
};
|
|
263590
|
+
}
|
|
263591
|
+
|
|
262941
263592
|
// apps/agent/src/list-sort.ts
|
|
262942
263593
|
function assignTier(status) {
|
|
262943
263594
|
if (status === null || status.kind === "error")
|
|
@@ -262960,7 +263611,7 @@ function createdAtOf(status) {
|
|
|
262960
263611
|
return "";
|
|
262961
263612
|
}
|
|
262962
263613
|
function sortRows(rows) {
|
|
262963
|
-
const cmp = chain((a, b2) => assignTier(a.status) - assignTier(b2.status), (a, b2) => {
|
|
263614
|
+
const cmp = chain((a, b2) => assignTier(a.status) - assignTier(b2.status), (a, b2) => a.bucketOrder - b2.bucketOrder, (a, b2) => {
|
|
262964
263615
|
const ia = a.issueCreatedAt;
|
|
262965
263616
|
const ib = b2.issueCreatedAt;
|
|
262966
263617
|
if (ia === ib)
|
|
@@ -262976,7 +263627,7 @@ function sortRows(rows) {
|
|
|
262976
263627
|
if (ca === cb)
|
|
262977
263628
|
return 0;
|
|
262978
263629
|
return ca < cb ? -1 : 1;
|
|
262979
|
-
}, (a, b2) => a.
|
|
263630
|
+
}, (a, b2) => a.identifier.localeCompare(b2.identifier));
|
|
262980
263631
|
return [...rows].sort(cmp);
|
|
262981
263632
|
}
|
|
262982
263633
|
var init_list_sort = () => {};
|
|
@@ -263025,7 +263676,8 @@ __export(exports_list, {
|
|
|
263025
263676
|
formatReviewCell: () => formatReviewCell,
|
|
263026
263677
|
formatPrStatusMarker: () => formatPrStatusMarker,
|
|
263027
263678
|
formatBlockedCell: () => formatBlockedCell,
|
|
263028
|
-
buildBuckets: () => buildBuckets
|
|
263679
|
+
buildBuckets: () => buildBuckets,
|
|
263680
|
+
backlogRankByIssueId: () => backlogRankByIssueId
|
|
263029
263681
|
});
|
|
263030
263682
|
import { join as join36 } from "path";
|
|
263031
263683
|
function countTaskItems(content) {
|
|
@@ -263127,14 +263779,15 @@ function buildBuckets(indicators) {
|
|
|
263127
263779
|
{ label: "auto-merge", indicator: indicators.getAutoMerge, exclude: [] }
|
|
263128
263780
|
];
|
|
263129
263781
|
}
|
|
263130
|
-
async function fetchBucketIssues(apiKey, bucket, team, assignee) {
|
|
263782
|
+
async function fetchBucketIssues(apiKey, bucket, team, assignee, ticketNumbers) {
|
|
263131
263783
|
if (!bucket.indicator || bucket.indicator.filter.length === 0)
|
|
263132
263784
|
return [];
|
|
263133
263785
|
const spec = {
|
|
263134
263786
|
team,
|
|
263135
263787
|
assignee,
|
|
263136
263788
|
include: bucket.indicator.filter,
|
|
263137
|
-
exclude: bucket.exclude
|
|
263789
|
+
exclude: bucket.exclude,
|
|
263790
|
+
...ticketNumbers.length > 0 ? { numbers: ticketNumbers } : {}
|
|
263138
263791
|
};
|
|
263139
263792
|
return fetchOpenIssues(apiKey, spec);
|
|
263140
263793
|
}
|
|
@@ -263178,13 +263831,19 @@ function formatPrStatusMarker(status, failedCheckNames) {
|
|
|
263178
263831
|
return "ok";
|
|
263179
263832
|
return parts.join(" ");
|
|
263180
263833
|
}
|
|
263181
|
-
|
|
263834
|
+
function backlogRankByIssueId(issues) {
|
|
263835
|
+
const ordered = orderIssuesHierarchically(issues.map((issue2) => linearIssueToOrderable(issue2)));
|
|
263836
|
+
const rankById = new Map;
|
|
263837
|
+
ordered.forEach((o, i) => rankById.set(o.id, i));
|
|
263838
|
+
return rankById;
|
|
263839
|
+
}
|
|
263840
|
+
async function fetchAndPrintLinear(apiKey, buckets, team, assignee, cwd2, runner, ignoreCiChecks = [], checks3 = false, review = false, ticketNumbers = []) {
|
|
263182
263841
|
const bucketResults = await Promise.all(buckets.map(async (bucket) => {
|
|
263183
263842
|
if (!bucket.indicator || bucket.indicator.filter.length === 0) {
|
|
263184
263843
|
return { bucket, issues: [], error: null };
|
|
263185
263844
|
}
|
|
263186
263845
|
try {
|
|
263187
|
-
const issues = await fetchBucketIssues(apiKey, bucket, team, assignee);
|
|
263846
|
+
const issues = await fetchBucketIssues(apiKey, bucket, team, assignee, ticketNumbers);
|
|
263188
263847
|
return { bucket, issues, error: null };
|
|
263189
263848
|
} catch (err) {
|
|
263190
263849
|
return {
|
|
@@ -263202,16 +263861,17 @@ ${bucket.label}: error fetching from Linear \u2014 ${error48}
|
|
|
263202
263861
|
}
|
|
263203
263862
|
}
|
|
263204
263863
|
const seen = new Map;
|
|
263205
|
-
|
|
263864
|
+
const issueById = new Map;
|
|
263206
263865
|
for (const { bucket, issues } of bucketResults) {
|
|
263207
263866
|
for (const issue2 of issues) {
|
|
263208
263867
|
if (seen.has(issue2.id))
|
|
263209
263868
|
continue;
|
|
263869
|
+
issueById.set(issue2.id, issue2);
|
|
263210
263870
|
seen.set(issue2.id, {
|
|
263211
263871
|
issueId: issue2.id,
|
|
263212
263872
|
identifier: issue2.identifier,
|
|
263213
263873
|
status: null,
|
|
263214
|
-
bucketOrder:
|
|
263874
|
+
bucketOrder: 0,
|
|
263215
263875
|
issueCreatedAt: issue2.createdAt,
|
|
263216
263876
|
bucketLabel: bucket.label,
|
|
263217
263877
|
stateName: issue2.state.name,
|
|
@@ -263222,6 +263882,9 @@ ${bucket.label}: error fetching from Linear \u2014 ${error48}
|
|
|
263222
263882
|
}
|
|
263223
263883
|
}
|
|
263224
263884
|
const rows = [...seen.values()];
|
|
263885
|
+
const rankById = backlogRankByIssueId([...issueById.values()]);
|
|
263886
|
+
for (const row of rows)
|
|
263887
|
+
row.bucketOrder = rankById.get(row.issueId) ?? 0;
|
|
263225
263888
|
try {
|
|
263226
263889
|
const attachmentsByIssue = await fetchAttachmentsForIssues(apiKey, rows.map((r) => r.issueId));
|
|
263227
263890
|
for (const row of rows) {
|
|
@@ -263338,6 +264001,15 @@ Linear: LINEAR_API_KEY not set \u2014 cannot fetch tickets. Configured buckets:
|
|
|
263338
264001
|
}
|
|
263339
264002
|
return;
|
|
263340
264003
|
}
|
|
264004
|
+
let ticketNumbers = [];
|
|
264005
|
+
try {
|
|
264006
|
+
ticketNumbers = resolveTicketNumbers(input.ticketTokens ?? [], team);
|
|
264007
|
+
} catch (err) {
|
|
264008
|
+
process.stderr.write(`Error: ${formatTicketError(err)}
|
|
264009
|
+
`);
|
|
264010
|
+
process.exitCode = 1;
|
|
264011
|
+
return;
|
|
264012
|
+
}
|
|
263341
264013
|
if (team)
|
|
263342
264014
|
process.stdout.write(`
|
|
263343
264015
|
team: ${team}
|
|
@@ -263345,13 +264017,21 @@ team: ${team}
|
|
|
263345
264017
|
if (assignee)
|
|
263346
264018
|
process.stdout.write(`assignee: ${assignee}
|
|
263347
264019
|
`);
|
|
263348
|
-
|
|
264020
|
+
if (ticketNumbers.length > 0)
|
|
264021
|
+
process.stdout.write(`ticket: ${ticketNumbers.join(", ")}
|
|
264022
|
+
`);
|
|
264023
|
+
await fetchAndPrintLinear(apiKey, buckets, team, assignee, projectRoot, localCmdRunner, cfg.ignoreCiChecks, input.checks, input.review, ticketNumbers);
|
|
263349
264024
|
}
|
|
263350
264025
|
function normalizeIdentifier(input) {
|
|
263351
|
-
|
|
263352
|
-
|
|
264026
|
+
let parsed;
|
|
264027
|
+
try {
|
|
264028
|
+
parsed = parseTicketIdentifier(input);
|
|
264029
|
+
} catch {
|
|
264030
|
+
return null;
|
|
264031
|
+
}
|
|
264032
|
+
if (parsed.teamKey === null)
|
|
263353
264033
|
return null;
|
|
263354
|
-
return `${
|
|
264034
|
+
return `${parsed.teamKey}-${parsed.number}`;
|
|
263355
264035
|
}
|
|
263356
264036
|
async function fetchIssueByIdentifier(apiKey, identifier) {
|
|
263357
264037
|
const match = identifier.match(/^([A-Z]+)-(\d+)$/);
|
|
@@ -263501,6 +264181,8 @@ var init_list = __esm(() => {
|
|
|
263501
264181
|
init_pr_status();
|
|
263502
264182
|
init_pr_url();
|
|
263503
264183
|
init_list_sort();
|
|
264184
|
+
init_ordering();
|
|
264185
|
+
init_queue_order();
|
|
263504
264186
|
init_ci();
|
|
263505
264187
|
init_linear_client();
|
|
263506
264188
|
init_indicators();
|
|
@@ -263810,7 +264492,8 @@ async function main3(argv) {
|
|
|
263810
264492
|
debug: args.debug,
|
|
263811
264493
|
name: args.name,
|
|
263812
264494
|
checks: args.checks,
|
|
263813
|
-
review: args.review
|
|
264495
|
+
review: args.review,
|
|
264496
|
+
ticketTokens: args.ticketTokens
|
|
263814
264497
|
});
|
|
263815
264498
|
});
|
|
263816
264499
|
return typeof process.exitCode === "number" ? process.exitCode : 0;
|
|
@@ -263830,6 +264513,19 @@ async function main3(argv) {
|
|
|
263830
264513
|
`);
|
|
263831
264514
|
return 0;
|
|
263832
264515
|
}
|
|
264516
|
+
if (args.ticketTokens.length > 0) {
|
|
264517
|
+
const { loadRalphyConfig: loadRalphyConfig2 } = await Promise.resolve().then(() => (init_config(), exports_config));
|
|
264518
|
+
const { resolveTicketNumbers: resolveTicketNumbers2, formatTicketError: formatTicketError2 } = await Promise.resolve().then(() => (init_linear_client(), exports_linear_client));
|
|
264519
|
+
const cfg = await loadRalphyConfig2(projectRoot);
|
|
264520
|
+
const team = args.linearTeam || cfg.linear.team;
|
|
264521
|
+
try {
|
|
264522
|
+
resolveTicketNumbers2(args.ticketTokens, team);
|
|
264523
|
+
} catch (err) {
|
|
264524
|
+
process.stderr.write(formatTicketError2(err) + `
|
|
264525
|
+
`);
|
|
264526
|
+
return 1;
|
|
264527
|
+
}
|
|
264528
|
+
}
|
|
263833
264529
|
await mkdir15(statesDir, { recursive: true });
|
|
263834
264530
|
await mkdir15(tasksDir, { recursive: true });
|
|
263835
264531
|
await mkdir15(join39(projectRoot, ".ralph"), { recursive: true });
|