@neriros/ralphy 3.10.21 → 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 +226 -32
  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.21")
18932
- return "3.10.21";
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
  }
@@ -103058,6 +103086,13 @@ function parseRalphyMarker(body) {
103058
103086
  return null;
103059
103087
  return { version: version3, type, fields };
103060
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
+ }
103061
103096
  function buildRalphyComment(input) {
103062
103097
  const lines = [`${RALPHY_TITLE_PREFIX}${input.action}`];
103063
103098
  const body = input.body?.trim();
@@ -108153,8 +108188,8 @@ async function releaseAwaitingMarker(issue2, statePath, deps) {
108153
108188
  return true;
108154
108189
  }
108155
108190
  function confirmationUsesCommentIndicator(cfg) {
108156
- const { getApproved, getAutoApprove, getConfirmGate } = cfg.linear.indicators;
108157
- 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"));
108158
108193
  }
108159
108194
  async function processAwaitingForIssue(issue2, deps) {
108160
108195
  try {
@@ -108795,6 +108830,46 @@ var init_linear_resolvers = __esm(() => {
108795
108830
  init_identifier_strategy();
108796
108831
  });
108797
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
+
108798
108873
  // apps/agent/src/agent/wire/tracker/github.ts
108799
108874
  function identifierForNumber(n) {
108800
108875
  return `issue-${n}`;
@@ -108890,6 +108965,24 @@ function createGithubTrackerProvider(input) {
108890
108965
  diag("github-marker", ` \u2192 ${issue2.identifier} comment`, "gray");
108891
108966
  return;
108892
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
+ }
108893
108986
  if (m.type !== "label") {
108894
108987
  diag("github-marker", `! ${issue2.identifier}: '${m.type}' markers are not supported by the GitHub tracker \u2014 skipped`, "yellow");
108895
108988
  return;
@@ -108917,6 +109010,18 @@ function createGithubTrackerProvider(input) {
108917
109010
  diag("github-marker", ` \u2192 ${issue2.identifier} -label='${m.value}'`, "gray");
108918
109011
  }
108919
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
+ }
108920
109025
  async function fetchDoneCandidates() {
108921
109026
  return listIssues(["--label", statusLabels.inProgress]);
108922
109027
  }
@@ -108929,7 +109034,9 @@ function createGithubTrackerProvider(input) {
108929
109034
  removeIndicator,
108930
109035
  applyMarker,
108931
109036
  fetchDoneCandidates,
108932
- resolveLabelIdForTeam
109037
+ resolveLabelIdForTeam,
109038
+ listOpenIssues,
109039
+ repo
108933
109040
  };
108934
109041
  }
108935
109042
  function githubIndicators(issues) {
@@ -108945,7 +109052,9 @@ function githubIndicators(issues) {
108945
109052
  }
108946
109053
  var DEFAULT_STATUS_LABELS;
108947
109054
  var init_github = __esm(() => {
109055
+ init_src8();
108948
109056
  init_types2();
109057
+ init_sticky_comment();
108949
109058
  DEFAULT_STATUS_LABELS = {
108950
109059
  inProgress: "ralph:in-progress",
108951
109060
  done: "ralph:done",
@@ -109696,44 +109805,49 @@ async function addGithubReactionToComment(cmdRunner, projectRoot, source, commen
109696
109805
  const path = source.kind === "issue" ? `repos/${source.owner}/${source.repo}/issues/comments/${commentId}/reactions` : `repos/${source.owner}/${source.repo}/pulls/comments/${commentId}/reactions`;
109697
109806
  await cmdRunner.run(["gh", "api", "-X", "POST", path, "-f", `content=${content}`], projectRoot);
109698
109807
  }
109699
- async function postGithubPrComment(cmdRunner, projectRoot, prUrl, body, onLog) {
109700
- const m = /^https:\/\/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/.exec(prUrl);
109701
- if (!m)
109702
- return;
109703
- 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) {
109704
109824
  try {
109705
109825
  await cmdRunner.run([
109706
109826
  "gh",
109707
109827
  "api",
109708
109828
  "-X",
109709
109829
  "POST",
109710
- `repos/${owner}/${repo}/issues/${num}/comments`,
109830
+ `repos/${repo}/issues/${issueNumber}/comments`,
109711
109831
  "-f",
109712
109832
  `body=${body}`
109713
109833
  ], projectRoot);
109714
109834
  } catch (err) {
109715
- 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");
109716
109836
  }
109717
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
+ }
109718
109845
  async function fetchPrIssueComments(cmdRunner, projectRoot, prUrl, onLog) {
109719
109846
  const m = /^https:\/\/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/.exec(prUrl);
109720
109847
  if (!m)
109721
109848
  return [];
109722
109849
  const [, owner, repo, num] = m;
109723
- try {
109724
- const res = await cmdRunner.run([
109725
- "gh",
109726
- "api",
109727
- `repos/${owner}/${repo}/issues/${num}/comments`,
109728
- "--jq",
109729
- "[.[] | {id: .id, body: .body, createdAt: .created_at, author: .user.login, url: .html_url}]"
109730
- ], projectRoot);
109731
- const parsed = JSON.parse(res.stdout || "[]");
109732
- return parsed;
109733
- } catch (err) {
109734
- onLog(`! mention scan: gh comments failed for ${prUrl}: ${formatLinearError(err)}`, "yellow");
109735
- return [];
109736
- }
109850
+ return fetchGithubIssueComments(cmdRunner, projectRoot, `${owner}/${repo}`, Number(num), onLog);
109737
109851
  }
109738
109852
  var init_github2 = __esm(() => {
109739
109853
  init_linear();
@@ -109928,6 +110042,77 @@ function createMentionScanner(input) {
109928
110042
  return out;
109929
110043
  };
109930
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
+ }
109931
110116
  async function isChangeArchivedForIssue(issue2, cwdByChange, projectRoot) {
109932
110117
  const changeName = changeNameForIssue(issue2);
109933
110118
  const root = cwdByChange.get(changeName) ?? projectRoot;
@@ -264004,12 +264189,13 @@ function buildAgentCoordinator(input) {
264004
264189
  diag,
264005
264190
  ...ticketNumbers.length > 0 ? { ticketNumbers } : {}
264006
264191
  });
264007
- const provider = isGithubTracker ? createGithubTrackerProvider({
264192
+ const githubProvider = isGithubTracker ? createGithubTrackerProvider({
264008
264193
  issues: cfg.github?.issues,
264009
264194
  cmdRunner,
264010
264195
  projectRoot,
264011
264196
  diag
264012
- }) : {
264197
+ }) : null;
264198
+ const provider = githubProvider ? githubProvider : {
264013
264199
  ...resolvers,
264014
264200
  fetchDoneCandidates: () => fetchDoneCandidatesWith(apiKey, team, assignee, anyAssignee, scope, indicators, ticketNumbers.length > 0 ? ticketNumbers : undefined)
264015
264201
  };
@@ -264043,7 +264229,15 @@ function buildAgentCoordinator(input) {
264043
264229
  scriptRunner,
264044
264230
  ...input.runners?.worktree ? { worktreeProvider: input.runners.worktree } : {}
264045
264231
  });
264046
- 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({
264047
264241
  apiKey,
264048
264242
  args,
264049
264243
  cfg,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neriros/ralphy",
3
- "version": "3.10.21",
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",