@open-tomato/rafa 0.12.0 → 0.14.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.
@@ -4,7 +4,7 @@ import {
4
4
  openNdjsonStore,
5
5
  openSqliteStore,
6
6
  selectEffortStore
7
- } from "../../index-4g6vyznd.js";
7
+ } from "../../index-g9kvby04.js";
8
8
  import {
9
9
  STORE_BACKENDS
10
10
  } from "../../index-96pz9023.js";
@@ -867,6 +867,14 @@ function runningCommand() {
867
867
  return running;
868
868
  }
869
869
 
870
+ // src/utils/session-env.ts
871
+ function sessionSpawnEnv(base) {
872
+ return {
873
+ ...base,
874
+ CLAUDE_CODE_ENTRYPOINT: "cli"
875
+ };
876
+ }
877
+
870
878
  // src/utils/claude.ts
871
879
  async function getClaudeUsagePercent() {
872
880
  const envPct = process.env["CLAUDE_USAGE_PERCENT"];
@@ -912,12 +920,6 @@ function interruptClaudeSessions() {
912
920
  session.kill("SIGINT");
913
921
  return liveSessions.size;
914
922
  }
915
- function claudeSessionEnv() {
916
- return {
917
- ...process.env,
918
- CLAUDE_CODE_ENTRYPOINT: "cli"
919
- };
920
- }
921
923
 
922
924
  class UndeclaredSpendError extends Error {
923
925
  name = "UndeclaredSpendError";
@@ -962,7 +964,7 @@ async function spawnClaude(args, prompt) {
962
964
  stdin: new TextEncoder().encode(prompt),
963
965
  stdout: "inherit",
964
966
  stderr: "inherit",
965
- env: claudeSessionEnv()
967
+ env: sessionSpawnEnv(process.env)
966
968
  });
967
969
  liveSessions.add(proc);
968
970
  try {
@@ -1015,7 +1017,7 @@ async function spawnCaptured(args, prompt, options = {}) {
1015
1017
  stdin: new TextEncoder().encode(prompt),
1016
1018
  stdout: "pipe",
1017
1019
  stderr: "inherit",
1018
- env: claudeSessionEnv(),
1020
+ env: sessionSpawnEnv(process.env),
1019
1021
  ...options.cwd === undefined ? {} : { cwd: options.cwd }
1020
1022
  });
1021
1023
  liveSessions.add(proc);
@@ -1131,11 +1133,16 @@ var REST_STATES = {
1131
1133
  cancelled: "closed"
1132
1134
  };
1133
1135
  function createGhRunner(options) {
1134
- const { cwd, command = "gh" } = options;
1136
+ const { cwd, command = "gh", env } = options;
1135
1137
  return async (args) => {
1138
+ const executable = env === undefined ? command : Bun.which(command, { PATH: env.PATH ?? "", cwd });
1139
+ if (executable === null) {
1140
+ return { ok: false, stdout: "", stderr: notOnPath(command, cwd, env?.PATH) };
1141
+ }
1136
1142
  try {
1137
- const proc = Bun.spawn([command, ...args], {
1143
+ const proc = Bun.spawn([executable, ...args], {
1138
1144
  cwd,
1145
+ ...env === undefined ? {} : { env: { ...env } },
1139
1146
  stdin: "ignore",
1140
1147
  stdout: "pipe",
1141
1148
  stderr: "pipe"
@@ -1151,6 +1158,10 @@ function createGhRunner(options) {
1151
1158
  }
1152
1159
  };
1153
1160
  }
1161
+ function notOnPath(command, cwd, path) {
1162
+ const searched = path === undefined ? "an environment with no PATH" : `PATH ${JSON.stringify(path)}`;
1163
+ return `could not run ${command} in ${cwd}: not found on ${searched} (ENOENT)`;
1164
+ }
1154
1165
  function isOneOf(members, value) {
1155
1166
  return typeof value === "string" && members.includes(value);
1156
1167
  }
@@ -1284,20 +1295,26 @@ function listedIssues(stdout) {
1284
1295
  function labelValue(labels, prefix) {
1285
1296
  return labels.find((label) => label.startsWith(prefix))?.slice(prefix.length);
1286
1297
  }
1298
+ function typeOfLabels(labels) {
1299
+ const type = labelValue(labels, GITHUB_LABELS.typePrefix);
1300
+ return isOneOf(ISSUE_TYPES, type) ? type : "code";
1301
+ }
1302
+ function moduleOfLabels(labels) {
1303
+ return labelValue(labels, GITHUB_LABELS.modulePrefix) ?? "unassigned";
1304
+ }
1287
1305
  function stateOf(viewed) {
1288
1306
  if (viewed.state === "OPEN")
1289
1307
  return "todo";
1290
1308
  return viewed.stateReason === NOT_PLANNED ? "cancelled" : "done";
1291
1309
  }
1292
1310
  function issueOf(ref, viewed) {
1293
- const type = labelValue(viewed.labels, GITHUB_LABELS.typePrefix);
1294
1311
  const priority = labelValue(viewed.labels, GITHUB_LABELS.priorityPrefix);
1295
1312
  return {
1296
1313
  opt: ref.opt,
1297
1314
  title: viewed.title,
1298
1315
  body: viewed.body,
1299
- type: isOneOf(ISSUE_TYPES, type) ? type : "code",
1300
- module: labelValue(viewed.labels, GITHUB_LABELS.modulePrefix) ?? "unassigned",
1316
+ type: typeOfLabels(viewed.labels),
1317
+ module: moduleOfLabels(viewed.labels),
1301
1318
  priority: isOneOf(ISSUE_PRIORITIES, priority) ? priority : null,
1302
1319
  project: null,
1303
1320
  blockedBy: viewed.labels.map((label) => label.startsWith(GITHUB_LABELS.blockedByPrefix) ? label.slice(GITHUB_LABELS.blockedByPrefix.length) : "").filter(isIssueNumber).map(Number),
package/dist/index.js CHANGED
@@ -50,7 +50,7 @@ import {
50
50
  sqliteStorePath,
51
51
  withSqliteStore,
52
52
  writeSqliteStore
53
- } from "./index-4g6vyznd.js";
53
+ } from "./index-g9kvby04.js";
54
54
  import {
55
55
  BUDGET_FLAG,
56
56
  CLAUDE_SETTING_SOURCES,
@@ -1902,7 +1902,7 @@ async function report(args, repoRoot) {
1902
1902
  }
1903
1903
  }
1904
1904
  // package.json
1905
- var version = "0.12.0";
1905
+ var version = "0.14.0";
1906
1906
 
1907
1907
  // src/modules/manifest.ts
1908
1908
  var MANIFEST_VERSION = 1;
@@ -4191,6 +4191,7 @@ var BLOCKED_BY_FIELD = "Blocked by";
4191
4191
  var FENCE_LINE2 = /^ {0,3}(?:```|~~~)/u;
4192
4192
  var FIELD_LINE = /^ {0,3}(?:[-*+]\s+)?[*_]{0,2}blocked\s+by[*_]{0,2}\s*:(.*)$/iu;
4193
4193
  var ISSUE_ID = /#(\d+)/gu;
4194
+ var FOREIGN_ID = /[A-Za-z0-9][A-Za-z0-9-]*\/[A-Za-z0-9._-]+#\d+/gu;
4194
4195
  var REMEDY4 = `name them as "${BLOCKED_BY_FIELD}: #24 #26", or take the ${SPEC_BLOCKED_LABEL} label off`;
4195
4196
  function hasSpecBlockedLabel(labels) {
4196
4197
  return labels.some((label) => label.trim().toLowerCase() === SPEC_BLOCKED_LABEL);
@@ -4223,29 +4224,41 @@ function idsIn(text) {
4223
4224
  }
4224
4225
  return found;
4225
4226
  }
4226
- function reading(kind, issue, found, blockers, unknown) {
4227
+ function foreignIn(text) {
4228
+ const found = [];
4229
+ for (const [token] of text.matchAll(FOREIGN_ID)) {
4230
+ if (!found.includes(token))
4231
+ found.push(token);
4232
+ }
4233
+ return found;
4234
+ }
4235
+ var NAMED_NOTHING = { ids: [], foreign: [] };
4236
+ function reading(kind, issue, found, named, unknown) {
4227
4237
  return {
4228
4238
  kind,
4229
4239
  issue,
4230
4240
  line: found?.number ?? null,
4231
4241
  text: found?.text ?? null,
4232
- blockers,
4242
+ blockers: named.ids,
4243
+ foreign: named.foreign,
4233
4244
  unknown
4234
4245
  };
4235
4246
  }
4236
4247
  function readBlockedBy(issue, body, known) {
4237
4248
  const found = findFieldLine(body);
4238
4249
  if (found === null)
4239
- return reading("no-line", issue, null, [], []);
4240
- const ids = idsIn(found.text);
4250
+ return reading("no-line", issue, null, NAMED_NOTHING, []);
4251
+ const foreign = foreignIn(found.text);
4252
+ const ids = idsIn(found.text.replace(FOREIGN_ID, " "));
4253
+ const named = { ids, foreign };
4241
4254
  if (ids.length === 0)
4242
- return reading("no-ids", issue, found, [], []);
4255
+ return reading("no-ids", issue, found, named, []);
4243
4256
  const unknown = known === undefined ? [] : ids.filter((id) => id !== issue && !known.has(id));
4244
4257
  if (ids.includes(issue))
4245
- return reading("self-reference", issue, found, ids, unknown);
4258
+ return reading("self-reference", issue, found, named, unknown);
4246
4259
  if (unknown.length > 0)
4247
- return reading("unknown-issue", issue, found, ids, unknown);
4248
- return reading("blocked", issue, found, ids, unknown);
4260
+ return reading("unknown-issue", issue, found, named, unknown);
4261
+ return reading("blocked", issue, found, named, unknown);
4249
4262
  }
4250
4263
  function nameIds(ids) {
4251
4264
  return ids.map((id) => `#${String(id)}`).join(" ");
@@ -4255,6 +4268,9 @@ function faultClause(read) {
4255
4268
  if (read.kind === "no-line") {
4256
4269
  return `is labelled ${SPEC_BLOCKED_LABEL} and its body carries no "${BLOCKED_BY_FIELD}:" line`;
4257
4270
  }
4271
+ if (read.kind === "no-ids" && read.foreign.length > 0) {
4272
+ return `has a "${BLOCKED_BY_FIELD}:" line ${at} naming only issues on other repositories: ${read.foreign.join(" ")}`;
4273
+ }
4258
4274
  if (read.kind === "no-ids") {
4259
4275
  return `has a "${BLOCKED_BY_FIELD}:" line ${at} naming no issue: "${read.text ?? ""}"`;
4260
4276
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-tomato/rafa",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "description": "Alpha. Runs a plan through the ralph loop: one Claude Code session per task, one commit per task, then a pull request and a CI wait.",
5
5
  "private": false,
6
6
  "publishConfig": {