@open-tomato/rafa 0.9.2 → 0.11.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-eqsm86n7.js";
7
+ } from "../../index-srdtw7w1.js";
8
8
  import {
9
9
  STORE_BACKENDS
10
10
  } from "../../index-96pz9023.js";
@@ -840,6 +840,33 @@ function parseSpecReview(output) {
840
840
  return judged(verdict, readGaps(document, issues), block, issues);
841
841
  }
842
842
 
843
+ // src/cli/command.ts
844
+ var MAX_EXIT_CODE = 255;
845
+
846
+ class CommandExit extends Error {
847
+ exitCode;
848
+ constructor(exitCode, message = "") {
849
+ super(message);
850
+ if (!Number.isInteger(exitCode) || exitCode < 0 || exitCode > MAX_EXIT_CODE) {
851
+ throw new TypeError(`CommandExit: exit code is ${describeValue(exitCode)}, expected a whole number from 0 to ${MAX_EXIT_CODE}`);
852
+ }
853
+ this.name = "CommandExit";
854
+ this.exitCode = exitCode;
855
+ }
856
+ }
857
+ function isTopLevel(command) {
858
+ return command.subject === command.action;
859
+ }
860
+ function commandSpelling(command) {
861
+ return isTopLevel(command) ? command.subject : `${command.subject} ${command.action}`;
862
+ }
863
+
864
+ // src/cli/running.ts
865
+ var running = null;
866
+ function runningCommand() {
867
+ return running;
868
+ }
869
+
843
870
  // src/utils/claude.ts
844
871
  async function getClaudeUsagePercent() {
845
872
  const envPct = process.env["CLAUDE_USAGE_PERCENT"];
@@ -891,9 +918,44 @@ function claudeSessionEnv() {
891
918
  CLAUDE_CODE_ENTRYPOINT: "cli"
892
919
  };
893
920
  }
921
+
922
+ class UndeclaredSpendError extends Error {
923
+ name = "UndeclaredSpendError";
924
+ }
925
+ function carriesFlag(flags, flag) {
926
+ const name = flag.replace(/^--/, "");
927
+ const value = flags[name];
928
+ if (value !== undefined && value !== false)
929
+ return true;
930
+ return name.startsWith("no-") && flags[name.slice(3)] === false;
931
+ }
932
+ function spendRefusal(running2) {
933
+ if (running2 === null)
934
+ return null;
935
+ const { command, flags } = running2;
936
+ const spend = command.spends;
937
+ const named = `rafa ${commandSpelling(command)}`;
938
+ const remedy = "declare spends on the command to cover this run";
939
+ if (spend === undefined) {
940
+ return `${named} started a Claude session but declares no spends: ${remedy}`;
941
+ }
942
+ if (spend.when === "with" && !carriesFlag(flags, spend.flag)) {
943
+ return `${named} started a Claude session without ${spend.flag}, and its spends covers only a run with ${spend.flag}: ${remedy}`;
944
+ }
945
+ if (spend.when === "unless" && carriesFlag(flags, spend.flag)) {
946
+ return `${named} started a Claude session with ${spend.flag}, and its spends covers no run with ${spend.flag}: ${remedy}`;
947
+ }
948
+ return null;
949
+ }
950
+ function guardSpend() {
951
+ const refusal = spendRefusal(runningCommand());
952
+ if (refusal !== null)
953
+ throw new UndeclaredSpendError(refusal);
954
+ }
894
955
  async function spawnClaude(args, prompt) {
956
+ guardSpend();
895
957
  if (activeOutputMode() === "json") {
896
- const { exitCode } = await spawnClaudeCaptured(args, prompt);
958
+ const { exitCode } = await spawnCaptured(args, prompt);
897
959
  return exitCode;
898
960
  }
899
961
  const proc = Bun.spawn([CLAUDE_BIN, ...args], {
@@ -945,6 +1007,10 @@ async function teeToOperator(stream) {
945
1007
  return text + tail;
946
1008
  }
947
1009
  async function spawnClaudeCaptured(args, prompt) {
1010
+ guardSpend();
1011
+ return spawnCaptured(args, prompt);
1012
+ }
1013
+ async function spawnCaptured(args, prompt) {
948
1014
  const proc = Bun.spawn([CLAUDE_BIN, ...args], {
949
1015
  stdin: new TextEncoder().encode(prompt),
950
1016
  stdout: "pipe",
@@ -1771,4 +1837,4 @@ function selectEffortStore(repoRoot, config) {
1771
1837
  return adapter.create({ repoRoot });
1772
1838
  }
1773
1839
 
1774
- export { EFFORT_KEY_PROJECTIONS, openNdjsonStore, sqliteStorePath, LONE_SURROGATE, withSqliteStore, writeSqliteStore, openSqliteStore, getClaudeUsagePercent, checkUsage, interruptClaudeSessions, runClaude, runClaudeCaptured, planFilePath, ClaudePlannerError, createGhRunner, createLocalTracker, PORT_VERSIONS, createAdapterRegistry, CORE_ADAPTER_REGISTRY, selectEffortStore };
1840
+ export { EFFORT_KEY_PROJECTIONS, openNdjsonStore, sqliteStorePath, LONE_SURROGATE, withSqliteStore, writeSqliteStore, openSqliteStore, CommandExit, commandSpelling, getClaudeUsagePercent, checkUsage, interruptClaudeSessions, runClaude, runClaudeCaptured, planFilePath, ClaudePlannerError, createGhRunner, createLocalTracker, PORT_VERSIONS, createAdapterRegistry, CORE_ADAPTER_REGISTRY, selectEffortStore };