@neriros/ralphy 3.10.20 → 3.10.22

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 +233 -37
  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.20")
18932
- return "3.10.20";
18931
+ if ("3.10.22")
18932
+ return "3.10.22";
18933
18933
  } catch {}
18934
18934
  const dirsToTry = [];
18935
18935
  try {
@@ -81405,7 +81405,6 @@ var init_schema = __esm(() => {
81405
81405
  getAutoMerge: GetIndicatorSchema.optional(),
81406
81406
  getApproved: GetIndicatorSchema.optional(),
81407
81407
  getConfirmGate: GetIndicatorSchema.optional(),
81408
- getAutoApprove: GetIndicatorSchema.optional(),
81409
81408
  setInProgress: SetIndicatorSchema.optional(),
81410
81409
  setDone: SetIndicatorSchema.optional(),
81411
81410
  setPrReady: SetIndicatorSchema.optional(),
@@ -82117,6 +82116,24 @@ function stampDescription(document2, path) {
82117
82116
  return;
82118
82117
  pair.key.commentBefore = toCommentLines(match.description);
82119
82118
  }
82119
+ function toMarkerArray(node2) {
82120
+ if (node2 == null)
82121
+ return [];
82122
+ const js = import_yaml2.default.isNode(node2) ? node2.toJSON() : node2;
82123
+ return Array.isArray(js) ? js : [];
82124
+ }
82125
+ function dedupeMarkers(markers) {
82126
+ const seen = new Set;
82127
+ const out = [];
82128
+ for (const marker of markers) {
82129
+ const key = JSON.stringify(marker);
82130
+ if (seen.has(key))
82131
+ continue;
82132
+ seen.add(key);
82133
+ out.push(marker);
82134
+ }
82135
+ return out;
82136
+ }
82120
82137
  function normalizeWorkflowMarkdown(markdown) {
82121
82138
  const match = FRONTMATTER_RE.exec(markdown);
82122
82139
  if (!match)
@@ -82133,6 +82150,17 @@ function normalizeWorkflowMarkdown(markdown) {
82133
82150
  stampDescription(document2, path);
82134
82151
  added.push(path.join("."));
82135
82152
  }
82153
+ const autoApprovePath = ["linear", "indicators", "getAutoApprove"];
82154
+ if (document2.getIn(autoApprovePath) !== undefined) {
82155
+ const merged = dedupeMarkers([
82156
+ ...toMarkerArray(document2.getIn(["linear", "indicators", "getApproved", "filter"])),
82157
+ ...toMarkerArray(document2.getIn([...autoApprovePath, "filter"]))
82158
+ ]);
82159
+ document2.setIn(["linear", "indicators", "getApproved"], { filter: merged });
82160
+ stampDescription(document2, ["linear", "indicators", "getApproved"]);
82161
+ document2.deleteIn(autoApprovePath);
82162
+ added.push("linear.indicators.getApproved");
82163
+ }
82136
82164
  const gateEnabled = document2.getIn(["linear", "confirmationMode", "enabled"]) === true;
82137
82165
  const hasGetApproved = document2.getIn(["linear", "indicators", "getApproved"]) !== undefined;
82138
82166
  if (gateEnabled && !hasGetApproved) {
@@ -82284,9 +82312,9 @@ function matchesAnyValue(type, values2, ticket) {
82284
82312
  }
82285
82313
  function computeConfirmationFlags(config2, ticket) {
82286
82314
  const cm = config2.linear.confirmationMode;
82287
- const { getConfirmGate, getAutoApprove, getApproved } = config2.linear.indicators;
82315
+ const { getConfirmGate, getApproved } = config2.linear.indicators;
82288
82316
  const optInSatisfied = !getConfirmGate || matchesIndicator(getConfirmGate, ticket);
82289
- const confirmationGated = cm.enabled && optInSatisfied && !matchesIndicator(getAutoApprove, ticket);
82317
+ const confirmationGated = cm.enabled && optInSatisfied;
82290
82318
  const approved = matchesIndicator(getApproved, ticket);
82291
82319
  return { confirmationGated, approved };
82292
82320
  }
@@ -102996,11 +103024,13 @@ var init_config = __esm(() => {
102996
103024
  // apps/agent/src/shared/capabilities/poll-context.ts
102997
103025
  class PollContext {
102998
103026
  memo = new Map;
102999
- fetchPrOnce(url2, fields, runner, cwd2) {
103027
+ fetchPrOnce(url2, fields, runner, cwd2, opts) {
103000
103028
  const key = `${url2}|${[...fields].sort().join(",")}`;
103001
- const existing = this.memo.get(key);
103002
- if (existing)
103003
- return existing;
103029
+ if (!opts?.forceRefresh) {
103030
+ const existing = this.memo.get(key);
103031
+ if (existing)
103032
+ return existing;
103033
+ }
103004
103034
  const pending = this.runGhView(url2, fields, runner, cwd2);
103005
103035
  this.memo.set(key, pending);
103006
103036
  pending.catch(() => {
@@ -103056,6 +103086,13 @@ function parseRalphyMarker(body) {
103056
103086
  return null;
103057
103087
  return { version: version3, type, fields };
103058
103088
  }
103089
+ function findStickyComment(comments, type) {
103090
+ for (const comment of comments) {
103091
+ if (parseRalphyMarker(comment.body)?.type === type)
103092
+ return comment;
103093
+ }
103094
+ return null;
103095
+ }
103059
103096
  function buildRalphyComment(input) {
103060
103097
  const lines = [`${RALPHY_TITLE_PREFIX}${input.action}`];
103061
103098
  const body = input.body?.trim();
@@ -108151,8 +108188,8 @@ async function releaseAwaitingMarker(issue2, statePath, deps) {
108151
108188
  return true;
108152
108189
  }
108153
108190
  function confirmationUsesCommentIndicator(cfg) {
108154
- const { getApproved, getAutoApprove, getConfirmGate } = cfg.linear.indicators;
108155
- return [getApproved, getAutoApprove, getConfirmGate].some((g) => g?.filter.some((m) => m.type === "comment"));
108191
+ const { getApproved, getConfirmGate } = cfg.linear.indicators;
108192
+ return [getApproved, getConfirmGate].some((g) => g?.filter.some((m) => m.type === "comment"));
108156
108193
  }
108157
108194
  async function processAwaitingForIssue(issue2, deps) {
108158
108195
  try {
@@ -108793,6 +108830,46 @@ var init_linear_resolvers = __esm(() => {
108793
108830
  init_identifier_strategy();
108794
108831
  });
108795
108832
 
108833
+ // apps/agent/src/agent/wire/tracker/sticky-comment.ts
108834
+ async function upsertStickyComment(deps) {
108835
+ const { cmdRunner, repo, projectRoot, issueNumber, type, body, diag } = deps;
108836
+ let comments;
108837
+ try {
108838
+ const { stdout } = await cmdRunner.run(["gh", "issue", "view", issueNumber, "--repo", repo, "--json", "comments"], projectRoot);
108839
+ const parsed = JSON.parse(stdout.trim() || "{}");
108840
+ comments = parsed.comments ?? [];
108841
+ } catch (err) {
108842
+ diag("sticky-comment", `! could not list comments for issue #${issueNumber}: ${err.message}`, "yellow");
108843
+ return;
108844
+ }
108845
+ const existing = findStickyComment(comments, type);
108846
+ try {
108847
+ if (existing?.id) {
108848
+ await cmdRunner.run([
108849
+ "gh",
108850
+ "api",
108851
+ "graphql",
108852
+ "-f",
108853
+ `query=${UPDATE_COMMENT_MUTATION}`,
108854
+ "-f",
108855
+ `id=${existing.id}`,
108856
+ "-F",
108857
+ `body=${body}`
108858
+ ], projectRoot);
108859
+ diag("sticky-comment", ` \u2192 #${issueNumber} ${type} comment edited in place`, "gray");
108860
+ return;
108861
+ }
108862
+ await cmdRunner.run(["gh", "issue", "comment", issueNumber, "--repo", repo, "--body", body], projectRoot);
108863
+ diag("sticky-comment", ` \u2192 #${issueNumber} ${type} comment created`, "gray");
108864
+ } catch (err) {
108865
+ diag("sticky-comment", `! could not upsert ${type} comment on issue #${issueNumber}: ${err.message}`, "yellow");
108866
+ }
108867
+ }
108868
+ var UPDATE_COMMENT_MUTATION = "mutation($id:ID!,$body:String!){updateIssueComment(input:{id:$id,body:$body}){clientMutationId}}";
108869
+ var init_sticky_comment = __esm(() => {
108870
+ init_src8();
108871
+ });
108872
+
108796
108873
  // apps/agent/src/agent/wire/tracker/github.ts
108797
108874
  function identifierForNumber(n) {
108798
108875
  return `issue-${n}`;
@@ -108888,6 +108965,24 @@ function createGithubTrackerProvider(input) {
108888
108965
  diag("github-marker", ` \u2192 ${issue2.identifier} comment`, "gray");
108889
108966
  return;
108890
108967
  }
108968
+ if (m.type === "attachment") {
108969
+ const body = buildRalphyComment({
108970
+ type: "attachment",
108971
+ action: m.value,
108972
+ fields: { issue: issue2.identifier }
108973
+ });
108974
+ await upsertStickyComment({
108975
+ cmdRunner,
108976
+ repo: await repo(),
108977
+ projectRoot,
108978
+ issueNumber: issue2.id,
108979
+ type: "attachment",
108980
+ body,
108981
+ diag
108982
+ });
108983
+ diag("github-marker", ` \u2192 ${issue2.identifier} attachment (sticky upsert)`, "gray");
108984
+ return;
108985
+ }
108891
108986
  if (m.type !== "label") {
108892
108987
  diag("github-marker", `! ${issue2.identifier}: '${m.type}' markers are not supported by the GitHub tracker \u2014 skipped`, "yellow");
108893
108988
  return;
@@ -108915,6 +109010,18 @@ function createGithubTrackerProvider(input) {
108915
109010
  diag("github-marker", ` \u2192 ${issue2.identifier} -label='${m.value}'`, "gray");
108916
109011
  }
108917
109012
  }
109013
+ async function listOpenIssues() {
109014
+ if (!todoLabel || todoLabel.trim() === "")
109015
+ return listIssues([]);
109016
+ const [todo, inProgress] = await Promise.all([
109017
+ listIssues(["--label", todoLabel.trim()]),
109018
+ listIssues(["--label", statusLabels.inProgress])
109019
+ ]);
109020
+ const byId = new Map;
109021
+ for (const i of [...todo, ...inProgress])
109022
+ byId.set(i.id, i);
109023
+ return [...byId.values()];
109024
+ }
108918
109025
  async function fetchDoneCandidates() {
108919
109026
  return listIssues(["--label", statusLabels.inProgress]);
108920
109027
  }
@@ -108927,7 +109034,9 @@ function createGithubTrackerProvider(input) {
108927
109034
  removeIndicator,
108928
109035
  applyMarker,
108929
109036
  fetchDoneCandidates,
108930
- resolveLabelIdForTeam
109037
+ resolveLabelIdForTeam,
109038
+ listOpenIssues,
109039
+ repo
108931
109040
  };
108932
109041
  }
108933
109042
  function githubIndicators(issues) {
@@ -108943,7 +109052,9 @@ function githubIndicators(issues) {
108943
109052
  }
108944
109053
  var DEFAULT_STATUS_LABELS;
108945
109054
  var init_github = __esm(() => {
109055
+ init_src8();
108946
109056
  init_types2();
109057
+ init_sticky_comment();
108947
109058
  DEFAULT_STATUS_LABELS = {
108948
109059
  inProgress: "ralph:in-progress",
108949
109060
  done: "ralph:done",
@@ -109431,7 +109542,7 @@ function createPrDiscovery(input) {
109431
109542
  prByChange.set(changeName, prUrl);
109432
109543
  }
109433
109544
  const outcome = await waitForMergeability({
109434
- probe: async () => await getPollContext().fetchPrOnce(prUrl, ["state", "mergeable", "mergeStateStatus"], cmdRunner, projectRoot),
109545
+ probe: async (attempt2) => await getPollContext().fetchPrOnce(prUrl, ["state", "mergeable", "mergeStateStatus"], cmdRunner, projectRoot, { forceRefresh: attempt2 > 0 }),
109435
109546
  onError: (err, attempt2, total) => diag("pr", `! gh pr view ${prUrl} failed (attempt ${attempt2 + 1}/${total}): ${err.message} \u2014 will retry`, "yellow")
109436
109547
  });
109437
109548
  if (outcome.kind === "closed") {
@@ -109694,44 +109805,49 @@ async function addGithubReactionToComment(cmdRunner, projectRoot, source, commen
109694
109805
  const path = source.kind === "issue" ? `repos/${source.owner}/${source.repo}/issues/comments/${commentId}/reactions` : `repos/${source.owner}/${source.repo}/pulls/comments/${commentId}/reactions`;
109695
109806
  await cmdRunner.run(["gh", "api", "-X", "POST", path, "-f", `content=${content}`], projectRoot);
109696
109807
  }
109697
- async function postGithubPrComment(cmdRunner, projectRoot, prUrl, body, onLog) {
109698
- const m = /^https:\/\/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/.exec(prUrl);
109699
- if (!m)
109700
- return;
109701
- const [, owner, repo, num] = m;
109808
+ async function fetchGithubIssueComments(cmdRunner, projectRoot, repo, issueNumber, onLog) {
109809
+ try {
109810
+ const res = await cmdRunner.run([
109811
+ "gh",
109812
+ "api",
109813
+ `repos/${repo}/issues/${issueNumber}/comments`,
109814
+ "--jq",
109815
+ "[.[] | {id: .id, body: .body, createdAt: .created_at, author: .user.login, url: .html_url}]"
109816
+ ], projectRoot);
109817
+ return JSON.parse(res.stdout || "[]");
109818
+ } catch (err) {
109819
+ onLog(`! mention scan: gh comments failed for ${repo}#${issueNumber}: ${formatLinearError(err)}`, "yellow");
109820
+ return [];
109821
+ }
109822
+ }
109823
+ async function postGithubIssueComment(cmdRunner, projectRoot, repo, issueNumber, body, onLog) {
109702
109824
  try {
109703
109825
  await cmdRunner.run([
109704
109826
  "gh",
109705
109827
  "api",
109706
109828
  "-X",
109707
109829
  "POST",
109708
- `repos/${owner}/${repo}/issues/${num}/comments`,
109830
+ `repos/${repo}/issues/${issueNumber}/comments`,
109709
109831
  "-f",
109710
109832
  `body=${body}`
109711
109833
  ], projectRoot);
109712
109834
  } catch (err) {
109713
- onLog(`! mention scan: gh ack comment failed for ${prUrl}: ${formatLinearError(err)}`, "yellow");
109835
+ onLog(`! mention scan: gh ack comment failed for ${repo}#${issueNumber}: ${formatLinearError(err)}`, "yellow");
109714
109836
  }
109715
109837
  }
109838
+ async function postGithubPrComment(cmdRunner, projectRoot, prUrl, body, onLog) {
109839
+ const m = /^https:\/\/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/.exec(prUrl);
109840
+ if (!m)
109841
+ return;
109842
+ const [, owner, repo, num] = m;
109843
+ await postGithubIssueComment(cmdRunner, projectRoot, `${owner}/${repo}`, Number(num), body, onLog);
109844
+ }
109716
109845
  async function fetchPrIssueComments(cmdRunner, projectRoot, prUrl, onLog) {
109717
109846
  const m = /^https:\/\/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/.exec(prUrl);
109718
109847
  if (!m)
109719
109848
  return [];
109720
109849
  const [, owner, repo, num] = m;
109721
- try {
109722
- const res = await cmdRunner.run([
109723
- "gh",
109724
- "api",
109725
- `repos/${owner}/${repo}/issues/${num}/comments`,
109726
- "--jq",
109727
- "[.[] | {id: .id, body: .body, createdAt: .created_at, author: .user.login, url: .html_url}]"
109728
- ], projectRoot);
109729
- const parsed = JSON.parse(res.stdout || "[]");
109730
- return parsed;
109731
- } catch (err) {
109732
- onLog(`! mention scan: gh comments failed for ${prUrl}: ${formatLinearError(err)}`, "yellow");
109733
- return [];
109734
- }
109850
+ return fetchGithubIssueComments(cmdRunner, projectRoot, `${owner}/${repo}`, Number(num), onLog);
109735
109851
  }
109736
109852
  var init_github2 = __esm(() => {
109737
109853
  init_linear();
@@ -109926,6 +110042,77 @@ function createMentionScanner(input) {
109926
110042
  return out;
109927
110043
  };
109928
110044
  }
110045
+ function createGithubMentionScanner(input) {
110046
+ const { cfg, cmdRunner, projectRoot, onLog, diag, listOpenIssues, repo } = input;
110047
+ return async function fetchMentions() {
110048
+ if (!cfg.linear.mentionTrigger)
110049
+ return [];
110050
+ const handle = cfg.linear.mentionHandle;
110051
+ let repoSlug;
110052
+ try {
110053
+ repoSlug = await repo();
110054
+ } catch (err) {
110055
+ diag("mention", `! github mention scan: ${err.message}`, "yellow");
110056
+ return [];
110057
+ }
110058
+ const [owner, name] = repoSlug.split("/");
110059
+ if (!owner || !name) {
110060
+ diag("mention", `! github mention scan: unexpected repo slug '${repoSlug}'`, "yellow");
110061
+ return [];
110062
+ }
110063
+ let candidates;
110064
+ try {
110065
+ candidates = await listOpenIssues();
110066
+ } catch (err) {
110067
+ diag("mention", `! github mention scan: list issues failed: ${formatLinearError(err)}`, "yellow");
110068
+ return [];
110069
+ }
110070
+ const out = [];
110071
+ for (const issue2 of candidates) {
110072
+ const issueNumber = Number(issue2.id);
110073
+ if (!Number.isFinite(issueNumber))
110074
+ continue;
110075
+ const comments = await fetchGithubIssueComments(cmdRunner, projectRoot, repoSlug, issueNumber, onLog);
110076
+ issue2.comments = comments.map((c) => ({
110077
+ id: String(c.id),
110078
+ body: c.body,
110079
+ createdAt: c.createdAt,
110080
+ user: c.author ? { name: c.author, email: null } : null
110081
+ }));
110082
+ const gate2 = latestIso(findLastRalphPickupISO(comments), findLastMentionAckISO(comments));
110083
+ for (const c of comments) {
110084
+ if (isRalphComment(c.body))
110085
+ continue;
110086
+ if (!containsHandle(c.body, handle))
110087
+ continue;
110088
+ if (gate2 && c.createdAt <= gate2)
110089
+ continue;
110090
+ out.push({
110091
+ issue: issue2,
110092
+ trigger: {
110093
+ source: "github",
110094
+ body: c.body,
110095
+ createdAt: c.createdAt,
110096
+ ...c.author ? { author: c.author } : {},
110097
+ url: c.url
110098
+ }
110099
+ });
110100
+ try {
110101
+ await addGithubReactionToComment(cmdRunner, projectRoot, { owner, repo: name, kind: "issue" }, c.id, "\uD83D\uDC40");
110102
+ } catch (err) {
110103
+ if (!isAlreadyReactedError(err)) {
110104
+ diag("mention", `! github mention scan: reaction failed for ${repoSlug}#${issueNumber}: ${formatLinearError(err)}`, "yellow");
110105
+ }
110106
+ }
110107
+ if (cfg.linear.postComments !== false) {
110108
+ await postGithubIssueComment(cmdRunner, projectRoot, repoSlug, issueNumber, buildMentionAckComment(), onLog);
110109
+ }
110110
+ break;
110111
+ }
110112
+ }
110113
+ return out;
110114
+ };
110115
+ }
109929
110116
  async function isChangeArchivedForIssue(issue2, cwdByChange, projectRoot) {
109930
110117
  const changeName = changeNameForIssue(issue2);
109931
110118
  const root = cwdByChange.get(changeName) ?? projectRoot;
@@ -264002,12 +264189,13 @@ function buildAgentCoordinator(input) {
264002
264189
  diag,
264003
264190
  ...ticketNumbers.length > 0 ? { ticketNumbers } : {}
264004
264191
  });
264005
- const provider = isGithubTracker ? createGithubTrackerProvider({
264192
+ const githubProvider = isGithubTracker ? createGithubTrackerProvider({
264006
264193
  issues: cfg.github?.issues,
264007
264194
  cmdRunner,
264008
264195
  projectRoot,
264009
264196
  diag
264010
- }) : {
264197
+ }) : null;
264198
+ const provider = githubProvider ? githubProvider : {
264011
264199
  ...resolvers,
264012
264200
  fetchDoneCandidates: () => fetchDoneCandidatesWith(apiKey, team, assignee, anyAssignee, scope, indicators, ticketNumbers.length > 0 ? ticketNumbers : undefined)
264013
264201
  };
@@ -264041,7 +264229,15 @@ function buildAgentCoordinator(input) {
264041
264229
  scriptRunner,
264042
264230
  ...input.runners?.worktree ? { worktreeProvider: input.runners.worktree } : {}
264043
264231
  });
264044
- const fetchMentions = isGithubTracker ? async () => [] : createMentionScanner({
264232
+ const fetchMentions = githubProvider ? createGithubMentionScanner({
264233
+ cfg,
264234
+ cmdRunner,
264235
+ projectRoot,
264236
+ onLog,
264237
+ diag,
264238
+ listOpenIssues: githubProvider.listOpenIssues,
264239
+ repo: githubProvider.repo
264240
+ }) : createMentionScanner({
264045
264241
  apiKey,
264046
264242
  args,
264047
264243
  cfg,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neriros/ralphy",
3
- "version": "3.10.20",
3
+ "version": "3.10.22",
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",