@neriros/ralphy 3.10.18 → 3.10.19

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 +219 -55
  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.18")
18932
- return "3.10.18";
18931
+ if ("3.10.19")
18932
+ return "3.10.19";
18933
18933
  } catch {}
18934
18934
  const dirsToTry = [];
18935
18935
  try {
@@ -81308,6 +81308,20 @@ var init_zod = __esm(() => {
81308
81308
  });
81309
81309
 
81310
81310
  // packages/workflow/src/schema.ts
81311
+ function normalizeNegationShorthand(v) {
81312
+ if (!v || typeof v !== "object" || Array.isArray(v))
81313
+ return v;
81314
+ const obj = v;
81315
+ const t = obj["type"];
81316
+ const negatable = t === "label" || t === "status" || t === "project";
81317
+ if (!negatable || obj["negate"] !== undefined)
81318
+ return v;
81319
+ const value = obj["value"];
81320
+ if (typeof value === "string" && value.startsWith("!")) {
81321
+ return { ...obj, value: value.slice(1), negate: true };
81322
+ }
81323
+ return v;
81324
+ }
81311
81325
  function foldLegacyAssignee(v) {
81312
81326
  if (!v || typeof v !== "object" || Array.isArray(v))
81313
81327
  return v;
@@ -81325,21 +81339,39 @@ function foldLegacyAssignee(v) {
81325
81339
  var CURRENT_WORKFLOW_VERSION = 8, MarkerSchema, FilterMarkerSchema, LinearFilterSchema, SET_INDICATOR_KEYS, GetIndicatorSchema, SetIndicatorSchema, IndicatorsSchema, ProjectSchema, CommandsSchema, DEFAULT_META_ONLY_FILES, BoundariesSchema, WorkflowConfigSchema;
81326
81340
  var init_schema = __esm(() => {
81327
81341
  init_zod();
81328
- MarkerSchema = exports_external.discriminatedUnion("type", [
81342
+ MarkerSchema = exports_external.preprocess(normalizeNegationShorthand, exports_external.discriminatedUnion("type", [
81329
81343
  exports_external.object({
81330
81344
  type: exports_external.literal("label"),
81331
81345
  value: exports_external.string().min(1),
81332
- group: exports_external.string().min(1).optional()
81346
+ group: exports_external.string().min(1).optional(),
81347
+ negate: exports_external.boolean().optional()
81333
81348
  }),
81334
- exports_external.object({ type: exports_external.literal("status"), value: exports_external.string().min(1) }).strict(),
81349
+ exports_external.object({
81350
+ type: exports_external.literal("status"),
81351
+ value: exports_external.string().min(1),
81352
+ negate: exports_external.boolean().optional()
81353
+ }).strict(),
81335
81354
  exports_external.object({ type: exports_external.literal("attachment"), value: exports_external.string().min(1) }).strict(),
81336
- exports_external.object({ type: exports_external.literal("project"), value: exports_external.string().min(1) }).strict(),
81355
+ exports_external.object({
81356
+ type: exports_external.literal("project"),
81357
+ value: exports_external.string().min(1),
81358
+ negate: exports_external.boolean().optional()
81359
+ }).strict(),
81337
81360
  exports_external.object({ type: exports_external.literal("comment"), value: exports_external.string().min(1) }).strict()
81338
- ]);
81339
- FilterMarkerSchema = exports_external.discriminatedUnion("type", [
81340
- exports_external.object({ type: exports_external.literal("label"), value: exports_external.string().min(1) }).strict(),
81361
+ ]));
81362
+ FilterMarkerSchema = exports_external.preprocess(normalizeNegationShorthand, exports_external.discriminatedUnion("type", [
81363
+ exports_external.object({
81364
+ type: exports_external.literal("label"),
81365
+ value: exports_external.string().min(1),
81366
+ negate: exports_external.boolean().optional()
81367
+ }).strict(),
81368
+ exports_external.object({
81369
+ type: exports_external.literal("project"),
81370
+ value: exports_external.string().min(1),
81371
+ negate: exports_external.boolean().optional()
81372
+ }).strict(),
81341
81373
  exports_external.object({ type: exports_external.literal("assignee"), value: exports_external.string().min(1) }).strict()
81342
- ]);
81374
+ ]));
81343
81375
  LinearFilterSchema = exports_external.array(FilterMarkerSchema).superRefine((markers, ctx) => {
81344
81376
  const assigneeCount = markers.filter((m) => m.type === "assignee").length;
81345
81377
  if (assigneeCount > 1) {
@@ -81348,6 +81380,13 @@ var init_schema = __esm(() => {
81348
81380
  message: `linear.filter allows at most one "assignee" clause, found ${assigneeCount}.`
81349
81381
  });
81350
81382
  }
81383
+ const positiveProjects = markers.filter((m) => m.type === "project" && !m.negate).length;
81384
+ if (positiveProjects > 1) {
81385
+ ctx.addIssue({
81386
+ code: exports_external.ZodIssueCode.custom,
81387
+ message: `linear.filter allows at most one positive "project" clause, found ${positiveProjects}.`
81388
+ });
81389
+ }
81351
81390
  }).default([{ type: "assignee", value: "me" }]);
81352
81391
  SET_INDICATOR_KEYS = [
81353
81392
  "setInProgress",
@@ -81381,6 +81420,14 @@ var init_schema = __esm(() => {
81381
81420
  continue;
81382
81421
  const markers = Array.isArray(clear) ? clear : [clear];
81383
81422
  for (const m of markers) {
81423
+ if ("negate" in m && m.negate) {
81424
+ ctx.addIssue({
81425
+ code: exports_external.ZodIssueCode.custom,
81426
+ path: [key],
81427
+ message: `${key} cannot use a negated marker \u2014 negation is only meaningful in getX filters`
81428
+ });
81429
+ break;
81430
+ }
81384
81431
  if (m.type === "comment")
81385
81432
  continue;
81386
81433
  if (m.type !== "label") {
@@ -81399,6 +81446,14 @@ var init_schema = __esm(() => {
81399
81446
  continue;
81400
81447
  const markers = Array.isArray(set3) ? set3 : [set3];
81401
81448
  for (const m of markers) {
81449
+ if ("negate" in m && m.negate) {
81450
+ ctx.addIssue({
81451
+ code: exports_external.ZodIssueCode.custom,
81452
+ path: [key],
81453
+ message: `${key} cannot use a negated marker \u2014 negation is only meaningful in getX filters`
81454
+ });
81455
+ break;
81456
+ }
81402
81457
  if (m.type === "comment") {
81403
81458
  ctx.addIssue({
81404
81459
  code: exports_external.ZodIssueCode.custom,
@@ -82261,34 +82316,64 @@ function describeApprovalMarker(indicator) {
82261
82316
  }
82262
82317
 
82263
82318
  // packages/workflow/src/linear-filter.ts
82319
+ function linearFilterScope(resolved) {
82320
+ const scope = { requireAllLabels: resolved.requireAllLabels };
82321
+ if (resolved.excludeLabels)
82322
+ scope.excludeLabels = resolved.excludeLabels;
82323
+ if (resolved.requireProject)
82324
+ scope.requireProject = resolved.requireProject;
82325
+ if (resolved.excludeProjects)
82326
+ scope.excludeProjects = resolved.excludeProjects;
82327
+ return scope;
82328
+ }
82264
82329
  function resolveLinearFilter(filter2) {
82265
82330
  const assigneeClauses = filter2.filter((marker) => marker.type === "assignee");
82266
82331
  if (assigneeClauses.length > 1) {
82267
82332
  throw new Error(`Invalid linear.filter: at most one "assignee" clause is allowed, found ${assigneeClauses.length}.`);
82268
82333
  }
82269
- const requireAllLabels = [];
82270
- const seenLabels = new Set;
82271
- for (const marker of filter2) {
82272
- if (marker.type !== "label")
82273
- continue;
82274
- if (seenLabels.has(marker.value))
82275
- continue;
82276
- seenLabels.add(marker.value);
82277
- requireAllLabels.push(marker.value);
82278
- }
82334
+ const requireAllLabels = collectDeduped(filter2, "label", false);
82335
+ const excludeLabels = collectDeduped(filter2, "label", true);
82336
+ const requireProjects = collectDeduped(filter2, "project", false);
82337
+ const excludeProjects = collectDeduped(filter2, "project", true);
82338
+ if (requireProjects.length > 1) {
82339
+ throw new Error(`Invalid linear.filter: at most one positive "project" clause is allowed, found ${requireProjects.length}.`);
82340
+ }
82341
+ const optional2 = {};
82342
+ if (excludeLabels.length > 0)
82343
+ optional2.excludeLabels = excludeLabels;
82344
+ if (requireProjects[0] !== undefined)
82345
+ optional2.requireProject = requireProjects[0];
82346
+ if (excludeProjects.length > 0)
82347
+ optional2.excludeProjects = excludeProjects;
82348
+ const base2 = { requireAllLabels, ...optional2 };
82279
82349
  const assigneeClause = assigneeClauses[0];
82280
82350
  if (!assigneeClause)
82281
- return { requireAllLabels };
82351
+ return base2;
82282
82352
  const value = assigneeClause.value.trim();
82283
82353
  const lower = value.toLowerCase();
82284
82354
  if (lower === "any")
82285
- return { anyAssignee: true, requireAllLabels };
82355
+ return { anyAssignee: true, ...base2 };
82286
82356
  if (lower === "" || lower === "unassigned") {
82287
- return { assignee: "unassigned", requireAllLabels };
82357
+ return { assignee: "unassigned", ...base2 };
82288
82358
  }
82289
82359
  if (lower === "me")
82290
- return { assignee: "me", requireAllLabels };
82291
- return { assignee: value, requireAllLabels };
82360
+ return { assignee: "me", ...base2 };
82361
+ return { assignee: value, ...base2 };
82362
+ }
82363
+ function collectDeduped(filter2, type, negated) {
82364
+ const out = [];
82365
+ const seen = new Set;
82366
+ for (const marker of filter2) {
82367
+ if (marker.type !== type)
82368
+ continue;
82369
+ if (Boolean(marker.negate) !== negated)
82370
+ continue;
82371
+ if (seen.has(marker.value))
82372
+ continue;
82373
+ seen.add(marker.value);
82374
+ out.push(marker.value);
82375
+ }
82376
+ return out;
82292
82377
  }
82293
82378
  function applyAssigneeOverride(filter2, assignee) {
82294
82379
  const trimmed = assignee.trim();
@@ -82315,6 +82400,7 @@ __export(exports_workflow, {
82315
82400
  migrateWorkflowMarkdown: () => migrateWorkflowMarkdown,
82316
82401
  matchesIndicator: () => matchesIndicator,
82317
82402
  loadWorkflow: () => loadWorkflow,
82403
+ linearFilterScope: () => linearFilterScope,
82318
82404
  ensureWorkflow: () => ensureWorkflow,
82319
82405
  describeApprovalMarker: () => describeApprovalMarker,
82320
82406
  computeConfirmationFlags: () => computeConfirmationFlags,
@@ -103151,6 +103237,39 @@ function applyRequiredLabels(where, requireAllLabels) {
103151
103237
  }
103152
103238
  where.and = and2;
103153
103239
  }
103240
+ function isNegatedMarker(m) {
103241
+ return "negate" in m && Boolean(m.negate);
103242
+ }
103243
+ function applyRequiredProject(where, requireProject) {
103244
+ if (!requireProject)
103245
+ return;
103246
+ const clause = { project: { name: { in: [requireProject] } } };
103247
+ const existing = where.project;
103248
+ if (existing === undefined) {
103249
+ const and3 = where.and;
103250
+ if (and3 !== undefined)
103251
+ and3.push(clause);
103252
+ else
103253
+ where.project = clause.project;
103254
+ return;
103255
+ }
103256
+ const and2 = where.and ?? [];
103257
+ and2.push({ project: existing }, clause);
103258
+ delete where.project;
103259
+ where.and = and2;
103260
+ }
103261
+ function applyGlobalExcludes(where, excludeLabels, excludeProjects) {
103262
+ if (excludeLabels && excludeLabels.length > 0) {
103263
+ const and2 = where.and ?? [];
103264
+ and2.push({ labels: { every: { name: { nin: excludeLabels } } } });
103265
+ where.and = and2;
103266
+ }
103267
+ if (excludeProjects && excludeProjects.length > 0) {
103268
+ const and2 = where.and ?? [];
103269
+ and2.push({ project: { name: { nin: excludeProjects } } });
103270
+ where.and = and2;
103271
+ }
103272
+ }
103154
103273
  function buildIssueFilter(spec) {
103155
103274
  const where = {};
103156
103275
  if (spec.team)
@@ -103169,9 +103288,13 @@ function buildIssueFilter(spec) {
103169
103288
  if (spec.numbers && spec.numbers.length > 0) {
103170
103289
  where.number = { in: spec.numbers };
103171
103290
  }
103172
- const inc = spec.include ?? [];
103291
+ const incAll = spec.include ?? [];
103292
+ const inc = incAll.filter((m) => !isNegatedMarker(m));
103293
+ const negatedInc = incAll.filter(isNegatedMarker);
103294
+ let pinnedStatus = false;
103173
103295
  if (inc.length > 0) {
103174
103296
  const { statuses, labels, attachmentSubtitles, projects } = partition2(inc);
103297
+ pinnedStatus = statuses.length > 0;
103175
103298
  const branches = [];
103176
103299
  if (statuses.length > 0)
103177
103300
  branches.push({ state: { name: { in: statuses } } });
@@ -103191,10 +103314,16 @@ function buildIssueFilter(spec) {
103191
103314
  branches.push({ project: { name: { in: projects } } });
103192
103315
  for (const b of branches)
103193
103316
  Object.assign(where, b);
103194
- } else {
103317
+ }
103318
+ if (!pinnedStatus) {
103195
103319
  where.state = { type: { in: ["unstarted", "started", "backlog"] } };
103196
103320
  }
103197
- const exc = spec.exclude ?? [];
103321
+ const exc = [
103322
+ ...spec.exclude ?? [],
103323
+ ...negatedInc,
103324
+ ...(spec.excludeLabels ?? []).map((value) => ({ type: "label", value })),
103325
+ ...(spec.excludeProjects ?? []).map((value) => ({ type: "project", value }))
103326
+ ];
103198
103327
  if (exc.length > 0) {
103199
103328
  const {
103200
103329
  statuses,
@@ -103250,12 +103379,13 @@ function buildIssueFilter(spec) {
103250
103379
  }
103251
103380
  }
103252
103381
  applyRequiredLabels(where, spec.requireAllLabels);
103382
+ applyRequiredProject(where, spec.requireProject);
103253
103383
  return where;
103254
103384
  }
103255
103385
  function clauseFromMarkers(markers) {
103256
103386
  if (markers.length === 0)
103257
103387
  return null;
103258
- const { statuses, labels, attachmentSubtitles, projects } = partition2(markers);
103388
+ const { statuses, labels, attachmentSubtitles, projects } = partition2(markers.filter((m) => !isNegatedMarker(m)));
103259
103389
  const parts = {};
103260
103390
  if (statuses.length > 0)
103261
103391
  parts.state = { name: { in: statuses } };
@@ -103271,6 +103401,16 @@ function clauseFromMarkers(markers) {
103271
103401
  }
103272
103402
  if (projects.length > 0)
103273
103403
  parts.project = { name: { in: projects } };
103404
+ const negated = partition2(markers.filter(isNegatedMarker));
103405
+ const negClauses = [];
103406
+ if (negated.statuses.length > 0)
103407
+ negClauses.push({ state: { name: { nin: negated.statuses } } });
103408
+ if (negated.labels.length > 0)
103409
+ negClauses.push({ labels: { every: { name: { nin: negated.labels } } } });
103410
+ if (negated.projects.length > 0)
103411
+ negClauses.push({ project: { name: { nin: negated.projects } } });
103412
+ if (negClauses.length > 0)
103413
+ parts.and = negClauses;
103274
103414
  return Object.keys(parts).length > 0 ? parts : null;
103275
103415
  }
103276
103416
  function mapNodeProject(node2) {
@@ -103331,6 +103471,8 @@ async function fetchMentionScanIssues(apiKey, spec) {
103331
103471
  where.number = { in: spec.numbers };
103332
103472
  }
103333
103473
  applyRequiredLabels(where, spec.requireAllLabels);
103474
+ applyRequiredProject(where, spec.requireProject);
103475
+ applyGlobalExcludes(where, spec.excludeLabels, spec.excludeProjects);
103334
103476
  const query = `query MentionScanIssues($filter: IssueFilter) {
103335
103477
  issues(filter: $filter, first: 50) {
103336
103478
  nodes {
@@ -103858,7 +104000,7 @@ function issueMatchesGetIndicator(issue2, indicator) {
103858
104000
  const labels = new Set(issue2.labels.map((l) => l.toLowerCase()));
103859
104001
  const stateName = issue2.state.name.toLowerCase();
103860
104002
  const projectName = issue2.project?.name.toLowerCase() ?? null;
103861
- return indicator.filter.some((m) => {
104003
+ const baseMatch = (m) => {
103862
104004
  if (m.type === "label")
103863
104005
  return labels.has(m.value.toLowerCase());
103864
104006
  if (m.type === "status")
@@ -103878,7 +104020,8 @@ function issueMatchesGetIndicator(issue2, indicator) {
103878
104020
  return comments.some((c) => !isRalphComment(c.body) && c.body.toLowerCase().includes(needle));
103879
104021
  }
103880
104022
  return false;
103881
- });
104023
+ };
104024
+ return indicator.filter.some((m) => isNegatedMarker(m) ? !baseMatch(m) : baseMatch(m));
103882
104025
  }
103883
104026
  async function fetchProjectIdByName(apiKey, name) {
103884
104027
  const query = `query ProjectId($name: String!) {
@@ -105869,6 +106012,15 @@ function statusLabel(row) {
105869
106012
  }
105870
106013
  }
105871
106014
  }
106015
+ function orderActiveWorkersFirst(rows, activeWorkerIds) {
106016
+ if (activeWorkerIds.size === 0)
106017
+ return rows.slice();
106018
+ const active = [];
106019
+ const rest2 = [];
106020
+ for (const r of rows)
106021
+ (activeWorkerIds.has(r.id) ? active : rest2).push(r);
106022
+ return [...active, ...rest2];
106023
+ }
105872
106024
  function buildBoardTree(rows) {
105873
106025
  const byId = new Map(rows.map((r) => [r.id, r]));
105874
106026
  const orderIndex = new Map(rows.map((r, i) => [r.id, i]));
@@ -106578,7 +106730,7 @@ class AgentCoordinator {
106578
106730
  state = machineStateToTicketState(snapshot.value);
106579
106731
  if (state === "done")
106580
106732
  return null;
106581
- if (kind === "queued" && state === "in-progress")
106733
+ if (kind === "queued" && (state === "working" || state === "in-progress"))
106582
106734
  state = "queued";
106583
106735
  const flowRecovery = snapshot.context.data.recovery;
106584
106736
  if (flowRecovery) {
@@ -108436,7 +108588,7 @@ var init_indicators = __esm(() => {
108436
108588
 
108437
108589
  // apps/agent/src/agent/wire/linear-resolvers.ts
108438
108590
  function createLinearResolvers(input) {
108439
- const { apiKey, team, assignee, anyAssignee, requireAllLabels, diag } = input;
108591
+ const { apiKey, team, assignee, anyAssignee, scope, diag } = input;
108440
108592
  const ticketNumbers = input.ticketNumbers ?? [];
108441
108593
  const stateCache = new Map;
108442
108594
  const labelCache = new Map;
@@ -108571,7 +108723,7 @@ function createLinearResolvers(input) {
108571
108723
  team,
108572
108724
  assignee,
108573
108725
  anyAssignee,
108574
- requireAllLabels,
108726
+ ...scope,
108575
108727
  include,
108576
108728
  exclude: excl,
108577
108729
  ...ticketNumbers.length > 0 ? { numbers: ticketNumbers } : {}
@@ -108594,18 +108746,18 @@ function createLinearResolvers(input) {
108594
108746
  resolveLabelIdForTeam
108595
108747
  };
108596
108748
  }
108597
- function doneCandidateSpec(team, assignee, anyAssignee, requireAllLabels, include, ticketNumbers) {
108749
+ function doneCandidateSpec(team, assignee, anyAssignee, scope, include, ticketNumbers) {
108598
108750
  return {
108599
108751
  team,
108600
108752
  assignee,
108601
108753
  anyAssignee,
108602
- requireAllLabels,
108754
+ ...scope,
108603
108755
  include,
108604
108756
  exclude: [],
108605
108757
  ...ticketNumbers && ticketNumbers.length > 0 ? { numbers: ticketNumbers } : {}
108606
108758
  };
108607
108759
  }
108608
- async function fetchDoneCandidatesWith(apiKey, team, assignee, anyAssignee, requireAllLabels, indicators, ticketNumbers) {
108760
+ async function fetchDoneCandidatesWith(apiKey, team, assignee, anyAssignee, scope, indicators, ticketNumbers) {
108609
108761
  const getIndicators = [
108610
108762
  indicators.getTodo,
108611
108763
  indicators.getInProgress,
@@ -108624,7 +108776,7 @@ async function fetchDoneCandidatesWith(apiKey, team, assignee, anyAssignee, requ
108624
108776
  const include = ind.filter ?? [];
108625
108777
  if (include.length === 0)
108626
108778
  return;
108627
- const issues = await fetchOpenIssues(apiKey, doneCandidateSpec(team, assignee, anyAssignee, requireAllLabels, include, ticketNumbers));
108779
+ const issues = await fetchOpenIssues(apiKey, doneCandidateSpec(team, assignee, anyAssignee, scope, include, ticketNumbers));
108628
108780
  for (const issue2 of issues) {
108629
108781
  if (!seen.has(issue2.id)) {
108630
108782
  seen.add(issue2.id);
@@ -108805,7 +108957,7 @@ function createLinearTrackerProvider(input) {
108805
108957
  team,
108806
108958
  assignee,
108807
108959
  anyAssignee,
108808
- requireAllLabels,
108960
+ scope,
108809
108961
  indicators,
108810
108962
  resolvers,
108811
108963
  fetchMentions,
@@ -108818,7 +108970,7 @@ function createLinearTrackerProvider(input) {
108818
108970
  fetchInProgress: () => resolvers.fetchByGet(indicators.getInProgress, excludeFromInProgress),
108819
108971
  fetchReview: () => resolvers.fetchByGet(indicators.getReview, []),
108820
108972
  fetchMentions,
108821
- fetchDoneCandidates: () => fetchDoneCandidatesWith(apiKey, team, assignee, anyAssignee, requireAllLabels, indicators, ticketNumbers && ticketNumbers.length > 0 ? ticketNumbers : undefined),
108973
+ fetchDoneCandidates: () => fetchDoneCandidatesWith(apiKey, team, assignee, anyAssignee, scope, indicators, ticketNumbers && ticketNumbers.length > 0 ? ticketNumbers : undefined),
108822
108974
  applyIndicator: resolvers.applyIndicator,
108823
108975
  removeIndicator: resolvers.removeIndicator,
108824
108976
  postComment: (issue2, body) => addIssueComment(apiKey, issue2.id, body),
@@ -109609,7 +109761,7 @@ function createMentionScanner(input) {
109609
109761
  team,
109610
109762
  assignee,
109611
109763
  anyAssignee,
109612
- requireAllLabels,
109764
+ scope,
109613
109765
  indicators,
109614
109766
  projectRoot,
109615
109767
  useWorktree,
@@ -109634,7 +109786,7 @@ function createMentionScanner(input) {
109634
109786
  team,
109635
109787
  assignee,
109636
109788
  anyAssignee,
109637
- ...requireAllLabels && requireAllLabels.length > 0 ? { requireAllLabels } : {},
109789
+ ...scope,
109638
109790
  ...ticketNumbers && ticketNumbers.length > 0 ? { numbers: ticketNumbers } : {},
109639
109791
  indicators: {
109640
109792
  ...indicators.getTodo !== undefined ? { getTodo: indicators.getTodo } : {},
@@ -263801,7 +263953,9 @@ function buildAgentCoordinator(input) {
263801
263953
  const isGithubTracker = cfg.tracker.kind === "github";
263802
263954
  const indicators = isGithubTracker ? githubIndicators(cfg.github?.issues) : mergeIndicators(cfg.linear.indicators, args.indicators);
263803
263955
  const team = args.linearTeam || cfg.linear.team;
263804
- const { assignee, anyAssignee, requireAllLabels } = resolveLinearFilter(applyAssigneeOverride(cfg.linear.filter, args.linearAssignee));
263956
+ const resolvedFilter = resolveLinearFilter(applyAssigneeOverride(cfg.linear.filter, args.linearAssignee));
263957
+ const { assignee, anyAssignee, requireAllLabels } = resolvedFilter;
263958
+ const scope = linearFilterScope(resolvedFilter);
263805
263959
  const ticketNumbers = resolveTicketNumbers(args.ticketTokens, team);
263806
263960
  const excludeFromTodo = isGithubTracker ? unionMarkers(indicators.setDone, indicators.setError, indicators.setInProgress) : unionMarkers(indicators.setDone, indicators.setError);
263807
263961
  const gitRunner = input.runners?.git ?? bunGitRunner;
@@ -263843,7 +263997,7 @@ function buildAgentCoordinator(input) {
263843
263997
  team,
263844
263998
  assignee,
263845
263999
  anyAssignee,
263846
- requireAllLabels,
264000
+ scope,
263847
264001
  diag,
263848
264002
  ...ticketNumbers.length > 0 ? { ticketNumbers } : {}
263849
264003
  });
@@ -263854,7 +264008,7 @@ function buildAgentCoordinator(input) {
263854
264008
  diag
263855
264009
  }) : {
263856
264010
  ...resolvers,
263857
- fetchDoneCandidates: () => fetchDoneCandidatesWith(apiKey, team, assignee, anyAssignee, requireAllLabels, indicators, ticketNumbers.length > 0 ? ticketNumbers : undefined)
264011
+ fetchDoneCandidates: () => fetchDoneCandidatesWith(apiKey, team, assignee, anyAssignee, scope, indicators, ticketNumbers.length > 0 ? ticketNumbers : undefined)
263858
264012
  };
263859
264013
  if (ticketNumbers.length > 0) {
263860
264014
  const hasGetIndicator = [indicators.getTodo, indicators.getInProgress].some((ind) => ind && ind.filter.length > 0);
@@ -263893,7 +264047,7 @@ function buildAgentCoordinator(input) {
263893
264047
  team,
263894
264048
  assignee,
263895
264049
  anyAssignee,
263896
- requireAllLabels,
264050
+ scope,
263897
264051
  indicators,
263898
264052
  projectRoot,
263899
264053
  useWorktree,
@@ -263921,7 +264075,7 @@ function buildAgentCoordinator(input) {
263921
264075
  team,
263922
264076
  assignee,
263923
264077
  anyAssignee,
263924
- requireAllLabels,
264078
+ scope,
263925
264079
  indicators,
263926
264080
  resolvers,
263927
264081
  fetchMentions,
@@ -265071,7 +265225,8 @@ function AgentMode({
265071
265225
  const termWidth = columns - 2;
265072
265226
  const termHeight = rows;
265073
265227
  const board = pollStatus.lastBoard;
265074
- const tree = buildBoardTree(board);
265228
+ const liveWorkerIds = new Set(coordRef.current?.activeWorkers.map((w2) => w2.issueId) ?? []);
265229
+ const tree = buildBoardTree(orderActiveWorkersFirst(board, liveWorkerIds));
265075
265230
  const focusedIndex = (() => {
265076
265231
  if (tree.length === 0)
265077
265232
  return -1;
@@ -266283,14 +266438,14 @@ function buildBuckets(indicators) {
266283
266438
  { label: "auto-merge", indicator: indicators.getAutoMerge, exclude: [] }
266284
266439
  ];
266285
266440
  }
266286
- async function fetchBucketIssues(apiKey, bucket, team, assignee, anyAssignee, requireAllLabels, ticketNumbers) {
266441
+ async function fetchBucketIssues(apiKey, bucket, team, assignee, anyAssignee, scope, ticketNumbers) {
266287
266442
  if (!bucket.indicator || bucket.indicator.filter.length === 0)
266288
266443
  return [];
266289
266444
  const spec = {
266290
266445
  team,
266291
266446
  assignee,
266292
266447
  anyAssignee,
266293
- requireAllLabels,
266448
+ ...scope,
266294
266449
  include: bucket.indicator.filter,
266295
266450
  exclude: bucket.exclude,
266296
266451
  ...ticketNumbers.length > 0 ? { numbers: ticketNumbers } : {}
@@ -266343,13 +266498,13 @@ function backlogRankByIssueId(issues) {
266343
266498
  ordered.forEach((o, i) => rankById.set(o.id, i));
266344
266499
  return rankById;
266345
266500
  }
266346
- async function fetchAndPrintLinear(apiKey, buckets, team, assignee, anyAssignee, requireAllLabels, cwd2, runner, ignoreCiChecks = [], checks3 = false, review = false, ticketNumbers = []) {
266501
+ async function fetchAndPrintLinear(apiKey, buckets, team, assignee, anyAssignee, scope, cwd2, runner, ignoreCiChecks = [], checks3 = false, review = false, ticketNumbers = []) {
266347
266502
  const bucketResults = await Promise.all(buckets.map(async (bucket) => {
266348
266503
  if (!bucket.indicator || bucket.indicator.filter.length === 0) {
266349
266504
  return { bucket, issues: [], error: null };
266350
266505
  }
266351
266506
  try {
266352
- const issues = await fetchBucketIssues(apiKey, bucket, team, assignee, anyAssignee, requireAllLabels, ticketNumbers);
266507
+ const issues = await fetchBucketIssues(apiKey, bucket, team, assignee, anyAssignee, scope, ticketNumbers);
266353
266508
  return { bucket, issues, error: null };
266354
266509
  } catch (err) {
266355
266510
  return {
@@ -266485,7 +266640,9 @@ async function runList(input) {
266485
266640
  const apiKey = process.env["LINEAR_API_KEY"];
266486
266641
  const indicators = cfg.linear.indicators;
266487
266642
  const team = input.linearTeamOverride || cfg.linear.team;
266488
- const { assignee, anyAssignee, requireAllLabels } = resolveLinearFilter(applyAssigneeOverride(cfg.linear.filter, input.linearAssigneeOverride));
266643
+ const resolved = resolveLinearFilter(applyAssigneeOverride(cfg.linear.filter, input.linearAssigneeOverride));
266644
+ const { assignee, anyAssignee } = resolved;
266645
+ const scope = linearFilterScope(resolved);
266489
266646
  const buckets = buildBuckets(indicators);
266490
266647
  const anyConfigured = buckets.some((b2) => b2.indicator && b2.indicator.filter.length > 0);
266491
266648
  if (!anyConfigured) {
@@ -266525,7 +266682,7 @@ team: ${team}
266525
266682
  if (ticketNumbers.length > 0)
266526
266683
  process.stdout.write(`ticket: ${ticketNumbers.join(", ")}
266527
266684
  `);
266528
- await fetchAndPrintLinear(apiKey, buckets, team, assignee, anyAssignee, requireAllLabels, projectRoot, localCmdRunner, cfg.prRecovery.ignoreChecks, input.checks, input.review, ticketNumbers);
266685
+ await fetchAndPrintLinear(apiKey, buckets, team, assignee, anyAssignee, scope, projectRoot, localCmdRunner, cfg.prRecovery.ignoreChecks, input.checks, input.review, ticketNumbers);
266529
266686
  }
266530
266687
  function normalizeIdentifier(input) {
266531
266688
  let parsed;
@@ -266608,7 +266765,7 @@ async function runListDebug(input) {
266608
266765
  const cfg = await loadRalphyConfig(projectRoot, getArgs().workflowFile);
266609
266766
  const indicators = cfg.linear.indicators;
266610
266767
  const team = input.linearTeamOverride || cfg.linear.team;
266611
- const { assignee, anyAssignee, requireAllLabels } = resolveLinearFilter(applyAssigneeOverride(cfg.linear.filter, input.linearAssigneeOverride));
266768
+ const { assignee, anyAssignee, requireAllLabels, excludeLabels } = resolveLinearFilter(applyAssigneeOverride(cfg.linear.filter, input.linearAssigneeOverride));
266612
266769
  const assigneeLabel = anyAssignee ? "any" : assignee ?? "*";
266613
266770
  const normalized = normalizeIdentifier(identifier);
266614
266771
  if (!normalized) {
@@ -266659,6 +266816,13 @@ Per-bucket diagnostics:
266659
266816
  reasons.push(`missing required linear.filter label(s): ${missing.join(", ")}`);
266660
266817
  }
266661
266818
  }
266819
+ if (excludeLabels && excludeLabels.length > 0) {
266820
+ const issueLabels = new Set(issue2.labels.nodes.map((l3) => l3.name));
266821
+ const present = excludeLabels.filter((label) => issueLabels.has(label));
266822
+ if (present.length > 0) {
266823
+ reasons.push(`carries excluded linear.filter label(s): ${present.join(", ")}`);
266824
+ }
266825
+ }
266662
266826
  const includeMatches = bucket.indicator.filter.some((m2) => markerMatches(issue2, m2));
266663
266827
  if (!includeMatches) {
266664
266828
  const want = bucket.indicator.filter.map((m2) => `${m2.type}:${m2.value}`).join(" OR ");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neriros/ralphy",
3
- "version": "3.10.18",
3
+ "version": "3.10.19",
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",