@neriros/ralphy 3.10.4 → 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 +178 -43
  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.4")
18932
- return "3.10.4";
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 = 2, 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", [
@@ -80635,9 +80649,9 @@ var init_schema = __esm(() => {
80635
80649
  ignoreCiChecks: exports_external.array(exports_external.string()).default([]),
80636
80650
  engine: exports_external.enum(["claude", "codex"]).default("claude"),
80637
80651
  model: exports_external.enum(["haiku", "sonnet", "opus"]).default("opus"),
80638
- linear: exports_external.object({
80652
+ linear: exports_external.preprocess(foldLegacyAssignee, exports_external.object({
80639
80653
  team: exports_external.string().optional(),
80640
- assignee: exports_external.string().optional(),
80654
+ filter: exports_external.string().default("assignee = me"),
80641
80655
  postComments: exports_external.boolean().default(true),
80642
80656
  updateEveryIterations: exports_external.number().int().nonnegative().default(10),
80643
80657
  mentionTrigger: exports_external.boolean().default(true),
@@ -80657,7 +80671,8 @@ var init_schema = __esm(() => {
80657
80671
  maxConfirmationRounds: 3
80658
80672
  }),
80659
80673
  indicators: IndicatorsSchema.default({})
80660
- }).strict().default({
80674
+ }).strict()).default({
80675
+ filter: "assignee = me",
80661
80676
  postComments: true,
80662
80677
  updateEveryIterations: 10,
80663
80678
  mentionTrigger: true,
@@ -80746,7 +80761,7 @@ var init_schema = __esm(() => {
80746
80761
  var FRONTMATTER_RE, DEFAULT_WORKFLOW_MD = `---
80747
80762
  # WORKFLOW.md schema version \u2014 managed by \`ralphy init\`. When a newer version
80748
80763
  # ships, re-running init migrates this file and fills in the new settings.
80749
- version: 1
80764
+ version: 2
80750
80765
 
80751
80766
  project:
80752
80767
  name: ralphy
@@ -80810,6 +80825,7 @@ preExistingErrorCheck:
80810
80825
  outputCharLimit: 4000
80811
80826
 
80812
80827
  linear:
80828
+ filter: assignee = me
80813
80829
  postComments: true
80814
80830
  updateEveryIterations: 10
80815
80831
  mentionTrigger: true
@@ -81056,7 +81072,7 @@ function modelOptionValues() {
81056
81072
  const field = findField("model");
81057
81073
  return field && field.spec.kind === "select" ? field.spec.options.map((o) => o.value) : [];
81058
81074
  }
81059
- var PROMPT_BODY_FIELD_ID = "promptBody", REPO_LINK_FIELD_ID = "repo.link", yes = () => ({ kind: "confirm", defaultChoice: "confirm" }), no = () => ({ kind: "confirm", defaultChoice: "cancel" }), PROJECT_NAME, LINEAR_TEAM, REPO_LINK, 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;
81060
81076
  var init_fields = __esm(() => {
81061
81077
  PROJECT_NAME = {
81062
81078
  id: "project.name",
@@ -81079,15 +81095,15 @@ var init_fields = __esm(() => {
81079
81095
  spec: yes(),
81080
81096
  when: (answers) => typeof answers["repo.name"] === "string" && answers["repo.name"] !== ""
81081
81097
  };
81082
- LINEAR_ASSIGNEE = {
81083
- id: "linear.assignee",
81084
- label: "Linear assignee",
81085
- hint: "user id, email, 'me', 'any', or 'unassigned' \u2014 blank means unassigned",
81086
- 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.",
81087
- emptyLabel: "unassigned",
81088
- spec: { kind: "text" }
81098
+ LINEAR_FILTER = {
81099
+ id: "linear.filter",
81100
+ label: "Linear filter",
81101
+ hint: "e.g. 'assignee = me', 'assignee = any', 'assignee = unassigned', or an email/user-id",
81102
+ description: "Global filter applied to every Linear ticket fetch. The only clause today is 'assignee = <value>', where <value> is 'me' (issues assigned to you), 'any' (regardless of assignee), 'unassigned', a Linear user id, or an email. Blank defaults to 'assignee = me'.",
81103
+ emptyLabel: "assignee = me",
81104
+ spec: { kind: "text", placeholder: "assignee = me" }
81089
81105
  };
81090
- QUICK_FIELDS = [PROJECT_NAME, LINEAR_TEAM, REPO_LINK, LINEAR_ASSIGNEE];
81106
+ QUICK_FIELDS = [PROJECT_NAME, LINEAR_TEAM, REPO_LINK, LINEAR_FILTER];
81091
81107
  CUSTOMIZED_FIELDS = [
81092
81108
  PROJECT_NAME,
81093
81109
  {
@@ -81336,7 +81352,7 @@ var init_fields = __esm(() => {
81336
81352
  },
81337
81353
  LINEAR_TEAM,
81338
81354
  REPO_LINK,
81339
- LINEAR_ASSIGNEE,
81355
+ LINEAR_FILTER,
81340
81356
  {
81341
81357
  id: "linear.postComments",
81342
81358
  label: "Post progress comments on the Linear issue?",
@@ -81725,6 +81741,34 @@ function describeApprovalMarker(indicator) {
81725
81741
  return `${phrases.slice(0, -1).join(", ")}, or ${phrases[phrases.length - 1]}`;
81726
81742
  }
81727
81743
 
81744
+ // packages/workflow/src/linear-filter.ts
81745
+ function parseLinearFilter(filter2) {
81746
+ const trimmed = filter2.trim();
81747
+ if (trimmed === "")
81748
+ return { assignee: "me" };
81749
+ const eq = trimmed.indexOf("=");
81750
+ if (eq < 0) {
81751
+ throw new Error(`Invalid linear.filter "${filter2}": expected "<key> = <value>" (e.g. "assignee = me").`);
81752
+ }
81753
+ const key = trimmed.slice(0, eq).trim().toLowerCase();
81754
+ const value = trimmed.slice(eq + 1).trim();
81755
+ if (!SUPPORTED_KEYS.has(key)) {
81756
+ throw new Error(`Unrecognized linear.filter key "${key}" in "${filter2}". Supported keys: ${[...SUPPORTED_KEYS].join(", ")}.`);
81757
+ }
81758
+ const lower = value.toLowerCase();
81759
+ if (lower === "any")
81760
+ return { anyAssignee: true };
81761
+ if (lower === "" || lower === "unassigned")
81762
+ return { assignee: "unassigned" };
81763
+ if (lower === "me")
81764
+ return { assignee: "me" };
81765
+ return { assignee: value };
81766
+ }
81767
+ var SUPPORTED_KEYS;
81768
+ var init_linear_filter = __esm(() => {
81769
+ SUPPORTED_KEYS = new Set(["assignee"]);
81770
+ });
81771
+
81728
81772
  // packages/workflow/src/workflow.ts
81729
81773
  var exports_workflow = {};
81730
81774
  __export(exports_workflow, {
@@ -81733,6 +81777,7 @@ __export(exports_workflow, {
81733
81777
  renderWorkflowPrompt: () => renderWorkflowPrompt,
81734
81778
  renderTemplate: () => renderTemplate,
81735
81779
  parseWorkflow: () => parseWorkflow,
81780
+ parseLinearFilter: () => parseLinearFilter,
81736
81781
  matchesIndicator: () => matchesIndicator,
81737
81782
  loadWorkflow: () => loadWorkflow,
81738
81783
  ensureWorkflow: () => ensureWorkflow,
@@ -81896,6 +81941,7 @@ var init_workflow = __esm(() => {
81896
81941
  init_wizard();
81897
81942
  init_schema();
81898
81943
  init_default();
81944
+ init_linear_filter();
81899
81945
  import_yaml2 = __toESM(require_dist(), 1);
81900
81946
  });
81901
81947
 
@@ -83968,6 +84014,11 @@ var init_migrations = __esm(() => {
83968
84014
  version: 2,
83969
84015
  description: "Ralphy now detects the current git repo and records it in WORKFLOW.md, " + "linking it to your Linear team. Confirm the detected repo to adopt it.",
83970
84016
  fields: ["repo.link"]
84017
+ },
84018
+ {
84019
+ version: 3,
84020
+ description: "The per-workflow `linear.assignee` setting is replaced by a global " + "`linear.filter` expression (e.g. `assignee = me`) applied to every " + "ticket fetch. Existing `assignee` values are folded in automatically; " + "note that an empty filter now defaults to `assignee = me` (it previously " + "meant unassigned-only).",
84021
+ fields: ["linear.filter"]
83971
84022
  }
83972
84023
  ];
83973
84024
  LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max2, migration) => Math.max(max2, migration.version), 0);
@@ -84055,8 +84106,8 @@ function initialValuesFromConfig(config2) {
84055
84106
  values2["useWorktree"] = config2.useWorktree;
84056
84107
  if (config2.linear.team)
84057
84108
  values2["linear.team"] = config2.linear.team;
84058
- if (config2.linear.assignee)
84059
- values2["linear.assignee"] = config2.linear.assignee;
84109
+ if (config2.linear.filter)
84110
+ values2["linear.filter"] = config2.linear.filter;
84060
84111
  return values2;
84061
84112
  }
84062
84113
  async function promptEditOrExit() {
@@ -101403,6 +101454,7 @@ async function parseAgentArgs(argv) {
101403
101454
  ...common2,
101404
101455
  mode: "agent",
101405
101456
  linearTeam: "",
101457
+ linearFilter: "",
101406
101458
  linearAssignee: "",
101407
101459
  pollInterval: 0,
101408
101460
  concurrency: 0,
@@ -101423,6 +101475,7 @@ async function parseAgentArgs(argv) {
101423
101475
  };
101424
101476
  const state = emptyParseState();
101425
101477
  let expectLinearTeam = false;
101478
+ let expectLinearFilter = false;
101426
101479
  let expectLinearAssignee = false;
101427
101480
  let expectPollInterval = false;
101428
101481
  let expectConcurrency = false;
@@ -101436,6 +101489,11 @@ async function parseAgentArgs(argv) {
101436
101489
  expectLinearTeam = false;
101437
101490
  continue;
101438
101491
  }
101492
+ if (expectLinearFilter) {
101493
+ result2.linearFilter = arg;
101494
+ expectLinearFilter = false;
101495
+ continue;
101496
+ }
101439
101497
  if (expectLinearAssignee) {
101440
101498
  result2.linearAssignee = arg;
101441
101499
  expectLinearAssignee = false;
@@ -101481,6 +101539,9 @@ async function parseAgentArgs(argv) {
101481
101539
  case "--linear-team":
101482
101540
  expectLinearTeam = true;
101483
101541
  break;
101542
+ case "--linear-filter":
101543
+ expectLinearFilter = true;
101544
+ break;
101484
101545
  case "--linear-assignee":
101485
101546
  expectLinearAssignee = true;
101486
101547
  break;
@@ -101605,7 +101666,8 @@ var init_cli2 = __esm(() => {
101605
101666
  " --log Log raw engine stream",
101606
101667
  " --verbose Verbose output",
101607
101668
  " --linear-team <key> Linear team key (e.g. ENG)",
101608
- " --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",
101609
101671
  " --poll-interval <s> Seconds between Linear polls (default: 60)",
101610
101672
  " --concurrency <n> Max concurrent task loops (default: 1)",
101611
101673
  " --worktree Run each task in its own git worktree",
@@ -102290,9 +102352,11 @@ async function fetchMentionScanIssues(apiKey, spec) {
102290
102352
  const where = branches.length === 1 ? { ...branches[0] } : { or: branches };
102291
102353
  if (spec.team)
102292
102354
  where.team = { key: { eq: spec.team } };
102293
- if (spec.assignee) {
102355
+ if (spec.anyAssignee || spec.assignee === "any") {} else if (spec.assignee) {
102294
102356
  if (spec.assignee === "me")
102295
102357
  where.assignee = { isMe: { eq: true } };
102358
+ else if (spec.assignee === "unassigned")
102359
+ where.assignee = { null: true };
102296
102360
  else if (spec.assignee.includes("@"))
102297
102361
  where.assignee = { email: { eq: spec.assignee } };
102298
102362
  else
@@ -104893,6 +104957,7 @@ class AgentCoordinator {
104893
104957
  ciFailed: 0,
104894
104958
  review: 0,
104895
104959
  mentions: mentions.length,
104960
+ quarantined: 0,
104896
104961
  awaiting: awaitingCount
104897
104962
  };
104898
104963
  const found2 = buckets2.todo + buckets2.inProgress + buckets2.mentions + buckets2.awaiting;
@@ -104987,6 +105052,7 @@ class AgentCoordinator {
104987
105052
  ciFailed: prStatus.ciFailed,
104988
105053
  review: 0,
104989
105054
  mentions: mentions.length,
105055
+ quarantined: prStatus.quarantined,
104990
105056
  awaiting: awaitingCount
104991
105057
  };
104992
105058
  const found = buckets.todo + buckets.inProgress + buckets.conflicted + buckets.ciFailed + buckets.mentions + buckets.awaiting;
@@ -105182,6 +105248,7 @@ class AgentCoordinator {
105182
105248
  return counts;
105183
105249
  const preQueue = this.queue.map((q) => ({ id: q.issue.id, trigger: q.trigger }));
105184
105250
  const preWorkers = this.workers.map((w) => ({ id: w.issueId, trigger: w.trigger }));
105251
+ const tracker = this.opts.prTracker;
105185
105252
  for (const issue2 of candidates) {
105186
105253
  if (this.workers.some((w) => w.issueId === issue2.id))
105187
105254
  continue;
@@ -105189,6 +105256,13 @@ class AgentCoordinator {
105189
105256
  continue;
105190
105257
  if (this.queue.some((q) => q.issue.id === issue2.id))
105191
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
+ }
105192
105266
  let pr;
105193
105267
  try {
105194
105268
  pr = await this.deps.checkPrStatus(issue2);
@@ -105208,10 +105282,18 @@ class AgentCoordinator {
105208
105282
  }
105209
105283
  }
105210
105284
  if (pr.status === "conflicted") {
105285
+ if (tracker?.isBailed(issue2.identifier)) {
105286
+ counts.quarantined += 1;
105287
+ continue;
105288
+ }
105289
+ counts.conflicted += 1;
105211
105290
  if (this.conflictNotified.has(issue2.id))
105212
105291
  continue;
105213
- 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;
105214
105295
  continue;
105296
+ }
105215
105297
  emitCapture(this.bus, "agent_conflict_detected", { issue_identifier: issue2.identifier });
105216
105298
  this.conflictNotified.add(issue2.id);
105217
105299
  this.deps.onLog(` ${issue2.identifier}: PR ${pr.url} conflicting \u2014 queued (conflict-fix)`, "yellow");
@@ -105230,14 +105312,21 @@ class AgentCoordinator {
105230
105312
  trigger: "conflict-fix",
105231
105313
  priority: defaultPriorityFor("conflict-fix")
105232
105314
  });
105233
- counts.conflicted += 1;
105234
105315
  continue;
105235
105316
  }
105236
105317
  if (pr.status === "ci_failed") {
105318
+ if (tracker?.isBailed(issue2.identifier)) {
105319
+ counts.quarantined += 1;
105320
+ continue;
105321
+ }
105322
+ counts.ciFailed += 1;
105237
105323
  if (this.ciFailedNotified.has(issue2.id))
105238
105324
  continue;
105239
- 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;
105240
105328
  continue;
105329
+ }
105241
105330
  emitCapture(this.bus, "agent_ci_failed_detected", { issue_identifier: issue2.identifier });
105242
105331
  this.ciFailedNotified.add(issue2.id);
105243
105332
  this.deps.onLog(` ${issue2.identifier}: PR ${pr.url} CI failing \u2014 queued (ci-fix)`, "yellow");
@@ -105256,7 +105345,6 @@ class AgentCoordinator {
105256
105345
  trigger: "ci-fix",
105257
105346
  priority: defaultPriorityFor("ci-fix")
105258
105347
  });
105259
- counts.ciFailed += 1;
105260
105348
  }
105261
105349
  }
105262
105350
  for (const q of preQueue) {
@@ -105273,6 +105361,16 @@ class AgentCoordinator {
105273
105361
  }
105274
105362
  return counts;
105275
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
+ }
105276
105374
  async prTrackerBail(issue2, prUrl, reason) {
105277
105375
  const tracker = this.opts.prTracker;
105278
105376
  if (!tracker)
@@ -105652,7 +105750,12 @@ function triggerToFlowId(trigger) {
105652
105750
  return "review-followup";
105653
105751
  return "implement";
105654
105752
  }
105655
- 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 = () => ({
105656
105759
  found: 0,
105657
105760
  added: 0,
105658
105761
  buckets: {
@@ -105662,6 +105765,7 @@ var emptyPrStatus = () => ({ mergeable: 0, conflicted: 0, ciFailed: 0 }), emptyP
105662
105765
  ciFailed: 0,
105663
105766
  review: 0,
105664
105767
  mentions: 0,
105768
+ quarantined: 0,
105665
105769
  awaiting: 0
105666
105770
  },
105667
105771
  prStatus: emptyPrStatus(),
@@ -105669,6 +105773,7 @@ var emptyPrStatus = () => ({ mergeable: 0, conflicted: 0, ciFailed: 0 }), emptyP
105669
105773
  flow: {}
105670
105774
  });
105671
105775
  var init_coordinator = __esm(() => {
105776
+ init_types2();
105672
105777
  init_post_task();
105673
105778
  init_queue_order();
105674
105779
  init_src();
@@ -106550,10 +106655,10 @@ function unionMarkers(...sets) {
106550
106655
  }
106551
106656
  return out;
106552
106657
  }
106553
- function describeIndicators(indicators, team, assignee) {
106658
+ function describeIndicators(indicators, team, assignee, anyAssignee) {
106554
106659
  const parts = [];
106555
106660
  parts.push(`team=${team ?? "*"}`);
106556
- parts.push(`assignee=${assignee ?? "*"}`);
106661
+ parts.push(`assignee=${anyAssignee ? "any" : assignee ?? "*"}`);
106557
106662
  if (indicators.getTodo) {
106558
106663
  parts.push(`todo=[${indicators.getTodo.filter.map((m) => `${m.type}:${m.value}`).join(",")}]`);
106559
106664
  }
@@ -106568,7 +106673,7 @@ var init_indicators = __esm(() => {
106568
106673
 
106569
106674
  // apps/agent/src/agent/wire/linear-resolvers.ts
106570
106675
  function createLinearResolvers(input) {
106571
- const { apiKey, team, assignee, diag } = input;
106676
+ const { apiKey, team, assignee, anyAssignee, diag } = input;
106572
106677
  const ticketNumbers = input.ticketNumbers ?? [];
106573
106678
  const stateCache = new Map;
106574
106679
  const labelCache = new Map;
@@ -106684,6 +106789,7 @@ function createLinearResolvers(input) {
106684
106789
  const spec = {
106685
106790
  team,
106686
106791
  assignee,
106792
+ anyAssignee,
106687
106793
  include,
106688
106794
  exclude: excl,
106689
106795
  ...ticketNumbers.length > 0 ? { numbers: ticketNumbers } : {}
@@ -107417,6 +107523,7 @@ function createMentionScanner(input) {
107417
107523
  cfg,
107418
107524
  team,
107419
107525
  assignee,
107526
+ anyAssignee,
107420
107527
  indicators,
107421
107528
  projectRoot,
107422
107529
  useWorktree,
@@ -107440,6 +107547,7 @@ function createMentionScanner(input) {
107440
107547
  candidates = await fetchMentionScanIssues(apiKey, {
107441
107548
  team,
107442
107549
  assignee,
107550
+ anyAssignee,
107443
107551
  ...ticketNumbers && ticketNumbers.length > 0 ? { numbers: ticketNumbers } : {},
107444
107552
  indicators: {
107445
107553
  ...indicators.getTodo !== undefined ? { getTodo: indicators.getTodo } : {},
@@ -261179,7 +261287,8 @@ function buildAgentCoordinator(input) {
261179
261287
  const pollInterval = args.pollInterval || cfg.pollIntervalSeconds;
261180
261288
  const indicators = mergeIndicators(cfg.linear.indicators, args.indicators);
261181
261289
  const team = args.linearTeam || cfg.linear.team;
261182
- 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);
261183
261292
  const ticketNumbers = resolveTicketNumbers(args.ticketTokens, team);
261184
261293
  const excludeFromTodo = unionMarkers(indicators.setDone, indicators.setError);
261185
261294
  const gitRunner = input.runners?.git ?? bunGitRunner;
@@ -261216,6 +261325,7 @@ function buildAgentCoordinator(input) {
261216
261325
  apiKey,
261217
261326
  team,
261218
261327
  assignee,
261328
+ anyAssignee,
261219
261329
  diag,
261220
261330
  ...ticketNumbers.length > 0 ? { ticketNumbers } : {}
261221
261331
  });
@@ -261253,6 +261363,7 @@ function buildAgentCoordinator(input) {
261253
261363
  cfg,
261254
261364
  team,
261255
261365
  assignee,
261366
+ anyAssignee,
261256
261367
  indicators,
261257
261368
  projectRoot,
261258
261369
  useWorktree,
@@ -261396,7 +261507,7 @@ function buildAgentCoordinator(input) {
261396
261507
  ...prTracker ? { prTracker } : {}
261397
261508
  });
261398
261509
  coordRef.current = coord;
261399
- const filterDesc = describeIndicators(indicators, team, assignee);
261510
+ const filterDesc = describeIndicators(indicators, team, assignee, anyAssignee);
261400
261511
  const runBaselineGateOnce = createBaselineGateRunner({
261401
261512
  args,
261402
261513
  cfg,
@@ -261432,6 +261543,7 @@ function buildAgentCoordinator(input) {
261432
261543
  };
261433
261544
  }
261434
261545
  var init_wire = __esm(() => {
261546
+ init_workflow();
261435
261547
  init_src2();
261436
261548
  init_coordinator2();
261437
261549
  init_linear();
@@ -262753,6 +262865,19 @@ function AgentMode({
262753
262865
  /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
262754
262866
  color: pollStatus.lastPrStatus.ciFailed > 0 ? "red" : "white",
262755
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
262756
262881
  }, undefined, false, undefined, this)
262757
262882
  ]
262758
262883
  }, undefined, true, undefined, this)
@@ -263779,18 +263904,23 @@ function buildBuckets(indicators) {
263779
263904
  { label: "auto-merge", indicator: indicators.getAutoMerge, exclude: [] }
263780
263905
  ];
263781
263906
  }
263782
- async function fetchBucketIssues(apiKey, bucket, team, assignee, ticketNumbers) {
263907
+ async function fetchBucketIssues(apiKey, bucket, team, assignee, anyAssignee, ticketNumbers) {
263783
263908
  if (!bucket.indicator || bucket.indicator.filter.length === 0)
263784
263909
  return [];
263785
263910
  const spec = {
263786
263911
  team,
263787
263912
  assignee,
263913
+ anyAssignee,
263788
263914
  include: bucket.indicator.filter,
263789
263915
  exclude: bucket.exclude,
263790
263916
  ...ticketNumbers.length > 0 ? { numbers: ticketNumbers } : {}
263791
263917
  };
263792
263918
  return fetchOpenIssues(apiKey, spec);
263793
263919
  }
263920
+ function resolveLinearFilter(filterOverride, assigneeOverride, configFilter) {
263921
+ const effective = filterOverride || (assigneeOverride ? `assignee = ${assigneeOverride}` : "") || configFilter;
263922
+ return parseLinearFilter(effective);
263923
+ }
263794
263924
  function formatReviewCell(prUrl, count) {
263795
263925
  if (!prUrl)
263796
263926
  return "-";
@@ -263837,13 +263967,13 @@ function backlogRankByIssueId(issues) {
263837
263967
  ordered.forEach((o, i) => rankById.set(o.id, i));
263838
263968
  return rankById;
263839
263969
  }
263840
- async function fetchAndPrintLinear(apiKey, buckets, team, assignee, cwd2, runner, ignoreCiChecks = [], checks3 = false, review = false, ticketNumbers = []) {
263970
+ async function fetchAndPrintLinear(apiKey, buckets, team, assignee, anyAssignee, cwd2, runner, ignoreCiChecks = [], checks3 = false, review = false, ticketNumbers = []) {
263841
263971
  const bucketResults = await Promise.all(buckets.map(async (bucket) => {
263842
263972
  if (!bucket.indicator || bucket.indicator.filter.length === 0) {
263843
263973
  return { bucket, issues: [], error: null };
263844
263974
  }
263845
263975
  try {
263846
- const issues = await fetchBucketIssues(apiKey, bucket, team, assignee, ticketNumbers);
263976
+ const issues = await fetchBucketIssues(apiKey, bucket, team, assignee, anyAssignee, ticketNumbers);
263847
263977
  return { bucket, issues, error: null };
263848
263978
  } catch (err) {
263849
263979
  return {
@@ -263969,6 +264099,7 @@ async function runList(input) {
263969
264099
  identifier: name,
263970
264100
  projectRoot,
263971
264101
  linearTeamOverride: input.linearTeamOverride,
264102
+ linearFilterOverride: input.linearFilterOverride,
263972
264103
  linearAssigneeOverride: input.linearAssigneeOverride
263973
264104
  });
263974
264105
  return;
@@ -263979,7 +264110,7 @@ async function runList(input) {
263979
264110
  const apiKey = process.env["LINEAR_API_KEY"];
263980
264111
  const indicators = cfg.linear.indicators;
263981
264112
  const team = input.linearTeamOverride || cfg.linear.team;
263982
- const assignee = input.linearAssigneeOverride || cfg.linear.assignee;
264113
+ const { assignee, anyAssignee } = resolveLinearFilter(input.linearFilterOverride, input.linearAssigneeOverride, cfg.linear.filter);
263983
264114
  const buckets = buildBuckets(indicators);
263984
264115
  const anyConfigured = buckets.some((b2) => b2.indicator && b2.indicator.filter.length > 0);
263985
264116
  if (!anyConfigured) {
@@ -264014,13 +264145,12 @@ Linear: LINEAR_API_KEY not set \u2014 cannot fetch tickets. Configured buckets:
264014
264145
  process.stdout.write(`
264015
264146
  team: ${team}
264016
264147
  `);
264017
- if (assignee)
264018
- process.stdout.write(`assignee: ${assignee}
264148
+ process.stdout.write(`assignee: ${anyAssignee ? "any" : assignee ?? "*"}
264019
264149
  `);
264020
264150
  if (ticketNumbers.length > 0)
264021
264151
  process.stdout.write(`ticket: ${ticketNumbers.join(", ")}
264022
264152
  `);
264023
- await fetchAndPrintLinear(apiKey, buckets, team, assignee, projectRoot, localCmdRunner, cfg.ignoreCiChecks, input.checks, input.review, ticketNumbers);
264153
+ await fetchAndPrintLinear(apiKey, buckets, team, assignee, anyAssignee, projectRoot, localCmdRunner, cfg.ignoreCiChecks, input.checks, input.review, ticketNumbers);
264024
264154
  }
264025
264155
  function normalizeIdentifier(input) {
264026
264156
  let parsed;
@@ -264077,8 +264207,10 @@ function markerMatches(issue2, marker) {
264077
264207
  }
264078
264208
  return false;
264079
264209
  }
264080
- function assigneeMatches(issue2, assignee) {
264081
- if (!assignee)
264210
+ function assigneeMatches(issue2, assignee, anyAssignee) {
264211
+ if (anyAssignee)
264212
+ return true;
264213
+ if (!assignee || assignee === "unassigned")
264082
264214
  return issue2.assignee === null;
264083
264215
  const a = issue2.assignee;
264084
264216
  if (!a)
@@ -264101,7 +264233,8 @@ async function runListDebug(input) {
264101
264233
  const cfg = await loadRalphyConfig(projectRoot);
264102
264234
  const indicators = cfg.linear.indicators;
264103
264235
  const team = input.linearTeamOverride || cfg.linear.team;
264104
- 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 ?? "*";
264105
264238
  const normalized = normalizeIdentifier(identifier);
264106
264239
  if (!normalized) {
264107
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).
@@ -264141,8 +264274,8 @@ Per-bucket diagnostics:
264141
264274
  if (team && issue2.team?.key && issue2.team.key !== team) {
264142
264275
  reasons.push(`team mismatch: issue=${issue2.team.key}, config=${team}`);
264143
264276
  }
264144
- if (!assigneeMatches(issue2, assignee)) {
264145
- 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}`);
264146
264279
  }
264147
264280
  const includeMatches = bucket.indicator.filter.some((m2) => markerMatches(issue2, m2));
264148
264281
  if (!includeMatches) {
@@ -264175,6 +264308,7 @@ Per-bucket diagnostics:
264175
264308
  var localCmdRunner;
264176
264309
  var init_list = __esm(() => {
264177
264310
  init_context();
264311
+ init_workflow();
264178
264312
  init_worktree();
264179
264313
  init_config();
264180
264314
  init_linear();
@@ -264488,6 +264622,7 @@ async function main3(argv) {
264488
264622
  await runWithContext(createDefaultContext({ layout, args }), async () => {
264489
264623
  await runList2({
264490
264624
  linearTeamOverride: args.linearTeam,
264625
+ linearFilterOverride: args.linearFilter,
264491
264626
  linearAssigneeOverride: args.linearAssignee,
264492
264627
  debug: args.debug,
264493
264628
  name: args.name,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neriros/ralphy",
3
- "version": "3.10.4",
3
+ "version": "3.10.5",
4
4
  "description": "An iterative AI task execution framework. Orchestrates multi-phase autonomous work using Claude or Codex engines.",
5
5
  "keywords": [
6
6
  "agent",