@neriros/ralphy 3.10.3 → 3.10.5

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.
Files changed (2) hide show
  1. package/dist/shell/index.js +941 -110
  2. package/package.json +1 -1
@@ -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.3")
18932
- return "3.10.3";
18931
+ if ("3.10.5")
18932
+ return "3.10.5";
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
- var CURRENT_WORKFLOW_VERSION = 1, MarkerSchema, SET_INDICATOR_KEYS, GetIndicatorSchema, SetIndicatorSchema, IndicatorsSchema, ProjectSchema, CommandsSchema, DEFAULT_META_ONLY_FILES, BoundariesSchema, WorkflowConfigSchema;
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", [
@@ -80598,6 +80612,12 @@ var init_schema = __esm(() => {
80598
80612
  WorkflowConfigSchema = exports_external.object({
80599
80613
  version: exports_external.number().int().nonnegative().default(0),
80600
80614
  project: ProjectSchema,
80615
+ repo: exports_external.object({
80616
+ remote: exports_external.string().optional(),
80617
+ host: exports_external.string().optional(),
80618
+ owner: exports_external.string().optional(),
80619
+ name: exports_external.string().optional()
80620
+ }).strict().optional(),
80601
80621
  commands: CommandsSchema,
80602
80622
  rules: exports_external.array(exports_external.string()).default([]),
80603
80623
  boundaries: BoundariesSchema,
@@ -80629,9 +80649,9 @@ var init_schema = __esm(() => {
80629
80649
  ignoreCiChecks: exports_external.array(exports_external.string()).default([]),
80630
80650
  engine: exports_external.enum(["claude", "codex"]).default("claude"),
80631
80651
  model: exports_external.enum(["haiku", "sonnet", "opus"]).default("opus"),
80632
- linear: exports_external.object({
80652
+ linear: exports_external.preprocess(foldLegacyAssignee, exports_external.object({
80633
80653
  team: exports_external.string().optional(),
80634
- assignee: exports_external.string().optional(),
80654
+ filter: exports_external.string().default("assignee = me"),
80635
80655
  postComments: exports_external.boolean().default(true),
80636
80656
  updateEveryIterations: exports_external.number().int().nonnegative().default(10),
80637
80657
  mentionTrigger: exports_external.boolean().default(true),
@@ -80651,7 +80671,8 @@ var init_schema = __esm(() => {
80651
80671
  maxConfirmationRounds: 3
80652
80672
  }),
80653
80673
  indicators: IndicatorsSchema.default({})
80654
- }).strict().default({
80674
+ }).strict()).default({
80675
+ filter: "assignee = me",
80655
80676
  postComments: true,
80656
80677
  updateEveryIterations: 10,
80657
80678
  mentionTrigger: true,
@@ -80712,8 +80733,9 @@ var init_schema = __esm(() => {
80712
80733
  advanceMergedToDone: false
80713
80734
  }),
80714
80735
  metaPrompt: exports_external.object({
80715
- enabled: exports_external.boolean().default(true)
80716
- }).strict().default({ enabled: true }),
80736
+ enabled: exports_external.boolean().default(true),
80737
+ effort: exports_external.enum(["auto", "light", "standard", "heavy"]).default("auto")
80738
+ }).strict().default({ enabled: true, effort: "auto" }),
80717
80739
  openspec: exports_external.object({
80718
80740
  reviewPhase: exports_external.object({
80719
80741
  enabled: exports_external.boolean().default(false),
@@ -80739,7 +80761,7 @@ var init_schema = __esm(() => {
80739
80761
  var FRONTMATTER_RE, DEFAULT_WORKFLOW_MD = `---
80740
80762
  # WORKFLOW.md schema version \u2014 managed by \`ralphy init\`. When a newer version
80741
80763
  # ships, re-running init migrates this file and fills in the new settings.
80742
- version: 1
80764
+ version: 2
80743
80765
 
80744
80766
  project:
80745
80767
  name: ralphy
@@ -80803,6 +80825,7 @@ preExistingErrorCheck:
80803
80825
  outputCharLimit: 4000
80804
80826
 
80805
80827
  linear:
80828
+ filter: assignee = me
80806
80829
  postComments: true
80807
80830
  updateEveryIterations: 10
80808
80831
  mentionTrigger: true
@@ -81049,7 +81072,7 @@ function modelOptionValues() {
81049
81072
  const field = findField("model");
81050
81073
  return field && field.spec.kind === "select" ? field.spec.options.map((o) => o.value) : [];
81051
81074
  }
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;
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;
81053
81076
  var init_fields = __esm(() => {
81054
81077
  PROJECT_NAME = {
81055
81078
  id: "project.name",
@@ -81061,19 +81084,26 @@ var init_fields = __esm(() => {
81061
81084
  id: "linear.team",
81062
81085
  label: "Linear team key",
81063
81086
  hint: "e.g. ENG \u2014 leave blank to match all teams",
81064
- description: "Only pick up issues from this Linear team, given by its key (e.g. ENG). Leave blank to watch every team.",
81087
+ 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
81088
  emptyLabel: "all teams",
81066
81089
  spec: { kind: "text" }
81067
81090
  };
81068
- LINEAR_ASSIGNEE = {
81069
- id: "linear.assignee",
81070
- label: "Linear assignee",
81071
- hint: "user id, email, 'me', 'any', or 'unassigned' \u2014 blank means unassigned",
81072
- description: "Which issues to pick up by assignee. Use a Linear user id, email, or 'me' for a specific person; 'any' to pick up issues regardless of who they're assigned to; 'unassigned' (or blank) to pick up only unassigned issues.",
81073
- emptyLabel: "unassigned",
81074
- spec: { kind: "text" }
81091
+ REPO_LINK = {
81092
+ id: "repo.link",
81093
+ label: "Link this repository to the team?",
81094
+ 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.",
81095
+ spec: yes(),
81096
+ when: (answers) => typeof answers["repo.name"] === "string" && answers["repo.name"] !== ""
81075
81097
  };
81076
- QUICK_FIELDS = [PROJECT_NAME, LINEAR_TEAM, LINEAR_ASSIGNEE];
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" }
81105
+ };
81106
+ QUICK_FIELDS = [PROJECT_NAME, LINEAR_TEAM, REPO_LINK, LINEAR_FILTER];
81077
81107
  CUSTOMIZED_FIELDS = [
81078
81108
  PROJECT_NAME,
81079
81109
  {
@@ -81321,7 +81351,8 @@ var init_fields = __esm(() => {
81321
81351
  spec: { kind: "list", placeholder: "dist/**" }
81322
81352
  },
81323
81353
  LINEAR_TEAM,
81324
- LINEAR_ASSIGNEE,
81354
+ REPO_LINK,
81355
+ LINEAR_FILTER,
81325
81356
  {
81326
81357
  id: "linear.postComments",
81327
81358
  label: "Post progress comments on the Linear issue?",
@@ -81464,6 +81495,21 @@ var init_fields = __esm(() => {
81464
81495
  description: "Add Ralphy's task-level 'meta-prompt' layer (extra framing instructions) to each phase. Leave on unless you want raw prompts.",
81465
81496
  spec: yes()
81466
81497
  },
81498
+ {
81499
+ id: "metaPrompt.effort",
81500
+ label: "Per-ticket effort tier",
81501
+ 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.",
81502
+ spec: {
81503
+ kind: "select",
81504
+ options: [
81505
+ { label: "auto", value: "auto" },
81506
+ { label: "light", value: "light" },
81507
+ { label: "standard", value: "standard" },
81508
+ { label: "heavy", value: "heavy" }
81509
+ ]
81510
+ },
81511
+ when: isOn("metaPrompt.enabled")
81512
+ },
81467
81513
  {
81468
81514
  id: "openspec.reviewPhase.enabled",
81469
81515
  label: "Enable the OpenSpec review phase?",
@@ -81695,6 +81741,34 @@ function describeApprovalMarker(indicator) {
81695
81741
  return `${phrases.slice(0, -1).join(", ")}, or ${phrases[phrases.length - 1]}`;
81696
81742
  }
81697
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
+
81698
81772
  // packages/workflow/src/workflow.ts
81699
81773
  var exports_workflow = {};
81700
81774
  __export(exports_workflow, {
@@ -81703,6 +81777,7 @@ __export(exports_workflow, {
81703
81777
  renderWorkflowPrompt: () => renderWorkflowPrompt,
81704
81778
  renderTemplate: () => renderTemplate,
81705
81779
  parseWorkflow: () => parseWorkflow,
81780
+ parseLinearFilter: () => parseLinearFilter,
81706
81781
  matchesIndicator: () => matchesIndicator,
81707
81782
  loadWorkflow: () => loadWorkflow,
81708
81783
  ensureWorkflow: () => ensureWorkflow,
@@ -81866,9 +81941,55 @@ var init_workflow = __esm(() => {
81866
81941
  init_wizard();
81867
81942
  init_schema();
81868
81943
  init_default();
81944
+ init_linear_filter();
81869
81945
  import_yaml2 = __toESM(require_dist(), 1);
81870
81946
  });
81871
81947
 
81948
+ // packages/core/src/repo/index.ts
81949
+ function parseRepoIdentity(remoteUrl) {
81950
+ const remote = remoteUrl.trim();
81951
+ if (!remote)
81952
+ return null;
81953
+ const match = remote.includes("://") ? URL_RE.exec(remote) : SCP_RE.exec(remote);
81954
+ if (!match)
81955
+ return null;
81956
+ const host = match[1];
81957
+ let path = match[2];
81958
+ if (!host || !path)
81959
+ return null;
81960
+ path = path.replace(/\/+$/, "").replace(/\.git$/i, "").replace(/\/+$/, "");
81961
+ const segments = path.split("/").filter((segment) => segment.length > 0);
81962
+ if (segments.length < 2)
81963
+ return null;
81964
+ const name = segments[segments.length - 1];
81965
+ const owner = segments.slice(0, -1).join("/");
81966
+ if (!owner || !name)
81967
+ return null;
81968
+ return { remote, host, owner, name };
81969
+ }
81970
+ async function detectRepoIdentity(cwd2) {
81971
+ try {
81972
+ const proc = Bun.spawn({
81973
+ cmd: ["git", "remote", "get-url", "origin"],
81974
+ ...cwd2 ? { cwd: cwd2 } : {},
81975
+ stdout: "pipe",
81976
+ stderr: "ignore",
81977
+ timeout: GIT_DETECT_TIMEOUT_MS
81978
+ });
81979
+ const [stdout, exitCode] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
81980
+ if (exitCode !== 0)
81981
+ return null;
81982
+ return parseRepoIdentity(stdout.trim());
81983
+ } catch {
81984
+ return null;
81985
+ }
81986
+ }
81987
+ var GIT_DETECT_TIMEOUT_MS = 5000, SCP_RE, URL_RE;
81988
+ var init_repo = __esm(() => {
81989
+ SCP_RE = /^(?:[^@/]+@)?([^/:]+):(.+)$/;
81990
+ URL_RE = /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\/(?:[^@/]+@)?([^/:]+)(?::\d+)?\/(.+)$/;
81991
+ });
81992
+
81872
81993
  // node_modules/.bun/react@18.3.1/node_modules/react/cjs/react-jsx-dev-runtime.development.js
81873
81994
  var require_react_jsx_dev_runtime_development = __commonJS((exports) => {
81874
81995
  var React10 = __toESM(require_react());
@@ -82787,6 +82908,12 @@ function buildFromAnswers(mode, answers, build = buildWorkflowMarkdown) {
82787
82908
  values2["linear.indicators"] = map3;
82788
82909
  }
82789
82910
  }
82911
+ const linkRepo = values2[REPO_LINK_FIELD_ID] === true;
82912
+ delete values2[REPO_LINK_FIELD_ID];
82913
+ if (!linkRepo) {
82914
+ for (const id of REPO_ANSWER_IDS)
82915
+ delete values2[id];
82916
+ }
82790
82917
  let bodyOverride;
82791
82918
  if (PROMPT_BODY_FIELD_ID in values2) {
82792
82919
  const body = values2[PROMPT_BODY_FIELD_ID];
@@ -82868,7 +82995,8 @@ function SetupWizard({
82868
82995
  initialValues,
82869
82996
  buildMarkdown,
82870
82997
  onlyFields,
82871
- initialBody
82998
+ initialBody,
82999
+ detectedRepo
82872
83000
  }) {
82873
83001
  const { exit } = use_app_default();
82874
83002
  const startValues = initialValues ?? {};
@@ -82944,7 +83072,15 @@ function SetupWizard({
82944
83072
  setIndex(target);
82945
83073
  initEditing(fieldsFor(mode, source)[target], source);
82946
83074
  };
82947
- const valuesToWrite = (source) => onlyFields ? Object.fromEntries(Object.entries(source).filter(([id]) => onlyFields.includes(id))) : source;
83075
+ const valuesToWrite = (source) => {
83076
+ if (!onlyFields)
83077
+ return source;
83078
+ const allowed = new Set(onlyFields);
83079
+ if (allowed.has(REPO_LINK_FIELD_ID))
83080
+ for (const id of REPO_ANSWER_IDS)
83081
+ allowed.add(id);
83082
+ return Object.fromEntries(Object.entries(source).filter(([id]) => allowed.has(id)));
83083
+ };
82948
83084
  const advance = (source) => {
82949
83085
  const nextFields = fieldsFor(mode, source);
82950
83086
  if (index >= nextFields.length - 1) {
@@ -83152,6 +83288,22 @@ ${draft.slice(at2)}`, at2 + 1);
83152
83288
  marginTop: 1,
83153
83289
  flexDirection: "column",
83154
83290
  children: [
83291
+ field.id === REPO_LINK_FIELD_ID && detectedRepo ? /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
83292
+ children: [
83293
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
83294
+ dimColor: true,
83295
+ children: "Detected repo: "
83296
+ }, undefined, false, undefined, this),
83297
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
83298
+ color: "cyan",
83299
+ children: [
83300
+ detectedRepo.owner,
83301
+ "/",
83302
+ detectedRepo.name
83303
+ ]
83304
+ }, undefined, true, undefined, this)
83305
+ ]
83306
+ }, undefined, true, undefined, this) : null,
83155
83307
  /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
83156
83308
  children: [
83157
83309
  /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
@@ -83724,13 +83876,14 @@ function IndicatorBuilder({
83724
83876
  ]
83725
83877
  }, undefined, true, undefined, this);
83726
83878
  }
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;
83879
+ 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
83880
  var init_SetupWizard = __esm(async () => {
83729
83881
  init_wizard();
83730
83882
  init_fields();
83731
83883
  await init_build2();
83732
83884
  import_react22 = __toESM(require_react(), 1);
83733
83885
  jsx_dev_runtime = __toESM(require_jsx_dev_runtime(), 1);
83886
+ REPO_ANSWER_IDS = ["repo.remote", "repo.host", "repo.owner", "repo.name"];
83734
83887
  MODE_OPTIONS = [
83735
83888
  { label: "Quick \u2014 sensible defaults, only a few questions", value: "quick" },
83736
83889
  { label: "Permissive \u2014 defaults + auto-PR / auto-merge / CI auto-fix", value: "permissive" },
@@ -83856,6 +84009,16 @@ var init_migrations = __esm(() => {
83856
84009
  "openspec.reviewPhase.reviewerModel",
83857
84010
  "openspec.reviewPhase.reviewerContextStrategy"
83858
84011
  ]
84012
+ },
84013
+ {
84014
+ version: 2,
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.",
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"]
83859
84022
  }
83860
84023
  ];
83861
84024
  LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max2, migration) => Math.max(max2, migration.version), 0);
@@ -83868,6 +84031,18 @@ __export(exports_src, {
83868
84031
  maybeRunSetupWizard: () => maybeRunSetupWizard,
83869
84032
  main: () => main
83870
84033
  });
84034
+ function withDetectedRepo(initial2, repo) {
84035
+ if (!repo)
84036
+ return initial2;
84037
+ const values2 = { ...initial2 };
84038
+ values2["repo.remote"] = repo.remote;
84039
+ values2["repo.host"] = repo.host;
84040
+ values2["repo.owner"] = repo.owner;
84041
+ values2["repo.name"] = repo.name;
84042
+ if (!values2["project.name"])
84043
+ values2["project.name"] = repo.name;
84044
+ return values2;
84045
+ }
83871
84046
  function clearScreen2() {
83872
84047
  if (process.stdout.isTTY)
83873
84048
  process.stdout.write("\x1B[2J\x1B[3J\x1B[H");
@@ -83876,6 +84051,7 @@ async function runSetupWizard(projectRoot, options = {}) {
83876
84051
  let markdown = null;
83877
84052
  const buildMarkdown = options.existing ? (answers, bodyOverride) => applyAnswersToWorkflow(options.existing, answers, bodyOverride) : undefined;
83878
84053
  const initialBody = workflowBody(options.existing ?? DEFAULT_WORKFLOW_MD);
84054
+ const initialValues = withDetectedRepo(options.initialValues, options.detectedRepo);
83879
84055
  clearScreen2();
83880
84056
  const { waitUntilExit } = render_default(import_react23.createElement(SetupWizard, {
83881
84057
  onComplete: (md) => {
@@ -83886,8 +84062,9 @@ async function runSetupWizard(projectRoot, options = {}) {
83886
84062
  },
83887
84063
  initialBody,
83888
84064
  ...options.initialMode ? { initialMode: options.initialMode } : {},
83889
- ...options.initialValues ? { initialValues: options.initialValues } : {},
84065
+ ...initialValues ? { initialValues } : {},
83890
84066
  ...options.onlyFields ? { onlyFields: options.onlyFields } : {},
84067
+ ...options.detectedRepo ? { detectedRepo: { owner: options.detectedRepo.owner, name: options.detectedRepo.name } } : {},
83891
84068
  ...buildMarkdown ? { buildMarkdown } : {}
83892
84069
  }));
83893
84070
  await waitUntilExit();
@@ -83929,8 +84106,8 @@ function initialValuesFromConfig(config2) {
83929
84106
  values2["useWorktree"] = config2.useWorktree;
83930
84107
  if (config2.linear.team)
83931
84108
  values2["linear.team"] = config2.linear.team;
83932
- if (config2.linear.assignee)
83933
- values2["linear.assignee"] = config2.linear.assignee;
84109
+ if (config2.linear.filter)
84110
+ values2["linear.filter"] = config2.linear.filter;
83934
84111
  return values2;
83935
84112
  }
83936
84113
  async function promptEditOrExit() {
@@ -83971,10 +84148,12 @@ async function promptMigrate(fromVersion) {
83971
84148
  }
83972
84149
  async function editExisting(projectRoot, path, config2, onlyFields) {
83973
84150
  const existing = await Bun.file(path).text();
84151
+ const detectedRepo = await detectRepoIdentity(projectRoot);
83974
84152
  const wrote = await runSetupWizard(projectRoot, {
83975
84153
  existing,
83976
84154
  initialMode: "customized",
83977
84155
  initialValues: initialValuesFromConfig(config2),
84156
+ ...detectedRepo ? { detectedRepo } : {},
83978
84157
  ...onlyFields ? { onlyFields } : {}
83979
84158
  });
83980
84159
  process.stdout.write(wrote ? `
@@ -84010,7 +84189,8 @@ async function main(argv) {
84010
84189
  `);
84011
84190
  return 0;
84012
84191
  }
84013
- const wrote2 = await runSetupWizard(projectRoot);
84192
+ const detectedRepo2 = await detectRepoIdentity(projectRoot);
84193
+ const wrote2 = await runSetupWizard(projectRoot, detectedRepo2 ? { detectedRepo: detectedRepo2 } : {});
84014
84194
  process.stdout.write(wrote2 ? `
84015
84195
  \u2713 Recreated ${path}
84016
84196
  ` : `
@@ -84043,7 +84223,8 @@ Setup cancelled \u2014 no file written.
84043
84223
  `);
84044
84224
  return 0;
84045
84225
  }
84046
- const wrote = await runSetupWizard(projectRoot);
84226
+ const detectedRepo = await detectRepoIdentity(projectRoot);
84227
+ const wrote = await runSetupWizard(projectRoot, detectedRepo ? { detectedRepo } : {});
84047
84228
  process.stdout.write(wrote ? `
84048
84229
  \u2713 Created ${path}
84049
84230
  ` : `
@@ -84056,6 +84237,7 @@ var init_src4 = __esm(async () => {
84056
84237
  init_paths();
84057
84238
  init_workflow();
84058
84239
  init_wizard();
84240
+ init_repo();
84059
84241
  init_migrations();
84060
84242
  await __promiseAll([
84061
84243
  init_build2(),
@@ -99164,10 +99346,112 @@ var init_tasks_md = __esm(() => {
99164
99346
  ];
99165
99347
  });
99166
99348
 
99349
+ // packages/core/src/prompt/effort.ts
99350
+ function countUnchecked2(tasksContent) {
99351
+ const matches2 = tasksContent.match(/^\s*[-*]\s+\[ \]/gm);
99352
+ return matches2 ? matches2.length : 0;
99353
+ }
99354
+ function clamp2(value, min2, max2) {
99355
+ return Math.max(min2, Math.min(max2, value));
99356
+ }
99357
+ function detectEffort(state, options = {}) {
99358
+ if (options.override)
99359
+ return options.override;
99360
+ const prompt = (state.prompt ?? "").toLowerCase();
99361
+ let keywordScore = 0;
99362
+ for (const kw of HEAVY_KEYWORDS) {
99363
+ if (prompt.includes(kw))
99364
+ keywordScore += HEAVY_WEIGHT;
99365
+ }
99366
+ for (const kw of LIGHT_KEYWORDS) {
99367
+ if (prompt.includes(kw))
99368
+ keywordScore += LIGHT_WEIGHT;
99369
+ }
99370
+ keywordScore = clamp2(keywordScore, -KEYWORD_HIT_CAP, KEYWORD_HIT_CAP);
99371
+ let score = keywordScore;
99372
+ const hasHeavyKeyword = keywordScore > 0;
99373
+ if (prompt.length > 0 && prompt.length < SHORT_PROMPT_CHARS && !hasHeavyKeyword) {
99374
+ score -= 1;
99375
+ } else if (prompt.length > LONG_PROMPT_CHARS) {
99376
+ score += 1;
99377
+ }
99378
+ if (options.tasksContent) {
99379
+ const unchecked = countUnchecked2(options.tasksContent);
99380
+ if (unchecked > 0 && unchecked <= FEW_TASKS) {
99381
+ score -= 2;
99382
+ } else if (unchecked >= MANY_TASKS) {
99383
+ score += 2;
99384
+ }
99385
+ }
99386
+ if (score <= LIGHT_THRESHOLD)
99387
+ return "light";
99388
+ if (score >= HEAVY_THRESHOLD)
99389
+ return "heavy";
99390
+ return "standard";
99391
+ }
99392
+ 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;
99393
+ var init_effort = __esm(() => {
99394
+ HEAVY_KEYWORDS = [
99395
+ "migrate",
99396
+ "refactor",
99397
+ "redesign",
99398
+ "re-architect",
99399
+ "architecture",
99400
+ "rewrite",
99401
+ "overhaul",
99402
+ "breaking change",
99403
+ "investigate",
99404
+ "spike",
99405
+ "cross-cutting",
99406
+ "end-to-end"
99407
+ ];
99408
+ LIGHT_KEYWORDS = [
99409
+ "typo",
99410
+ "rename",
99411
+ "bump",
99412
+ "tweak",
99413
+ "wording",
99414
+ "copy",
99415
+ "comment",
99416
+ "lint",
99417
+ "docs",
99418
+ "one-liner",
99419
+ "revert",
99420
+ "whitespace"
99421
+ ];
99422
+ EFFORT_GUIDANCE = {
99423
+ light: [
99424
+ "This ticket looks **light**. Make the smallest correct change.",
99425
+ "- Skip research/design ceremony \u2014 go straight to the fix",
99426
+ "- Avoid speculative abstraction; do not expand scope",
99427
+ "- Aim to finish in as few iterations as possible"
99428
+ ].join(`
99429
+ `),
99430
+ standard: [
99431
+ "This ticket looks **standard**. Balance thoroughness with momentum.",
99432
+ "- Do enough investigation to be confident, but don't over-engineer",
99433
+ "- Keep changes focused on the stated scope",
99434
+ "- Verify with the project's lint and test gates before finishing"
99435
+ ].join(`
99436
+ `),
99437
+ heavy: [
99438
+ "This ticket looks **heavy**. Invest up front before changing code.",
99439
+ "- Research the affected areas and write a real design",
99440
+ "- Break the work into small, independently-verifiable tasks",
99441
+ "- Watch for cross-cutting impact and regressions as you go"
99442
+ ].join(`
99443
+ `)
99444
+ };
99445
+ });
99446
+
99167
99447
  // packages/core/src/prompt/meta-prompt.ts
99168
99448
  function buildMetaPrompt(state, phase, options = {}) {
99169
99449
  if (options.enabled === false)
99170
99450
  return "";
99451
+ const effort = detectEffort(state, {
99452
+ ...options.effort !== undefined ? { override: options.effort } : {},
99453
+ ...options.tasksContent !== undefined ? { tasksContent: options.tasksContent } : {}
99454
+ });
99171
99455
  let out = `---
99172
99456
 
99173
99457
  ## Task Context
@@ -99178,6 +99462,8 @@ function buildMetaPrompt(state, phase, options = {}) {
99178
99462
  out += `**Engine/Model:** ${state.engine} / ${state.model}
99179
99463
  `;
99180
99464
  out += `**Phase:** ${phase}
99465
+ `;
99466
+ out += `**Effort:** ${effort}
99181
99467
  `;
99182
99468
  out += `**Iteration:** ${state.iteration + 1}`;
99183
99469
  if (options.maxIterations && options.maxIterations > 0) {
@@ -99201,6 +99487,12 @@ function buildMetaPrompt(state, phase, options = {}) {
99201
99487
  `;
99202
99488
  out += PHASE_GUIDANCE[phase] + `
99203
99489
 
99490
+ `;
99491
+ out += `### Effort Guidance
99492
+
99493
+ `;
99494
+ out += EFFORT_GUIDANCE[effort] + `
99495
+
99204
99496
  `;
99205
99497
  const flags = [];
99206
99498
  if (options.useWorktree) {
@@ -99233,6 +99525,7 @@ function buildMetaPrompt(state, phase, options = {}) {
99233
99525
  }
99234
99526
  var PHASE_GUIDANCE;
99235
99527
  var init_meta_prompt = __esm(() => {
99528
+ init_effort();
99236
99529
  PHASE_GUIDANCE = {
99237
99530
  research: [
99238
99531
  "You are in the **research** phase. Your goal is to understand, not to implement.",
@@ -99706,6 +99999,7 @@ var init_loop = __esm(() => {
99706
99999
  init_tasks_md();
99707
100000
  init_phase();
99708
100001
  init_meta_prompt();
100002
+ init_effort();
99709
100003
  init_tasks_md();
99710
100004
  });
99711
100005
 
@@ -99986,7 +100280,10 @@ function useLoop(opts) {
99986
100280
  design: designContent,
99987
100281
  tasks: tasksContent
99988
100282
  });
99989
- const prompt = buildPhasePrompt(routedPhase, currentState, tasksDir, opts.reviewPhase, opts.metaPrompt);
100283
+ const prompt = buildPhasePrompt(routedPhase, currentState, tasksDir, opts.reviewPhase, {
100284
+ ...opts.metaPrompt,
100285
+ ...tasksContent !== null ? { tasksContent } : {}
100286
+ });
99990
100287
  const iterStart = new Date().toISOString();
99991
100288
  try {
99992
100289
  const controller = new AbortController;
@@ -101157,6 +101454,7 @@ async function parseAgentArgs(argv) {
101157
101454
  ...common2,
101158
101455
  mode: "agent",
101159
101456
  linearTeam: "",
101457
+ linearFilter: "",
101160
101458
  linearAssignee: "",
101161
101459
  pollInterval: 0,
101162
101460
  concurrency: 0,
@@ -101172,22 +101470,30 @@ async function parseAgentArgs(argv) {
101172
101470
  debug: false,
101173
101471
  noTmux: false,
101174
101472
  checks: false,
101175
- review: false
101473
+ review: false,
101474
+ ticketTokens: []
101176
101475
  };
101177
101476
  const state = emptyParseState();
101178
101477
  let expectLinearTeam = false;
101478
+ let expectLinearFilter = false;
101179
101479
  let expectLinearAssignee = false;
101180
101480
  let expectPollInterval = false;
101181
101481
  let expectConcurrency = false;
101182
101482
  let expectMaxTickets = false;
101183
101483
  let expectIndicator = false;
101184
101484
  let expectJsonLogFile = false;
101485
+ let expectTicket = false;
101185
101486
  for (const arg of argv) {
101186
101487
  if (expectLinearTeam) {
101187
101488
  result2.linearTeam = arg;
101188
101489
  expectLinearTeam = false;
101189
101490
  continue;
101190
101491
  }
101492
+ if (expectLinearFilter) {
101493
+ result2.linearFilter = arg;
101494
+ expectLinearFilter = false;
101495
+ continue;
101496
+ }
101191
101497
  if (expectLinearAssignee) {
101192
101498
  result2.linearAssignee = arg;
101193
101499
  expectLinearAssignee = false;
@@ -101219,12 +101525,23 @@ async function parseAgentArgs(argv) {
101219
101525
  expectJsonLogFile = false;
101220
101526
  continue;
101221
101527
  }
101528
+ if (expectTicket) {
101529
+ for (const token of arg.split(",").map((t) => t.trim())) {
101530
+ if (token)
101531
+ result2.ticketTokens.push(token);
101532
+ }
101533
+ expectTicket = false;
101534
+ continue;
101535
+ }
101222
101536
  if (parseCommonArg(arg, result2, state))
101223
101537
  continue;
101224
101538
  switch (arg) {
101225
101539
  case "--linear-team":
101226
101540
  expectLinearTeam = true;
101227
101541
  break;
101542
+ case "--linear-filter":
101543
+ expectLinearFilter = true;
101544
+ break;
101228
101545
  case "--linear-assignee":
101229
101546
  expectLinearAssignee = true;
101230
101547
  break;
@@ -101237,6 +101554,9 @@ async function parseAgentArgs(argv) {
101237
101554
  case "--max-tickets":
101238
101555
  expectMaxTickets = true;
101239
101556
  break;
101557
+ case "--ticket":
101558
+ expectTicket = true;
101559
+ break;
101240
101560
  case "--worktree":
101241
101561
  result2.worktree = true;
101242
101562
  break;
@@ -101346,7 +101666,8 @@ var init_cli2 = __esm(() => {
101346
101666
  " --log Log raw engine stream",
101347
101667
  " --verbose Verbose output",
101348
101668
  " --linear-team <key> Linear team key (e.g. ENG)",
101349
- " --linear-assignee <id> Filter by assignee (user id, email, or 'me')",
101669
+ " --linear-filter <expr> Global Linear filter (e.g. 'assignee = me', 'assignee = any')",
101670
+ " --linear-assignee <id> [deprecated] Filter by assignee; use --linear-filter instead",
101350
101671
  " --poll-interval <s> Seconds between Linear polls (default: 60)",
101351
101672
  " --concurrency <n> Max concurrent task loops (default: 1)",
101352
101673
  " --worktree Run each task in its own git worktree",
@@ -101361,6 +101682,7 @@ var init_cli2 = __esm(() => {
101361
101682
  " --stack-prs Base the PR on a blocker issue's open-PR head branch when present (needs --create-pr)",
101362
101683
  " --code-review Watch open tracked PRs for unresolved review comments",
101363
101684
  " --max-tickets <n> Stop picking up new issues after N have been started (0 = unlimited)",
101685
+ " --ticket <id> Restrict issue discovery to specific ticket(s); repeatable or comma-separated (e.g. RLF-208 or 208)",
101364
101686
  " --no-tmux Disable tmux session management; run agent in the foreground directly",
101365
101687
  " --no-pr-tracker Disable RLF-173 pr-tracker bail / recovery counter for this run",
101366
101688
  " --json-output Emit JSONL to stdout instead of the Ink dashboard (for scripting/CI)",
@@ -101382,6 +101704,11 @@ var init_cli2 = __esm(() => {
101382
101704
  });
101383
101705
 
101384
101706
  // apps/agent/src/agent/config.ts
101707
+ var exports_config = {};
101708
+ __export(exports_config, {
101709
+ loadRalphyConfig: () => loadRalphyConfig,
101710
+ ensureRalphyConfig: () => ensureRalphyConfig
101711
+ });
101385
101712
  async function loadRalphyConfig(projectRoot) {
101386
101713
  const { config: config2 } = await loadWorkflow(projectRoot);
101387
101714
  return config2;
@@ -101746,6 +102073,103 @@ function isRalphComment(body) {
101746
102073
  }
101747
102074
 
101748
102075
  // apps/agent/src/shared/capabilities/linear-client.ts
102076
+ var exports_linear_client = {};
102077
+ __export(exports_linear_client, {
102078
+ upsertRalphyAttachment: () => upsertRalphyAttachment,
102079
+ uploadFileToLinear: () => uploadFileToLinear,
102080
+ updateIssueState: () => updateIssueState,
102081
+ updateIssueDescription: () => updateIssueDescription,
102082
+ updateIssueComment: () => updateIssueComment,
102083
+ updateAttachmentSubtitle: () => updateAttachmentSubtitle,
102084
+ setIssueProject: () => setIssueProject,
102085
+ resolveTicketNumbers: () => resolveTicketNumbers,
102086
+ removeLabelFromIssue: () => removeLabelFromIssue,
102087
+ parseTicketIdentifier: () => parseTicketIdentifier,
102088
+ linearRequestInternals: () => linearRequestInternals,
102089
+ issueMatchesGetIndicator: () => issueMatchesGetIndicator,
102090
+ isRateLimitedError: () => isRateLimitedError,
102091
+ formatTicketError: () => formatTicketError,
102092
+ formatLinearError: () => formatLinearError,
102093
+ findOpenIssueByLabel: () => findOpenIssueByLabel,
102094
+ findIssueAttachmentByTitle: () => findIssueAttachmentByTitle,
102095
+ fetchWorkflowStates: () => fetchWorkflowStates,
102096
+ fetchTeamIdByKey: () => fetchTeamIdByKey,
102097
+ fetchProjectIdByName: () => fetchProjectIdByName,
102098
+ fetchOpenIssues: () => fetchOpenIssues,
102099
+ fetchMentionScanIssues: () => fetchMentionScanIssues,
102100
+ fetchIssueLabels: () => fetchIssueLabels,
102101
+ fetchIssueComments: () => fetchIssueComments,
102102
+ fetchIssueAttachments: () => fetchIssueAttachments,
102103
+ fetchAttachmentsForIssues: () => fetchAttachmentsForIssues,
102104
+ deleteIssueComment: () => deleteIssueComment,
102105
+ deleteAttachment: () => deleteAttachment,
102106
+ createRalphyAttachment: () => createRalphyAttachment,
102107
+ createIssueLabel: () => createIssueLabel,
102108
+ createIssueComment: () => createIssueComment,
102109
+ createIssue: () => createIssue,
102110
+ createAttachmentForUrl: () => createAttachmentForUrl,
102111
+ clauseFromMarkers: () => clauseFromMarkers,
102112
+ buildIssueFilter: () => buildIssueFilter,
102113
+ baseBranchFromLabels: () => baseBranchFromLabels,
102114
+ addReactionToComment: () => addReactionToComment,
102115
+ addLabelToIssue: () => addLabelToIssue,
102116
+ addIssueComment: () => addIssueComment,
102117
+ RALPHY_ATTACHMENT_TITLE: () => RALPHY_ATTACHMENT_TITLE
102118
+ });
102119
+ function parseTicketIdentifier(raw) {
102120
+ const trimmed = raw.trim();
102121
+ if (!trimmed) {
102122
+ throw new Error("--ticket value cannot be empty");
102123
+ }
102124
+ const bare = TICKET_BARE_NUMBER_RE.exec(trimmed);
102125
+ if (bare) {
102126
+ return { teamKey: null, number: Number(bare[1]) };
102127
+ }
102128
+ const match = TICKET_IDENTIFIER_RE.exec(trimmed);
102129
+ if (!match) {
102130
+ const err = new Error("--ticket value is not a Linear ticket (expected e.g. RLF-208 or 208)");
102131
+ err.value = raw;
102132
+ throw err;
102133
+ }
102134
+ return { teamKey: match[1].toUpperCase(), number: Number(match[2]) };
102135
+ }
102136
+ function resolveTicketNumbers(tokens, team) {
102137
+ const teamKey = team?.trim() ? team.trim().toUpperCase() : null;
102138
+ const seen = new Set;
102139
+ const out = [];
102140
+ for (const token of tokens) {
102141
+ const { teamKey: parsedTeam, number: number4 } = parseTicketIdentifier(token);
102142
+ if (parsedTeam !== null) {
102143
+ if (teamKey !== null && parsedTeam !== teamKey) {
102144
+ const err = new Error("--ticket identifier is not in the configured team");
102145
+ err.ticket = token;
102146
+ err.team = team;
102147
+ throw err;
102148
+ }
102149
+ } else if (teamKey === null) {
102150
+ const err = new Error("--ticket bare number needs a configured team; pass --linear-team or set linear.team in config");
102151
+ err.ticket = token;
102152
+ throw err;
102153
+ }
102154
+ if (!seen.has(number4)) {
102155
+ seen.add(number4);
102156
+ out.push(number4);
102157
+ }
102158
+ }
102159
+ return out;
102160
+ }
102161
+ function formatTicketError(err) {
102162
+ if (!(err instanceof Error))
102163
+ return String(err);
102164
+ const e = err;
102165
+ const detail = e.ticket ?? e.value;
102166
+ const parts = [];
102167
+ if (detail)
102168
+ parts.push(`ticket: ${detail}`);
102169
+ if (e.team)
102170
+ parts.push(`configured team: ${e.team}`);
102171
+ return parts.length > 0 ? `${e.message} (${parts.join(", ")})` : e.message;
102172
+ }
101749
102173
  function partition2(markers) {
101750
102174
  const statuses = [];
101751
102175
  const labels = [];
@@ -101778,6 +102202,9 @@ function buildIssueFilter(spec) {
101778
102202
  } else {
101779
102203
  where.assignee = { null: true };
101780
102204
  }
102205
+ if (spec.numbers && spec.numbers.length > 0) {
102206
+ where.number = { in: spec.numbers };
102207
+ }
101781
102208
  const inc = spec.include ?? [];
101782
102209
  if (inc.length > 0) {
101783
102210
  const { statuses, labels, attachmentSubtitles, projects } = partition2(inc);
@@ -101881,6 +102308,30 @@ function clauseFromMarkers(markers) {
101881
102308
  parts.project = { name: { in: projects } };
101882
102309
  return Object.keys(parts).length > 0 ? parts : null;
101883
102310
  }
102311
+ function mapNodeProject(node2) {
102312
+ if (!node2.project)
102313
+ return null;
102314
+ return {
102315
+ id: node2.project.id,
102316
+ name: node2.project.name,
102317
+ ...node2.project.priority !== undefined && node2.project.priority !== null ? { priority: node2.project.priority } : {}
102318
+ };
102319
+ }
102320
+ function mapNodeMilestone(node2) {
102321
+ const m = node2.projectMilestone;
102322
+ if (!m)
102323
+ return;
102324
+ return {
102325
+ id: m.id,
102326
+ name: m.name,
102327
+ sortOrder: m.sortOrder,
102328
+ ...m.targetDate != null ? { targetDate: m.targetDate } : {}
102329
+ };
102330
+ }
102331
+ function milestoneSpread(node2) {
102332
+ const m = mapNodeMilestone(node2);
102333
+ return m ? { milestone: m } : {};
102334
+ }
101884
102335
  async function fetchMentionScanIssues(apiKey, spec) {
101885
102336
  const branches = [];
101886
102337
  const { getTodo, getInProgress, setDone } = spec.indicators;
@@ -101901,21 +102352,27 @@ async function fetchMentionScanIssues(apiKey, spec) {
101901
102352
  const where = branches.length === 1 ? { ...branches[0] } : { or: branches };
101902
102353
  if (spec.team)
101903
102354
  where.team = { key: { eq: spec.team } };
101904
- if (spec.assignee) {
102355
+ if (spec.anyAssignee || spec.assignee === "any") {} else if (spec.assignee) {
101905
102356
  if (spec.assignee === "me")
101906
102357
  where.assignee = { isMe: { eq: true } };
102358
+ else if (spec.assignee === "unassigned")
102359
+ where.assignee = { null: true };
101907
102360
  else if (spec.assignee.includes("@"))
101908
102361
  where.assignee = { email: { eq: spec.assignee } };
101909
102362
  else
101910
102363
  where.assignee = { id: { eq: spec.assignee } };
101911
102364
  }
102365
+ if (spec.numbers && spec.numbers.length > 0) {
102366
+ where.number = { in: spec.numbers };
102367
+ }
101912
102368
  const query = `query MentionScanIssues($filter: IssueFilter) {
101913
102369
  issues(filter: $filter, first: 50) {
101914
102370
  nodes {
101915
102371
  id identifier title description url priority createdAt
101916
102372
  state { name type }
101917
102373
  assignee { id email name }
101918
- project { id name }
102374
+ project { id name priority }
102375
+ projectMilestone { id name sortOrder targetDate }
101919
102376
  labels { nodes { name } }
101920
102377
  relations(first: 50) {
101921
102378
  nodes { type relatedIssue { id identifier state { type } } }
@@ -101938,7 +102395,8 @@ async function fetchMentionScanIssues(apiKey, spec) {
101938
102395
  url: n.url,
101939
102396
  state: n.state,
101940
102397
  assignee: n.assignee,
101941
- project: n.project ?? null,
102398
+ project: mapNodeProject(n),
102399
+ ...milestoneSpread(n),
101942
102400
  labels: n.labels.nodes.map((l) => l.name),
101943
102401
  priority: n.priority,
101944
102402
  createdAt: n.createdAt ?? "",
@@ -101959,7 +102417,8 @@ async function fetchOpenIssues(apiKey, spec, options) {
101959
102417
  id identifier title description url priority createdAt
101960
102418
  state { name type }
101961
102419
  assignee { id email name }
101962
- project { id name }
102420
+ project { id name priority }
102421
+ projectMilestone { id name sortOrder targetDate }
101963
102422
  labels { nodes { name } }
101964
102423
  relations(first: 50) {
101965
102424
  nodes {
@@ -101983,7 +102442,8 @@ async function fetchOpenIssues(apiKey, spec, options) {
101983
102442
  url: n.url,
101984
102443
  state: n.state,
101985
102444
  assignee: n.assignee,
101986
- project: n.project ?? null,
102445
+ project: mapNodeProject(n),
102446
+ ...milestoneSpread(n),
101987
102447
  labels: n.labels.nodes.map((l) => l.name),
101988
102448
  priority: n.priority,
101989
102449
  createdAt: n.createdAt ?? "",
@@ -102496,9 +102956,11 @@ async function removeLabelFromIssue(apiKey, issueId, labelId) {
102496
102956
  labelId
102497
102957
  });
102498
102958
  }
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:";
102959
+ 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
102960
  var init_linear_client = __esm(() => {
102501
102961
  init_types2();
102962
+ TICKET_IDENTIFIER_RE = /^([A-Za-z]+)-(\d+)(?:-.*)?$/;
102963
+ TICKET_BARE_NUMBER_RE = /^(\d+)$/;
102502
102964
  linearRequestInternals = {
102503
102965
  sleep: (ms) => Bun.sleep(ms)
102504
102966
  };
@@ -104121,17 +104583,183 @@ var init_post_task = __esm(() => {
104121
104583
  repoAutoMergeCache = new Map;
104122
104584
  });
104123
104585
 
104124
- // apps/agent/src/sort/compare.ts
104125
- function chain(...comparators) {
104126
- return (a, b) => {
104127
- for (const c of comparators) {
104128
- const r = c(a, b);
104129
- if (r !== 0)
104130
- return r;
104131
- }
104586
+ // packages/core/src/ordering/hierarchical-order.ts
104587
+ function rank(priority) {
104588
+ return !priority ? Number.POSITIVE_INFINITY : priority;
104589
+ }
104590
+ function cmpMaybe(a, b, cmp) {
104591
+ if (a === undefined && b === undefined)
104132
104592
  return 0;
104133
- };
104593
+ if (a === undefined)
104594
+ return 1;
104595
+ if (b === undefined)
104596
+ return -1;
104597
+ return cmp(a, b);
104598
+ }
104599
+ function cmpString(a, b) {
104600
+ return a < b ? -1 : a > b ? 1 : 0;
104134
104601
  }
104602
+ function cmpNumber(a, b) {
104603
+ return a - b;
104604
+ }
104605
+ function cmpRank(a, b) {
104606
+ return a === b ? 0 : a < b ? -1 : 1;
104607
+ }
104608
+ function orderIssuesHierarchically(issues, opts = {}) {
104609
+ const log3 = opts.log ?? ((message) => console.warn(message));
104610
+ if (issues.length <= 1)
104611
+ return issues.slice();
104612
+ const projectBuckets = new Map;
104613
+ for (const issue2 of issues) {
104614
+ const key = issue2.project?.id ?? NO_PROJECT;
104615
+ const bucket = projectBuckets.get(key);
104616
+ if (bucket)
104617
+ bucket.push(issue2);
104618
+ else
104619
+ projectBuckets.set(key, [issue2]);
104620
+ }
104621
+ const projectKeys = [...projectBuckets.keys()].sort((a, b) => {
104622
+ if (a === NO_PROJECT)
104623
+ return b === NO_PROJECT ? 0 : 1;
104624
+ if (b === NO_PROJECT)
104625
+ return -1;
104626
+ const ba = projectBuckets.get(a);
104627
+ const bb = projectBuckets.get(b);
104628
+ const ra = rank(ba[0]?.project?.priority);
104629
+ const rb = rank(bb[0]?.project?.priority);
104630
+ if (ra !== rb)
104631
+ return cmpRank(ra, rb);
104632
+ if (a !== b)
104633
+ return cmpString(a, b);
104634
+ return cmpString(earliestCreatedAt(ba), earliestCreatedAt(bb));
104635
+ });
104636
+ const ordered = [];
104637
+ for (const projectKey of projectKeys) {
104638
+ orderProjectBucket(projectBuckets.get(projectKey), ordered, log3);
104639
+ }
104640
+ return ordered;
104641
+ }
104642
+ function earliestCreatedAt(bucket) {
104643
+ let earliest = bucket[0].createdAt;
104644
+ for (const issue2 of bucket) {
104645
+ if (issue2.createdAt < earliest)
104646
+ earliest = issue2.createdAt;
104647
+ }
104648
+ return earliest;
104649
+ }
104650
+ function orderProjectBucket(bucket, ordered, log3) {
104651
+ const milestoneBuckets = new Map;
104652
+ for (const issue2 of bucket) {
104653
+ const key = issue2.milestone?.id ?? NO_MILESTONE;
104654
+ const b = milestoneBuckets.get(key);
104655
+ if (b)
104656
+ b.push(issue2);
104657
+ else
104658
+ milestoneBuckets.set(key, [issue2]);
104659
+ }
104660
+ const milestoneOf = new Map;
104661
+ for (const [key, b] of milestoneBuckets) {
104662
+ for (const issue2 of b)
104663
+ milestoneOf.set(issue2.id, key);
104664
+ }
104665
+ const milestonePrereqs = new Map;
104666
+ for (const key of milestoneBuckets.keys())
104667
+ milestonePrereqs.set(key, new Set);
104668
+ for (const issue2 of bucket) {
104669
+ const from = milestoneOf.get(issue2.id);
104670
+ for (const blockerId of issue2.blockedByIds) {
104671
+ const blockerMilestone = milestoneOf.get(blockerId);
104672
+ if (blockerMilestone === undefined)
104673
+ continue;
104674
+ if (blockerMilestone === from)
104675
+ continue;
104676
+ milestonePrereqs.get(from).add(blockerMilestone);
104677
+ }
104678
+ }
104679
+ const milestoneMeta = new Map;
104680
+ for (const [key, b] of milestoneBuckets) {
104681
+ milestoneMeta.set(key, key === NO_MILESTONE ? undefined : b[0].milestone);
104682
+ }
104683
+ const milestoneOrder = topoOrder([...milestoneBuckets.keys()], (key) => milestonePrereqs.get(key), (a, b) => {
104684
+ if (a === NO_MILESTONE)
104685
+ return b === NO_MILESTONE ? 0 : 1;
104686
+ if (b === NO_MILESTONE)
104687
+ return -1;
104688
+ const ma = milestoneMeta.get(a);
104689
+ const mb = milestoneMeta.get(b);
104690
+ const so = cmpNumber(ma?.sortOrder ?? 0, mb?.sortOrder ?? 0);
104691
+ if (so !== 0)
104692
+ return so;
104693
+ const td = cmpMaybe(ma?.targetDate, mb?.targetDate, cmpString);
104694
+ if (td !== 0)
104695
+ return td;
104696
+ return cmpString(a, b);
104697
+ }, (key) => log3(`hierarchical-order: milestone dependency cycle involving "${key}"; breaking by selection key`));
104698
+ for (const milestoneKey of milestoneOrder) {
104699
+ orderMilestoneBucket(milestoneBuckets.get(milestoneKey), ordered, log3);
104700
+ }
104701
+ }
104702
+ function orderMilestoneBucket(bucket, ordered, log3) {
104703
+ const inBucket = new Set(bucket.map((i) => i.id));
104704
+ const byId = new Map(bucket.map((i) => [i.id, i]));
104705
+ const order = topoOrder(bucket.map((i) => i.id), (id) => {
104706
+ const prereqs = new Set;
104707
+ for (const blockerId of byId.get(id).blockedByIds) {
104708
+ if (inBucket.has(blockerId))
104709
+ prereqs.add(blockerId);
104710
+ }
104711
+ return prereqs;
104712
+ }, (a, b) => {
104713
+ const ia = byId.get(a);
104714
+ const ib = byId.get(b);
104715
+ const rp = cmpRank(rank(ia.priority), rank(ib.priority));
104716
+ if (rp !== 0)
104717
+ return rp;
104718
+ const tb = cmpNumber(ia.tiebreak ?? 0, ib.tiebreak ?? 0);
104719
+ if (tb !== 0)
104720
+ return tb;
104721
+ const cc = cmpString(ia.createdAt, ib.createdAt);
104722
+ if (cc !== 0)
104723
+ return cc;
104724
+ return cmpString(a, b);
104725
+ }, (id) => log3(`hierarchical-order: item dependency cycle involving "${id}"; breaking by selection key`));
104726
+ for (const id of order)
104727
+ ordered.push(byId.get(id));
104728
+ }
104729
+ function topoOrder(nodes, prereqsOf, select2, onCycle) {
104730
+ const remaining = new Set(nodes);
104731
+ const placed = new Set;
104732
+ const result2 = [];
104733
+ while (remaining.size > 0) {
104734
+ const eligible = [];
104735
+ for (const node2 of remaining) {
104736
+ let ready = true;
104737
+ for (const prereq of prereqsOf(node2)) {
104738
+ if (remaining.has(prereq) && !placed.has(prereq)) {
104739
+ ready = false;
104740
+ break;
104741
+ }
104742
+ }
104743
+ if (ready)
104744
+ eligible.push(node2);
104745
+ }
104746
+ let pick3;
104747
+ if (eligible.length > 0) {
104748
+ pick3 = eligible.sort(select2)[0];
104749
+ } else {
104750
+ pick3 = [...remaining].sort(select2)[0];
104751
+ onCycle(pick3);
104752
+ }
104753
+ result2.push(pick3);
104754
+ placed.add(pick3);
104755
+ remaining.delete(pick3);
104756
+ }
104757
+ return result2;
104758
+ }
104759
+ var NO_PROJECT = "\x00no-project", NO_MILESTONE = "\x00no-milestone";
104760
+
104761
+ // packages/core/src/ordering/index.ts
104762
+ var init_ordering = () => {};
104135
104763
 
104136
104764
  // apps/agent/src/queue/queue-order.ts
104137
104765
  function defaultPriorityFor(trigger) {
@@ -104148,21 +104776,70 @@ function defaultPriorityFor(trigger) {
104148
104776
  return 4;
104149
104777
  }
104150
104778
  }
104151
- function compareQueueEntries(getAutoMerge) {
104152
- const isAutoMergeBoost = (e) => e.trigger === "conflict-fix" && issueMatchesGetIndicator(e.issue, getAutoMerge);
104153
- return chain((a, b) => Number(!isAutoMergeBoost(a)) - Number(!isAutoMergeBoost(b)), (a, b) => {
104154
- const pa = a.issue.priority === 0 ? Infinity : a.issue.priority;
104155
- const pb = b.issue.priority === 0 ? Infinity : b.issue.priority;
104156
- return pa - pb;
104157
- }, (a, b) => a.priority - b.priority, (a, b) => {
104158
- const ca = a.issue.createdAt;
104159
- const cb = b.issue.createdAt;
104160
- if (ca === cb)
104161
- return 0;
104162
- return ca < cb ? -1 : 1;
104163
- });
104779
+ function isAutoMergeBoost(e, getAutoMerge) {
104780
+ return e.trigger === "conflict-fix" && issueMatchesGetIndicator(e.issue, getAutoMerge);
104781
+ }
104782
+ function linearIssueToOrderable(issue2, tiebreak) {
104783
+ return {
104784
+ id: issue2.id,
104785
+ ...issue2.project ? {
104786
+ project: {
104787
+ id: issue2.project.id,
104788
+ ...issue2.project.priority !== undefined ? { priority: issue2.project.priority } : {}
104789
+ }
104790
+ } : {},
104791
+ ...issue2.milestone ? {
104792
+ milestone: {
104793
+ id: issue2.milestone.id,
104794
+ sortOrder: issue2.milestone.sortOrder,
104795
+ ...issue2.milestone.targetDate ? { targetDate: issue2.milestone.targetDate } : {}
104796
+ }
104797
+ } : {},
104798
+ priority: issue2.priority,
104799
+ ...tiebreak !== undefined ? { tiebreak } : {},
104800
+ blockedByIds: issue2.blockedByIds,
104801
+ createdAt: issue2.createdAt
104802
+ };
104803
+ }
104804
+ function toOrderable(entry) {
104805
+ return { ...linearIssueToOrderable(entry.issue, entry.priority), entry };
104806
+ }
104807
+ function orderEntries(entries) {
104808
+ if (entries.length <= 1)
104809
+ return entries.slice();
104810
+ const repByIssue = new Map;
104811
+ for (const entry of entries) {
104812
+ const orderable = toOrderable(entry);
104813
+ const existing = repByIssue.get(orderable.id);
104814
+ if (!existing || orderable.tiebreak < existing.tiebreak) {
104815
+ repByIssue.set(orderable.id, orderable);
104816
+ }
104817
+ }
104818
+ const rankOf = new Map;
104819
+ orderIssuesHierarchically([...repByIssue.values()]).forEach((o, i) => rankOf.set(o.id, i));
104820
+ return entries.map((entry, index) => ({ entry, index })).sort((a, b) => {
104821
+ const ra = rankOf.get(a.entry.issue.id);
104822
+ const rb = rankOf.get(b.entry.issue.id);
104823
+ if (ra !== rb)
104824
+ return ra - rb;
104825
+ if (a.entry.priority !== b.entry.priority)
104826
+ return a.entry.priority - b.entry.priority;
104827
+ return a.index - b.index;
104828
+ }).map((x) => x.entry);
104829
+ }
104830
+ function orderQueueEntries(entries, getAutoMerge) {
104831
+ const boosted = [];
104832
+ const rest2 = [];
104833
+ for (const e of entries) {
104834
+ if (isAutoMergeBoost(e, getAutoMerge))
104835
+ boosted.push(e);
104836
+ else
104837
+ rest2.push(e);
104838
+ }
104839
+ return [...orderEntries(boosted), ...orderEntries(rest2)];
104164
104840
  }
104165
104841
  var init_queue_order = __esm(() => {
104842
+ init_ordering();
104166
104843
  init_linear();
104167
104844
  });
104168
104845
 
@@ -104280,6 +104957,7 @@ class AgentCoordinator {
104280
104957
  ciFailed: 0,
104281
104958
  review: 0,
104282
104959
  mentions: mentions.length,
104960
+ quarantined: 0,
104283
104961
  awaiting: awaitingCount
104284
104962
  };
104285
104963
  const found2 = buckets2.todo + buckets2.inProgress + buckets2.mentions + buckets2.awaiting;
@@ -104362,7 +105040,7 @@ class AgentCoordinator {
104362
105040
  }
104363
105041
  const prStatus = await this.scanPrMergeStates();
104364
105042
  if (this.queue.length > 0) {
104365
- this.queue.sort(compareQueueEntries(this.opts.getAutoMerge));
105043
+ this.queue = orderQueueEntries(this.queue, this.opts.getAutoMerge);
104366
105044
  }
104367
105045
  this.spawnNext();
104368
105046
  await this.reportProgress();
@@ -104374,6 +105052,7 @@ class AgentCoordinator {
104374
105052
  ciFailed: prStatus.ciFailed,
104375
105053
  review: 0,
104376
105054
  mentions: mentions.length,
105055
+ quarantined: prStatus.quarantined,
104377
105056
  awaiting: awaitingCount
104378
105057
  };
104379
105058
  const found = buckets.todo + buckets.inProgress + buckets.conflicted + buckets.ciFailed + buckets.mentions + buckets.awaiting;
@@ -104569,6 +105248,7 @@ class AgentCoordinator {
104569
105248
  return counts;
104570
105249
  const preQueue = this.queue.map((q) => ({ id: q.issue.id, trigger: q.trigger }));
104571
105250
  const preWorkers = this.workers.map((w) => ({ id: w.issueId, trigger: w.trigger }));
105251
+ const tracker = this.opts.prTracker;
104572
105252
  for (const issue2 of candidates) {
104573
105253
  if (this.workers.some((w) => w.issueId === issue2.id))
104574
105254
  continue;
@@ -104576,6 +105256,13 @@ class AgentCoordinator {
104576
105256
  continue;
104577
105257
  if (this.queue.some((q) => q.issue.id === issue2.id))
104578
105258
  continue;
105259
+ if (tracker?.isBailed(issue2.identifier) && this.errorMarkerCleared(issue2)) {
105260
+ await tracker.clear(issue2.identifier).catch(() => {});
105261
+ this.conflictNotified.delete(issue2.id);
105262
+ this.ciFailedNotified.delete(issue2.id);
105263
+ this.conflictPromoted.delete(issue2.id);
105264
+ this.deps.onLog(` ${issue2.identifier}: pr-tracker bail cleared (ticket back in Todo) \u2014 retrying recovery`, "cyan");
105265
+ }
104579
105266
  let pr;
104580
105267
  try {
104581
105268
  pr = await this.deps.checkPrStatus(issue2);
@@ -104595,10 +105282,18 @@ class AgentCoordinator {
104595
105282
  }
104596
105283
  }
104597
105284
  if (pr.status === "conflicted") {
105285
+ if (tracker?.isBailed(issue2.identifier)) {
105286
+ counts.quarantined += 1;
105287
+ continue;
105288
+ }
105289
+ counts.conflicted += 1;
104598
105290
  if (this.conflictNotified.has(issue2.id))
104599
105291
  continue;
104600
- if (await this.prTrackerBail(issue2, pr.url, "conflicting"))
105292
+ if (await this.prTrackerBail(issue2, pr.url, "conflicting")) {
105293
+ counts.conflicted -= 1;
105294
+ counts.quarantined += 1;
104601
105295
  continue;
105296
+ }
104602
105297
  emitCapture(this.bus, "agent_conflict_detected", { issue_identifier: issue2.identifier });
104603
105298
  this.conflictNotified.add(issue2.id);
104604
105299
  this.deps.onLog(` ${issue2.identifier}: PR ${pr.url} conflicting \u2014 queued (conflict-fix)`, "yellow");
@@ -104617,14 +105312,21 @@ class AgentCoordinator {
104617
105312
  trigger: "conflict-fix",
104618
105313
  priority: defaultPriorityFor("conflict-fix")
104619
105314
  });
104620
- counts.conflicted += 1;
104621
105315
  continue;
104622
105316
  }
104623
105317
  if (pr.status === "ci_failed") {
105318
+ if (tracker?.isBailed(issue2.identifier)) {
105319
+ counts.quarantined += 1;
105320
+ continue;
105321
+ }
105322
+ counts.ciFailed += 1;
104624
105323
  if (this.ciFailedNotified.has(issue2.id))
104625
105324
  continue;
104626
- if (await this.prTrackerBail(issue2, pr.url, "ci_failed"))
105325
+ if (await this.prTrackerBail(issue2, pr.url, "ci_failed")) {
105326
+ counts.ciFailed -= 1;
105327
+ counts.quarantined += 1;
104627
105328
  continue;
105329
+ }
104628
105330
  emitCapture(this.bus, "agent_ci_failed_detected", { issue_identifier: issue2.identifier });
104629
105331
  this.ciFailedNotified.add(issue2.id);
104630
105332
  this.deps.onLog(` ${issue2.identifier}: PR ${pr.url} CI failing \u2014 queued (ci-fix)`, "yellow");
@@ -104643,7 +105345,6 @@ class AgentCoordinator {
104643
105345
  trigger: "ci-fix",
104644
105346
  priority: defaultPriorityFor("ci-fix")
104645
105347
  });
104646
- counts.ciFailed += 1;
104647
105348
  }
104648
105349
  }
104649
105350
  for (const q of preQueue) {
@@ -104660,6 +105361,16 @@ class AgentCoordinator {
104660
105361
  }
104661
105362
  return counts;
104662
105363
  }
105364
+ errorMarkerCleared(issue2) {
105365
+ const se = this.opts.setError;
105366
+ if (!se)
105367
+ return false;
105368
+ const wantLabels = markersOf(se).filter((m) => m.type === "label").map((m) => m.value.toLowerCase());
105369
+ if (wantLabels.length === 0)
105370
+ return false;
105371
+ const have = new Set(issue2.labels.map((l) => l.toLowerCase()));
105372
+ return !wantLabels.some((v) => have.has(v));
105373
+ }
104663
105374
  async prTrackerBail(issue2, prUrl, reason) {
104664
105375
  const tracker = this.opts.prTracker;
104665
105376
  if (!tracker)
@@ -105039,7 +105750,12 @@ function triggerToFlowId(trigger) {
105039
105750
  return "review-followup";
105040
105751
  return "implement";
105041
105752
  }
105042
- var emptyPrStatus = () => ({ mergeable: 0, conflicted: 0, ciFailed: 0 }), emptyPollResult = () => ({
105753
+ var emptyPrStatus = () => ({
105754
+ mergeable: 0,
105755
+ conflicted: 0,
105756
+ ciFailed: 0,
105757
+ quarantined: 0
105758
+ }), emptyPollResult = () => ({
105043
105759
  found: 0,
105044
105760
  added: 0,
105045
105761
  buckets: {
@@ -105049,6 +105765,7 @@ var emptyPrStatus = () => ({ mergeable: 0, conflicted: 0, ciFailed: 0 }), emptyP
105049
105765
  ciFailed: 0,
105050
105766
  review: 0,
105051
105767
  mentions: 0,
105768
+ quarantined: 0,
105052
105769
  awaiting: 0
105053
105770
  },
105054
105771
  prStatus: emptyPrStatus(),
@@ -105056,6 +105773,7 @@ var emptyPrStatus = () => ({ mergeable: 0, conflicted: 0, ciFailed: 0 }), emptyP
105056
105773
  flow: {}
105057
105774
  });
105058
105775
  var init_coordinator = __esm(() => {
105776
+ init_types2();
105059
105777
  init_post_task();
105060
105778
  init_queue_order();
105061
105779
  init_src();
@@ -105937,10 +106655,10 @@ function unionMarkers(...sets) {
105937
106655
  }
105938
106656
  return out;
105939
106657
  }
105940
- function describeIndicators(indicators, team, assignee) {
106658
+ function describeIndicators(indicators, team, assignee, anyAssignee) {
105941
106659
  const parts = [];
105942
106660
  parts.push(`team=${team ?? "*"}`);
105943
- parts.push(`assignee=${assignee ?? "*"}`);
106661
+ parts.push(`assignee=${anyAssignee ? "any" : assignee ?? "*"}`);
105944
106662
  if (indicators.getTodo) {
105945
106663
  parts.push(`todo=[${indicators.getTodo.filter.map((m) => `${m.type}:${m.value}`).join(",")}]`);
105946
106664
  }
@@ -105955,7 +106673,8 @@ var init_indicators = __esm(() => {
105955
106673
 
105956
106674
  // apps/agent/src/agent/wire/linear-resolvers.ts
105957
106675
  function createLinearResolvers(input) {
105958
- const { apiKey, team, assignee, diag } = input;
106676
+ const { apiKey, team, assignee, anyAssignee, diag } = input;
106677
+ const ticketNumbers = input.ticketNumbers ?? [];
105959
106678
  const stateCache = new Map;
105960
106679
  const labelCache = new Map;
105961
106680
  const teamIdCache = new Map;
@@ -106067,7 +106786,14 @@ function createLinearResolvers(input) {
106067
106786
  if (include.length === 0)
106068
106787
  return [];
106069
106788
  const hasCommentMarker = include.some((m) => m.type === "comment");
106070
- const spec = { team, assignee, include, exclude: excl };
106789
+ const spec = {
106790
+ team,
106791
+ assignee,
106792
+ anyAssignee,
106793
+ include,
106794
+ exclude: excl,
106795
+ ...ticketNumbers.length > 0 ? { numbers: ticketNumbers } : {}
106796
+ };
106071
106797
  const fetched = await fetchOpenIssues(apiKey, spec, hasCommentMarker ? { includeComments: true } : undefined);
106072
106798
  if (!hasCommentMarker)
106073
106799
  return fetched;
@@ -106086,7 +106812,7 @@ function createLinearResolvers(input) {
106086
106812
  resolveLabelIdForTeam
106087
106813
  };
106088
106814
  }
106089
- async function fetchDoneCandidatesWith(apiKey, team, _assignee, indicators) {
106815
+ async function fetchDoneCandidatesWith(apiKey, team, _assignee, indicators, ticketNumbers) {
106090
106816
  const getIndicators = [
106091
106817
  indicators.getTodo,
106092
106818
  indicators.getInProgress,
@@ -106109,7 +106835,8 @@ async function fetchDoneCandidatesWith(apiKey, team, _assignee, indicators) {
106109
106835
  team,
106110
106836
  anyAssignee: true,
106111
106837
  include,
106112
- exclude: []
106838
+ exclude: [],
106839
+ ...ticketNumbers && ticketNumbers.length > 0 ? { numbers: ticketNumbers } : {}
106113
106840
  });
106114
106841
  for (const issue2 of issues) {
106115
106842
  if (!seen.has(issue2.id)) {
@@ -106598,7 +107325,7 @@ async function fetchPrReviewState(prUrl, cmdRunner, projectRoot, onLog) {
106598
107325
  reviewRequests(first:5){nodes{requestedReviewer{... on User{login}}}}
106599
107326
  latestReviews(first:5){nodes{author{login} state submittedAt}}
106600
107327
  reviewThreads(first:50){nodes{
106601
- isResolved path line
107328
+ isResolved subjectType path line
106602
107329
  comments(first:20){nodes{body author{login} createdAt url}}
106603
107330
  }}
106604
107331
  }
@@ -106631,6 +107358,7 @@ async function fetchPrReviewState(prUrl, cmdRunner, projectRoot, onLog) {
106631
107358
  approved: pr2.reviewDecision === "APPROVED",
106632
107359
  threads: (pr2.reviewThreads?.nodes ?? []).map((t) => ({
106633
107360
  isResolved: t.isResolved,
107361
+ isFileLevel: t.subjectType === "FILE",
106634
107362
  ...t.path ? { path: t.path } : {},
106635
107363
  ...t.line != null ? { line: t.line } : {},
106636
107364
  comments: t.comments.nodes.map((c) => ({
@@ -106697,7 +107425,7 @@ async function scanCodeReview(issue2, prUrl, lastRalphPickup, deps) {
106697
107425
  const effectiveLastHandled = lastRalphPickup && lastHandled ? lastRalphPickup > lastHandled ? lastRalphPickup : lastHandled : lastRalphPickup ?? lastHandled;
106698
107426
  if (!effectiveLastHandled || newestReviewerActivity > effectiveLastHandled) {
106699
107427
  const body = unresolved.map((t) => {
106700
- const head3 = t.path ? `_${t.path}${t.line ? `:${t.line}` : ""}_` : "_(general)_";
107428
+ const head3 = t.path ? t.isFileLevel ? `_${t.path} (whole file)_` : `_${t.path}${t.line ? `:${t.line}` : ""}_` : "_(general)_";
106701
107429
  const lines = t.comments.map((c) => `> **${c.author ?? "reviewer"}** (${c.createdAt})
106702
107430
  >
106703
107431
  > ${c.body.trim().replace(/\n/g, `
@@ -106795,6 +107523,7 @@ function createMentionScanner(input) {
106795
107523
  cfg,
106796
107524
  team,
106797
107525
  assignee,
107526
+ anyAssignee,
106798
107527
  indicators,
106799
107528
  projectRoot,
106800
107529
  useWorktree,
@@ -106802,6 +107531,7 @@ function createMentionScanner(input) {
106802
107531
  onLog,
106803
107532
  diag,
106804
107533
  cwdByChange,
107534
+ ticketNumbers,
106805
107535
  stalePingedAt,
106806
107536
  lastHandledReviewActivity,
106807
107537
  resolvePrUrlForIssue
@@ -106817,6 +107547,8 @@ function createMentionScanner(input) {
106817
107547
  candidates = await fetchMentionScanIssues(apiKey, {
106818
107548
  team,
106819
107549
  assignee,
107550
+ anyAssignee,
107551
+ ...ticketNumbers && ticketNumbers.length > 0 ? { numbers: ticketNumbers } : {},
106820
107552
  indicators: {
106821
107553
  ...indicators.getTodo !== undefined ? { getTodo: indicators.getTodo } : {},
106822
107554
  ...indicators.getInProgress !== undefined ? { getInProgress: indicators.getInProgress } : {},
@@ -239295,9 +240027,9 @@ class $50c7aac9316f2948$export$2e2bcd8739ae039 {
239295
240027
 
239296
240028
  class $55f71433a605c87d$export$2e2bcd8739ae039 {
239297
240029
  process(glyphs, features = {}) {
239298
- for (let chain2 of this.morx.chains) {
239299
- let flags = chain2.defaultFlags;
239300
- for (let feature of chain2.features) {
240030
+ for (let chain of this.morx.chains) {
240031
+ let flags = chain.defaultFlags;
240032
+ for (let feature of chain.features) {
239301
240033
  let f2;
239302
240034
  if (f2 = features[feature.featureType]) {
239303
240035
  if (f2[feature.featureSetting]) {
@@ -239309,7 +240041,7 @@ class $55f71433a605c87d$export$2e2bcd8739ae039 {
239309
240041
  }
239310
240042
  }
239311
240043
  }
239312
- for (let subtable of chain2.subtables)
240044
+ for (let subtable of chain.subtables)
239313
240045
  if (subtable.subFeatureFlags & flags)
239314
240046
  this.processSubtable(subtable, glyphs);
239315
240047
  }
@@ -239456,8 +240188,8 @@ class $55f71433a605c87d$export$2e2bcd8739ae039 {
239456
240188
  }
239457
240189
  getSupportedFeatures() {
239458
240190
  let features = [];
239459
- for (let chain2 of this.morx.chains)
239460
- for (let feature of chain2.features)
240191
+ for (let chain of this.morx.chains)
240192
+ for (let feature of chain.features)
239461
240193
  features.push([
239462
240194
  feature.featureType,
239463
240195
  feature.featureSetting
@@ -239471,9 +240203,9 @@ class $55f71433a605c87d$export$2e2bcd8739ae039 {
239471
240203
  }
239472
240204
  generateInputCache() {
239473
240205
  this.inputCache = {};
239474
- for (let chain2 of this.morx.chains) {
239475
- let flags = chain2.defaultFlags;
239476
- for (let subtable of chain2.subtables)
240206
+ for (let chain of this.morx.chains) {
240207
+ let flags = chain.defaultFlags;
240208
+ for (let subtable of chain.subtables)
239477
240209
  if (subtable.subFeatureFlags & flags)
239478
240210
  this.generateInputsForSubtable(subtable);
239479
240211
  }
@@ -260555,7 +261287,9 @@ function buildAgentCoordinator(input) {
260555
261287
  const pollInterval = args.pollInterval || cfg.pollIntervalSeconds;
260556
261288
  const indicators = mergeIndicators(cfg.linear.indicators, args.indicators);
260557
261289
  const team = args.linearTeam || cfg.linear.team;
260558
- const assignee = args.linearAssignee || cfg.linear.assignee;
261290
+ const effectiveFilter = args.linearFilter || (args.linearAssignee ? `assignee = ${args.linearAssignee}` : "") || cfg.linear.filter;
261291
+ const { assignee, anyAssignee } = parseLinearFilter(effectiveFilter);
261292
+ const ticketNumbers = resolveTicketNumbers(args.ticketTokens, team);
260559
261293
  const excludeFromTodo = unionMarkers(indicators.setDone, indicators.setError);
260560
261294
  const gitRunner = input.runners?.git ?? bunGitRunner;
260561
261295
  const cmdRunner = input.runners?.cmd ?? bunCmdRunner;
@@ -260587,7 +261321,20 @@ function buildAgentCoordinator(input) {
260587
261321
  }
260588
261322
  return code;
260589
261323
  });
260590
- const resolvers = createLinearResolvers({ apiKey, team, assignee, diag });
261324
+ const resolvers = createLinearResolvers({
261325
+ apiKey,
261326
+ team,
261327
+ assignee,
261328
+ anyAssignee,
261329
+ diag,
261330
+ ...ticketNumbers.length > 0 ? { ticketNumbers } : {}
261331
+ });
261332
+ if (ticketNumbers.length > 0) {
261333
+ const hasGetIndicator = [indicators.getTodo, indicators.getInProgress].some((ind) => ind && ind.filter.length > 0);
261334
+ if (!hasGetIndicator) {
261335
+ diag("ticket", `! --ticket set (${ticketNumbers.join(", ")}) but no getTodo/getInProgress indicator is configured \u2014 nothing will be picked up`, "yellow");
261336
+ }
261337
+ }
260591
261338
  const prDiscovery = createPrDiscovery({
260592
261339
  apiKey,
260593
261340
  projectRoot,
@@ -260616,6 +261363,7 @@ function buildAgentCoordinator(input) {
260616
261363
  cfg,
260617
261364
  team,
260618
261365
  assignee,
261366
+ anyAssignee,
260619
261367
  indicators,
260620
261368
  projectRoot,
260621
261369
  useWorktree,
@@ -260623,6 +261371,7 @@ function buildAgentCoordinator(input) {
260623
261371
  onLog,
260624
261372
  diag,
260625
261373
  cwdByChange,
261374
+ ...ticketNumbers.length > 0 ? { ticketNumbers } : {},
260626
261375
  stalePingedAt,
260627
261376
  lastHandledReviewActivity,
260628
261377
  resolvePrUrlForIssue: prDiscovery.resolvePrUrlForIssue
@@ -260719,7 +261468,7 @@ function buildAgentCoordinator(input) {
260719
261468
  fetchTodo: () => resolvers.fetchByGet(indicators.getTodo, excludeFromTodo),
260720
261469
  fetchInProgress: () => resolvers.fetchByGet(indicators.getInProgress, unionMarkers(indicators.setError)),
260721
261470
  fetchMentions,
260722
- fetchDoneCandidates: () => fetchDoneCandidatesWith(apiKey, team, assignee, indicators),
261471
+ fetchDoneCandidates: () => fetchDoneCandidatesWith(apiKey, team, assignee, indicators, ticketNumbers.length > 0 ? ticketNumbers : undefined),
260723
261472
  prepare: prep.prepare,
260724
261473
  prepareTaskForTrigger: prep.prepareTaskForTrigger,
260725
261474
  spawnWorker,
@@ -260758,7 +261507,7 @@ function buildAgentCoordinator(input) {
260758
261507
  ...prTracker ? { prTracker } : {}
260759
261508
  });
260760
261509
  coordRef.current = coord;
260761
- const filterDesc = describeIndicators(indicators, team, assignee);
261510
+ const filterDesc = describeIndicators(indicators, team, assignee, anyAssignee);
260762
261511
  const runBaselineGateOnce = createBaselineGateRunner({
260763
261512
  args,
260764
261513
  cfg,
@@ -260794,6 +261543,7 @@ function buildAgentCoordinator(input) {
260794
261543
  };
260795
261544
  }
260796
261545
  var init_wire = __esm(() => {
261546
+ init_workflow();
260797
261547
  init_src2();
260798
261548
  init_coordinator2();
260799
261549
  init_linear();
@@ -260805,6 +261555,7 @@ var init_wire = __esm(() => {
260805
261555
  init_indicators();
260806
261556
  init_task_bodies();
260807
261557
  init_linear_resolvers();
261558
+ init_linear_client();
260808
261559
  init_prepare();
260809
261560
  init_pr_discovery();
260810
261561
  init_mention_scan();
@@ -260820,7 +261571,7 @@ import { dirname as dirname15 } from "path";
260820
261571
  function createJsonLogFileSink(path) {
260821
261572
  if (!path)
260822
261573
  return { emit: () => {} };
260823
- let chain2 = (async () => {
261574
+ let chain = (async () => {
260824
261575
  try {
260825
261576
  await mkdir12(dirname15(path), { recursive: true });
260826
261577
  await Bun.write(path, "");
@@ -260830,7 +261581,7 @@ function createJsonLogFileSink(path) {
260830
261581
  emit(event) {
260831
261582
  const line = JSON.stringify({ ts: Date.now(), v: VERSION, ...event }) + `
260832
261583
  `;
260833
- chain2 = chain2.then(async () => {
261584
+ chain = chain.then(async () => {
260834
261585
  try {
260835
261586
  await appendFile2(path, line);
260836
261587
  } catch {}
@@ -262114,6 +262865,19 @@ function AgentMode({
262114
262865
  /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
262115
262866
  color: pollStatus.lastPrStatus.ciFailed > 0 ? "red" : "white",
262116
262867
  children: pollStatus.lastPrStatus.ciFailed
262868
+ }, undefined, false, undefined, this),
262869
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
262870
+ dimColor: true,
262871
+ children: "\xB7"
262872
+ }, undefined, false, undefined, this),
262873
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
262874
+ dimColor: true,
262875
+ children: "quarantined"
262876
+ }, undefined, false, undefined, this),
262877
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
262878
+ color: pollStatus.lastPrStatus.quarantined > 0 ? "magenta" : "white",
262879
+ bold: true,
262880
+ children: pollStatus.lastPrStatus.quarantined
262117
262881
  }, undefined, false, undefined, this)
262118
262882
  ]
262119
262883
  }, undefined, true, undefined, this)
@@ -262938,6 +263702,18 @@ var init_tmux = __esm(() => {
262938
263702
  decoder = new TextDecoder;
262939
263703
  });
262940
263704
 
263705
+ // apps/agent/src/sort/compare.ts
263706
+ function chain(...comparators) {
263707
+ return (a, b2) => {
263708
+ for (const c of comparators) {
263709
+ const r = c(a, b2);
263710
+ if (r !== 0)
263711
+ return r;
263712
+ }
263713
+ return 0;
263714
+ };
263715
+ }
263716
+
262941
263717
  // apps/agent/src/list-sort.ts
262942
263718
  function assignTier(status) {
262943
263719
  if (status === null || status.kind === "error")
@@ -262960,7 +263736,7 @@ function createdAtOf(status) {
262960
263736
  return "";
262961
263737
  }
262962
263738
  function sortRows(rows) {
262963
- const cmp = chain((a, b2) => assignTier(a.status) - assignTier(b2.status), (a, b2) => {
263739
+ const cmp = chain((a, b2) => assignTier(a.status) - assignTier(b2.status), (a, b2) => a.bucketOrder - b2.bucketOrder, (a, b2) => {
262964
263740
  const ia = a.issueCreatedAt;
262965
263741
  const ib = b2.issueCreatedAt;
262966
263742
  if (ia === ib)
@@ -262976,7 +263752,7 @@ function sortRows(rows) {
262976
263752
  if (ca === cb)
262977
263753
  return 0;
262978
263754
  return ca < cb ? -1 : 1;
262979
- }, (a, b2) => a.bucketOrder - b2.bucketOrder, (a, b2) => a.identifier.localeCompare(b2.identifier));
263755
+ }, (a, b2) => a.identifier.localeCompare(b2.identifier));
262980
263756
  return [...rows].sort(cmp);
262981
263757
  }
262982
263758
  var init_list_sort = () => {};
@@ -263025,7 +263801,8 @@ __export(exports_list, {
263025
263801
  formatReviewCell: () => formatReviewCell,
263026
263802
  formatPrStatusMarker: () => formatPrStatusMarker,
263027
263803
  formatBlockedCell: () => formatBlockedCell,
263028
- buildBuckets: () => buildBuckets
263804
+ buildBuckets: () => buildBuckets,
263805
+ backlogRankByIssueId: () => backlogRankByIssueId
263029
263806
  });
263030
263807
  import { join as join36 } from "path";
263031
263808
  function countTaskItems(content) {
@@ -263127,17 +263904,23 @@ function buildBuckets(indicators) {
263127
263904
  { label: "auto-merge", indicator: indicators.getAutoMerge, exclude: [] }
263128
263905
  ];
263129
263906
  }
263130
- async function fetchBucketIssues(apiKey, bucket, team, assignee) {
263907
+ async function fetchBucketIssues(apiKey, bucket, team, assignee, anyAssignee, ticketNumbers) {
263131
263908
  if (!bucket.indicator || bucket.indicator.filter.length === 0)
263132
263909
  return [];
263133
263910
  const spec = {
263134
263911
  team,
263135
263912
  assignee,
263913
+ anyAssignee,
263136
263914
  include: bucket.indicator.filter,
263137
- exclude: bucket.exclude
263915
+ exclude: bucket.exclude,
263916
+ ...ticketNumbers.length > 0 ? { numbers: ticketNumbers } : {}
263138
263917
  };
263139
263918
  return fetchOpenIssues(apiKey, spec);
263140
263919
  }
263920
+ function resolveLinearFilter(filterOverride, assigneeOverride, configFilter) {
263921
+ const effective = filterOverride || (assigneeOverride ? `assignee = ${assigneeOverride}` : "") || configFilter;
263922
+ return parseLinearFilter(effective);
263923
+ }
263141
263924
  function formatReviewCell(prUrl, count) {
263142
263925
  if (!prUrl)
263143
263926
  return "-";
@@ -263178,13 +263961,19 @@ function formatPrStatusMarker(status, failedCheckNames) {
263178
263961
  return "ok";
263179
263962
  return parts.join(" ");
263180
263963
  }
263181
- async function fetchAndPrintLinear(apiKey, buckets, team, assignee, cwd2, runner, ignoreCiChecks = [], checks3 = false, review = false) {
263964
+ function backlogRankByIssueId(issues) {
263965
+ const ordered = orderIssuesHierarchically(issues.map((issue2) => linearIssueToOrderable(issue2)));
263966
+ const rankById = new Map;
263967
+ ordered.forEach((o, i) => rankById.set(o.id, i));
263968
+ return rankById;
263969
+ }
263970
+ async function fetchAndPrintLinear(apiKey, buckets, team, assignee, anyAssignee, cwd2, runner, ignoreCiChecks = [], checks3 = false, review = false, ticketNumbers = []) {
263182
263971
  const bucketResults = await Promise.all(buckets.map(async (bucket) => {
263183
263972
  if (!bucket.indicator || bucket.indicator.filter.length === 0) {
263184
263973
  return { bucket, issues: [], error: null };
263185
263974
  }
263186
263975
  try {
263187
- const issues = await fetchBucketIssues(apiKey, bucket, team, assignee);
263976
+ const issues = await fetchBucketIssues(apiKey, bucket, team, assignee, anyAssignee, ticketNumbers);
263188
263977
  return { bucket, issues, error: null };
263189
263978
  } catch (err) {
263190
263979
  return {
@@ -263202,16 +263991,17 @@ ${bucket.label}: error fetching from Linear \u2014 ${error48}
263202
263991
  }
263203
263992
  }
263204
263993
  const seen = new Map;
263205
- let order = 0;
263994
+ const issueById = new Map;
263206
263995
  for (const { bucket, issues } of bucketResults) {
263207
263996
  for (const issue2 of issues) {
263208
263997
  if (seen.has(issue2.id))
263209
263998
  continue;
263999
+ issueById.set(issue2.id, issue2);
263210
264000
  seen.set(issue2.id, {
263211
264001
  issueId: issue2.id,
263212
264002
  identifier: issue2.identifier,
263213
264003
  status: null,
263214
- bucketOrder: order++,
264004
+ bucketOrder: 0,
263215
264005
  issueCreatedAt: issue2.createdAt,
263216
264006
  bucketLabel: bucket.label,
263217
264007
  stateName: issue2.state.name,
@@ -263222,6 +264012,9 @@ ${bucket.label}: error fetching from Linear \u2014 ${error48}
263222
264012
  }
263223
264013
  }
263224
264014
  const rows = [...seen.values()];
264015
+ const rankById = backlogRankByIssueId([...issueById.values()]);
264016
+ for (const row of rows)
264017
+ row.bucketOrder = rankById.get(row.issueId) ?? 0;
263225
264018
  try {
263226
264019
  const attachmentsByIssue = await fetchAttachmentsForIssues(apiKey, rows.map((r) => r.issueId));
263227
264020
  for (const row of rows) {
@@ -263306,6 +264099,7 @@ async function runList(input) {
263306
264099
  identifier: name,
263307
264100
  projectRoot,
263308
264101
  linearTeamOverride: input.linearTeamOverride,
264102
+ linearFilterOverride: input.linearFilterOverride,
263309
264103
  linearAssigneeOverride: input.linearAssigneeOverride
263310
264104
  });
263311
264105
  return;
@@ -263316,7 +264110,7 @@ async function runList(input) {
263316
264110
  const apiKey = process.env["LINEAR_API_KEY"];
263317
264111
  const indicators = cfg.linear.indicators;
263318
264112
  const team = input.linearTeamOverride || cfg.linear.team;
263319
- const assignee = input.linearAssigneeOverride || cfg.linear.assignee;
264113
+ const { assignee, anyAssignee } = resolveLinearFilter(input.linearFilterOverride, input.linearAssigneeOverride, cfg.linear.filter);
263320
264114
  const buckets = buildBuckets(indicators);
263321
264115
  const anyConfigured = buckets.some((b2) => b2.indicator && b2.indicator.filter.length > 0);
263322
264116
  if (!anyConfigured) {
@@ -263338,20 +264132,36 @@ Linear: LINEAR_API_KEY not set \u2014 cannot fetch tickets. Configured buckets:
263338
264132
  }
263339
264133
  return;
263340
264134
  }
264135
+ let ticketNumbers = [];
264136
+ try {
264137
+ ticketNumbers = resolveTicketNumbers(input.ticketTokens ?? [], team);
264138
+ } catch (err) {
264139
+ process.stderr.write(`Error: ${formatTicketError(err)}
264140
+ `);
264141
+ process.exitCode = 1;
264142
+ return;
264143
+ }
263341
264144
  if (team)
263342
264145
  process.stdout.write(`
263343
264146
  team: ${team}
263344
264147
  `);
263345
- if (assignee)
263346
- process.stdout.write(`assignee: ${assignee}
264148
+ process.stdout.write(`assignee: ${anyAssignee ? "any" : assignee ?? "*"}
264149
+ `);
264150
+ if (ticketNumbers.length > 0)
264151
+ process.stdout.write(`ticket: ${ticketNumbers.join(", ")}
263347
264152
  `);
263348
- await fetchAndPrintLinear(apiKey, buckets, team, assignee, projectRoot, localCmdRunner, cfg.ignoreCiChecks, input.checks, input.review);
264153
+ await fetchAndPrintLinear(apiKey, buckets, team, assignee, anyAssignee, projectRoot, localCmdRunner, cfg.ignoreCiChecks, input.checks, input.review, ticketNumbers);
263349
264154
  }
263350
264155
  function normalizeIdentifier(input) {
263351
- const match = input.match(/^([A-Za-z]+)-(\d+)(?:-.*)?$/);
263352
- if (!match)
264156
+ let parsed;
264157
+ try {
264158
+ parsed = parseTicketIdentifier(input);
264159
+ } catch {
263353
264160
  return null;
263354
- return `${match[1].toUpperCase()}-${match[2]}`;
264161
+ }
264162
+ if (parsed.teamKey === null)
264163
+ return null;
264164
+ return `${parsed.teamKey}-${parsed.number}`;
263355
264165
  }
263356
264166
  async function fetchIssueByIdentifier(apiKey, identifier) {
263357
264167
  const match = identifier.match(/^([A-Z]+)-(\d+)$/);
@@ -263397,8 +264207,10 @@ function markerMatches(issue2, marker) {
263397
264207
  }
263398
264208
  return false;
263399
264209
  }
263400
- function assigneeMatches(issue2, assignee) {
263401
- if (!assignee)
264210
+ function assigneeMatches(issue2, assignee, anyAssignee) {
264211
+ if (anyAssignee)
264212
+ return true;
264213
+ if (!assignee || assignee === "unassigned")
263402
264214
  return issue2.assignee === null;
263403
264215
  const a = issue2.assignee;
263404
264216
  if (!a)
@@ -263421,7 +264233,8 @@ async function runListDebug(input) {
263421
264233
  const cfg = await loadRalphyConfig(projectRoot);
263422
264234
  const indicators = cfg.linear.indicators;
263423
264235
  const team = input.linearTeamOverride || cfg.linear.team;
263424
- const assignee = input.linearAssigneeOverride || cfg.linear.assignee;
264236
+ const { assignee, anyAssignee } = resolveLinearFilter(input.linearFilterOverride, input.linearAssigneeOverride, cfg.linear.filter);
264237
+ const assigneeLabel = anyAssignee ? "any" : assignee ?? "*";
263425
264238
  const normalized = normalizeIdentifier(identifier);
263426
264239
  if (!normalized) {
263427
264240
  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).
@@ -263461,8 +264274,8 @@ Per-bucket diagnostics:
263461
264274
  if (team && issue2.team?.key && issue2.team.key !== team) {
263462
264275
  reasons.push(`team mismatch: issue=${issue2.team.key}, config=${team}`);
263463
264276
  }
263464
- if (!assigneeMatches(issue2, assignee)) {
263465
- reasons.push(`assignee mismatch: issue=${issue2.assignee ? issue2.assignee.email ?? issue2.assignee.id : "unassigned"}, config=${assignee}`);
264277
+ if (!assigneeMatches(issue2, assignee, anyAssignee)) {
264278
+ reasons.push(`assignee mismatch: issue=${issue2.assignee ? issue2.assignee.email ?? issue2.assignee.id : "unassigned"}, config=${assigneeLabel}`);
263466
264279
  }
263467
264280
  const includeMatches = bucket.indicator.filter.some((m2) => markerMatches(issue2, m2));
263468
264281
  if (!includeMatches) {
@@ -263495,12 +264308,15 @@ Per-bucket diagnostics:
263495
264308
  var localCmdRunner;
263496
264309
  var init_list = __esm(() => {
263497
264310
  init_context();
264311
+ init_workflow();
263498
264312
  init_worktree();
263499
264313
  init_config();
263500
264314
  init_linear();
263501
264315
  init_pr_status();
263502
264316
  init_pr_url();
263503
264317
  init_list_sort();
264318
+ init_ordering();
264319
+ init_queue_order();
263504
264320
  init_ci();
263505
264321
  init_linear_client();
263506
264322
  init_indicators();
@@ -263806,11 +264622,13 @@ async function main3(argv) {
263806
264622
  await runWithContext(createDefaultContext({ layout, args }), async () => {
263807
264623
  await runList2({
263808
264624
  linearTeamOverride: args.linearTeam,
264625
+ linearFilterOverride: args.linearFilter,
263809
264626
  linearAssigneeOverride: args.linearAssignee,
263810
264627
  debug: args.debug,
263811
264628
  name: args.name,
263812
264629
  checks: args.checks,
263813
- review: args.review
264630
+ review: args.review,
264631
+ ticketTokens: args.ticketTokens
263814
264632
  });
263815
264633
  });
263816
264634
  return typeof process.exitCode === "number" ? process.exitCode : 0;
@@ -263830,6 +264648,19 @@ async function main3(argv) {
263830
264648
  `);
263831
264649
  return 0;
263832
264650
  }
264651
+ if (args.ticketTokens.length > 0) {
264652
+ const { loadRalphyConfig: loadRalphyConfig2 } = await Promise.resolve().then(() => (init_config(), exports_config));
264653
+ const { resolveTicketNumbers: resolveTicketNumbers2, formatTicketError: formatTicketError2 } = await Promise.resolve().then(() => (init_linear_client(), exports_linear_client));
264654
+ const cfg = await loadRalphyConfig2(projectRoot);
264655
+ const team = args.linearTeam || cfg.linear.team;
264656
+ try {
264657
+ resolveTicketNumbers2(args.ticketTokens, team);
264658
+ } catch (err) {
264659
+ process.stderr.write(formatTicketError2(err) + `
264660
+ `);
264661
+ return 1;
264662
+ }
264663
+ }
263833
264664
  await mkdir15(statesDir, { recursive: true });
263834
264665
  await mkdir15(tasksDir, { recursive: true });
263835
264666
  await mkdir15(join39(projectRoot, ".ralph"), { recursive: true });