@neriros/ralphy 2.21.3 → 2.22.0

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.
package/README.md CHANGED
@@ -156,7 +156,15 @@ Linear is the source of truth for which issues Ralph has touched. The `linear.in
156
156
  | `clearConflicted` | `Marker` or `{apply: Marker[]}` | Label(s) removed when a conflict-fix succeeds (status removal is not supported) |
157
157
  | `clearReview` | `Marker` or `{apply: Marker[]}` | Label(s) removed when a review pickup happens (status removal is not supported) |
158
158
 
159
- A `Marker` is `{type: "label", value: "ralph:foo"}` or `{type: "status", value: "In Progress"}`. Combine with `apply` when one event sets multiple — e.g. `setDone` flipping a status _and_ adding a label.
159
+ A `Marker` is one of three types:
160
+
161
+ | Marker type | Example value | Effect |
162
+ | -------------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
163
+ | `"label"` | `"ralph:in-progress"` | Adds or removes a Linear label on the issue |
164
+ | `"status"` | `"In Progress"` | Updates the Linear workflow status of the issue |
165
+ | `"attachment"` | `"In Progress"` | Upserts a single **Ralphy** attachment on the issue; `value` becomes the subtitle. The same entry is reused across every lifecycle transition — Ralph creates it on first apply and edits it on subsequent ones, so the issue stays tidy. |
166
+
167
+ Combine with `apply` when one event sets multiple — e.g. `setDone` flipping a status _and_ adding a label _and_ updating the attachment subtitle.
160
168
 
161
169
  Example `ralphy.config.json`:
162
170
 
package/dist/cli/index.js CHANGED
@@ -35029,8 +35029,8 @@ import { readFileSync as readFileSync2 } from "fs";
35029
35029
  import { resolve } from "path";
35030
35030
  function getVersion() {
35031
35031
  try {
35032
- if ("2.21.3")
35033
- return "2.21.3";
35032
+ if ("2.22.0")
35033
+ return "2.22.0";
35034
35034
  } catch {}
35035
35035
  const dirsToTry = [];
35036
35036
  try {
@@ -35079,8 +35079,8 @@ function parseIndicatorArg(raw) {
35079
35079
  err.key = key;
35080
35080
  throw err;
35081
35081
  }
35082
- if (type !== "label" && type !== "status") {
35083
- const err = new Error("indicator type must be 'label' or 'status'");
35082
+ if (type !== "label" && type !== "status" && type !== "attachment") {
35083
+ const err = new Error("indicator type must be 'label', 'status', or 'attachment'");
35084
35084
  err.type = type;
35085
35085
  throw err;
35086
35086
  }
@@ -35420,7 +35420,9 @@ var init_cli = __esm(() => {
35420
35420
  " Keys: getTodo, getInProgress, getConflicted, getReview, getAutoMerge,",
35421
35421
  " setInProgress, setDone, setError, setConflicted,",
35422
35422
  " clearConflicted, clearReview",
35423
- " Types: label, status",
35423
+ " Types: label, status, attachment",
35424
+ " --indicator setInProgress:attachment:In Progress",
35425
+ " (attachment upserts a single 'Ralphy' entry; value = subtitle)",
35424
35426
  " --create-pr Push the worker branch and open a GitHub PR on success (needs --worktree)",
35425
35427
  " --fix-ci After opening the PR, re-run on CI failures until green (needs --create-pr)",
35426
35428
  " --code-review Watch open tracked PRs for unresolved review comments and prepend a code-review task",
@@ -59669,12 +59671,16 @@ var MarkerSchema, GetIndicatorSchema, SetIndicatorSchema, IndicatorsSchema, Ralp
59669
59671
 
59670
59672
  // Applied when Ralph picks up an issue.
59671
59673
  // "setInProgress": { "type": "label", "value": "ralph:in-progress" },
59674
+ // \u2014 or use attachment type to stamp a single "Ralphy" entry on the issue:
59675
+ // "setInProgress": { "type": "attachment", "value": "In Progress" },
59672
59676
 
59673
59677
  // Applied on clean success.
59674
59678
  // "setDone": { "type": "status", "value": "In Review" },
59679
+ // "setDone": { "type": "attachment", "value": "Done" },
59675
59680
 
59676
59681
  // Applied when the task exits with an error (quarantine signal).
59677
59682
  // "setError": { "type": "label", "value": "ralph:error" },
59683
+ // "setError": { "type": "attachment", "value": "Error" },
59678
59684
 
59679
59685
  // Applied when a PR merge conflict is detected.
59680
59686
  // "setConflicted": { "type": "label", "value": "ralph:conflict" },
@@ -59691,7 +59697,7 @@ var MarkerSchema, GetIndicatorSchema, SetIndicatorSchema, IndicatorsSchema, Ralp
59691
59697
  var init_config = __esm(() => {
59692
59698
  init_zod();
59693
59699
  MarkerSchema = exports_external.object({
59694
- type: exports_external.enum(["label", "status"]),
59700
+ type: exports_external.enum(["label", "status", "attachment"]),
59695
59701
  value: exports_external.string().min(1)
59696
59702
  });
59697
59703
  GetIndicatorSchema = exports_external.object({
@@ -60007,6 +60013,44 @@ async function fetchIssueComments(apiKey, issueId) {
60007
60013
  const data = await linearRequest(apiKey, query, { id: issueId });
60008
60014
  return data.issue?.comments.nodes ?? [];
60009
60015
  }
60016
+ async function createRalphyAttachment(apiKey, issueId, issueUrl, subtitle) {
60017
+ const mutation = `mutation CreateAttachment(
60018
+ $issueId: String!, $url: String!, $title: String!, $subtitle: String!
60019
+ ) {
60020
+ attachmentCreate(input: { issueId: $issueId, url: $url, title: $title, subtitle: $subtitle }) {
60021
+ success
60022
+ attachment { id }
60023
+ }
60024
+ }`;
60025
+ const data = await linearRequest(apiKey, mutation, {
60026
+ issueId,
60027
+ url: issueUrl,
60028
+ title: RALPHY_ATTACHMENT_TITLE,
60029
+ subtitle
60030
+ });
60031
+ const attachmentId = data.attachmentCreate.attachment?.id;
60032
+ if (!attachmentId)
60033
+ throw new Error("attachmentCreate returned no attachment id");
60034
+ return attachmentId;
60035
+ }
60036
+ async function updateAttachmentSubtitle(apiKey, attachmentId, subtitle) {
60037
+ const mutation = `mutation UpdateAttachment($id: String!, $subtitle: String!) {
60038
+ attachmentUpdate(id: $id, input: { subtitle: $subtitle }) { success }
60039
+ }`;
60040
+ await linearRequest(apiKey, mutation, {
60041
+ id: attachmentId,
60042
+ subtitle
60043
+ });
60044
+ }
60045
+ async function upsertRalphyAttachment(apiKey, issueId, issueUrl, subtitle) {
60046
+ const attachments = await fetchIssueAttachments(apiKey, issueId);
60047
+ const existing = attachments.find((a) => a.title === RALPHY_ATTACHMENT_TITLE);
60048
+ if (existing) {
60049
+ await updateAttachmentSubtitle(apiKey, existing.id, subtitle);
60050
+ } else {
60051
+ await createRalphyAttachment(apiKey, issueId, issueUrl, subtitle);
60052
+ }
60053
+ }
60010
60054
  async function fetchIssueAttachments(apiKey, issueId) {
60011
60055
  const query = `query IssueAttachments($id: String!) {
60012
60056
  issue(id: $id) {
@@ -60127,7 +60171,7 @@ async function removeLabelFromIssue(apiKey, issueId, labelId) {
60127
60171
  labelId
60128
60172
  });
60129
60173
  }
60130
- var LINEAR_API = "https://api.linear.app/graphql", BRANCH_LABEL_PREFIX = "ralph:branch:";
60174
+ var LINEAR_API = "https://api.linear.app/graphql", RALPHY_ATTACHMENT_TITLE = "Ralphy", BRANCH_LABEL_PREFIX = "ralph:branch:";
60131
60175
 
60132
60176
  // apps/cli/src/agent/coordinator.ts
60133
60177
  class AgentCoordinator {
@@ -61404,6 +61448,9 @@ function buildAgentCoordinator(input) {
61404
61448
  }
61405
61449
  await updateIssueState(apiKey, issue.id, id);
61406
61450
  onLog(` \u2192 ${issue.identifier} status='${m.value}'`, "gray");
61451
+ } else if (m.type === "attachment") {
61452
+ await upsertRalphyAttachment(apiKey, issue.id, issue.url, m.value);
61453
+ onLog(` \u2192 ${issue.identifier} attachment='${m.value}'`, "gray");
61407
61454
  } else {
61408
61455
  const id = await resolveLabelId(issue, m.value);
61409
61456
  if (!id) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neriros/ralphy",
3
- "version": "2.21.3",
3
+ "version": "2.22.0",
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",