@klaudworks/rmr 0.4.4 → 1.0.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/dist/index.js CHANGED
@@ -9616,26 +9616,6 @@ function String2(descriptor, ...args) {
9616
9616
  return StringPositional(descriptor);
9617
9617
  }
9618
9618
  }
9619
- // src/lib/config.ts
9620
- import { mkdir } from "node:fs/promises";
9621
- import { resolve } from "node:path";
9622
- async function loadConfig(workspaceRoot = process.cwd()) {
9623
- const root = resolve(workspaceRoot);
9624
- const rexDir = resolve(root, ".rmr");
9625
- const config = {
9626
- workspaceRoot: root,
9627
- rexDir,
9628
- runsDir: resolve(rexDir, "runs"),
9629
- workflowsDir: resolve(rexDir, "workflows")
9630
- };
9631
- await Promise.all([
9632
- mkdir(config.rexDir, { recursive: true }),
9633
- mkdir(config.runsDir, { recursive: true }),
9634
- mkdir(config.workflowsDir, { recursive: true })
9635
- ]);
9636
- return config;
9637
- }
9638
-
9639
9619
  // src/lib/errors.ts
9640
9620
  class RmrError extends Error {
9641
9621
  code;
@@ -9674,1173 +9654,1618 @@ class StorageError extends RmrError {
9674
9654
  }
9675
9655
  }
9676
9656
 
9677
- // src/lib/completions.ts
9678
- import { access, readdir } from "node:fs/promises";
9679
- import { resolve as resolve2 } from "node:path";
9680
- function matchesPartial(value, partial) {
9681
- if (!partial) {
9682
- return true;
9657
+ // node_modules/chalk/source/vendor/ansi-styles/index.js
9658
+ var ANSI_BACKGROUND_OFFSET = 10;
9659
+ var wrapAnsi16 = (offset = 0) => (code) => `\x1B[${code + offset}m`;
9660
+ var wrapAnsi256 = (offset = 0) => (code) => `\x1B[${38 + offset};5;${code}m`;
9661
+ var wrapAnsi16m = (offset = 0) => (red, green, blue) => `\x1B[${38 + offset};2;${red};${green};${blue}m`;
9662
+ var styles = {
9663
+ modifier: {
9664
+ reset: [0, 0],
9665
+ bold: [1, 22],
9666
+ dim: [2, 22],
9667
+ italic: [3, 23],
9668
+ underline: [4, 24],
9669
+ overline: [53, 55],
9670
+ inverse: [7, 27],
9671
+ hidden: [8, 28],
9672
+ strikethrough: [9, 29]
9673
+ },
9674
+ color: {
9675
+ black: [30, 39],
9676
+ red: [31, 39],
9677
+ green: [32, 39],
9678
+ yellow: [33, 39],
9679
+ blue: [34, 39],
9680
+ magenta: [35, 39],
9681
+ cyan: [36, 39],
9682
+ white: [37, 39],
9683
+ blackBright: [90, 39],
9684
+ gray: [90, 39],
9685
+ grey: [90, 39],
9686
+ redBright: [91, 39],
9687
+ greenBright: [92, 39],
9688
+ yellowBright: [93, 39],
9689
+ blueBright: [94, 39],
9690
+ magentaBright: [95, 39],
9691
+ cyanBright: [96, 39],
9692
+ whiteBright: [97, 39]
9693
+ },
9694
+ bgColor: {
9695
+ bgBlack: [40, 49],
9696
+ bgRed: [41, 49],
9697
+ bgGreen: [42, 49],
9698
+ bgYellow: [43, 49],
9699
+ bgBlue: [44, 49],
9700
+ bgMagenta: [45, 49],
9701
+ bgCyan: [46, 49],
9702
+ bgWhite: [47, 49],
9703
+ bgBlackBright: [100, 49],
9704
+ bgGray: [100, 49],
9705
+ bgGrey: [100, 49],
9706
+ bgRedBright: [101, 49],
9707
+ bgGreenBright: [102, 49],
9708
+ bgYellowBright: [103, 49],
9709
+ bgBlueBright: [104, 49],
9710
+ bgMagentaBright: [105, 49],
9711
+ bgCyanBright: [106, 49],
9712
+ bgWhiteBright: [107, 49]
9683
9713
  }
9684
- return value.startsWith(partial);
9685
- }
9686
- async function listRunIdCompletions(config, partial = "") {
9687
- const entries = await readdir(config.runsDir, { withFileTypes: true });
9688
- return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -".json".length)).filter((id) => matchesPartial(id, partial)).sort();
9689
- }
9690
- async function listWorkflowCompletions(config, partial = "") {
9691
- const entries = await readdir(config.workflowsDir, { withFileTypes: true });
9692
- const workflows = [];
9693
- for (const entry of entries) {
9694
- if (!entry.isDirectory()) {
9695
- continue;
9714
+ };
9715
+ var modifierNames = Object.keys(styles.modifier);
9716
+ var foregroundColorNames = Object.keys(styles.color);
9717
+ var backgroundColorNames = Object.keys(styles.bgColor);
9718
+ var colorNames = [...foregroundColorNames, ...backgroundColorNames];
9719
+ function assembleStyles() {
9720
+ const codes = new Map;
9721
+ for (const [groupName, group] of Object.entries(styles)) {
9722
+ for (const [styleName, style] of Object.entries(group)) {
9723
+ styles[styleName] = {
9724
+ open: `\x1B[${style[0]}m`,
9725
+ close: `\x1B[${style[1]}m`
9726
+ };
9727
+ group[styleName] = styles[styleName];
9728
+ codes.set(style[0], style[1]);
9696
9729
  }
9697
- const workflowYaml = resolve2(config.workflowsDir, entry.name, "workflow.yaml");
9698
- const workflowYml = resolve2(config.workflowsDir, entry.name, "workflow.yml");
9699
- try {
9700
- await access(workflowYaml);
9701
- workflows.push(workflowYaml);
9702
- continue;
9703
- } catch {}
9704
- try {
9705
- await access(workflowYml);
9706
- workflows.push(workflowYml);
9707
- } catch {}
9708
- }
9709
- return workflows.filter((filePath) => matchesPartial(filePath, partial)).sort();
9710
- }
9711
-
9712
- // src/commands/complete.ts
9713
- function parseTarget(value) {
9714
- if (value === "run-id" || value === "workflow") {
9715
- return value;
9730
+ Object.defineProperty(styles, groupName, {
9731
+ value: group,
9732
+ enumerable: false
9733
+ });
9716
9734
  }
9717
- throw new UserInputError(`Invalid completion target "${value}".`);
9718
- }
9719
-
9720
- class CompleteCommand extends Command {
9721
- static paths = [["complete"]];
9722
- target = exports_options.String({
9723
- name: "target"
9724
- });
9725
- partial = exports_options.String({
9726
- required: false,
9727
- name: "partial"
9735
+ Object.defineProperty(styles, "codes", {
9736
+ value: codes,
9737
+ enumerable: false
9728
9738
  });
9729
- async execute() {
9730
- const config = await loadConfig();
9731
- const target = parseTarget(this.target);
9732
- const query = this.partial ?? "";
9733
- const suggestions = target === "run-id" ? await listRunIdCompletions(config, query) : await listWorkflowCompletions(config, query);
9734
- for (const value of suggestions) {
9735
- process.stdout.write(`${value}
9736
- `);
9739
+ styles.color.close = "\x1B[39m";
9740
+ styles.bgColor.close = "\x1B[49m";
9741
+ styles.color.ansi = wrapAnsi16();
9742
+ styles.color.ansi256 = wrapAnsi256();
9743
+ styles.color.ansi16m = wrapAnsi16m();
9744
+ styles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET);
9745
+ styles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET);
9746
+ styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET);
9747
+ Object.defineProperties(styles, {
9748
+ rgbToAnsi256: {
9749
+ value(red, green, blue) {
9750
+ if (red === green && green === blue) {
9751
+ if (red < 8) {
9752
+ return 16;
9753
+ }
9754
+ if (red > 248) {
9755
+ return 231;
9756
+ }
9757
+ return Math.round((red - 8) / 247 * 24) + 232;
9758
+ }
9759
+ return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green / 255 * 5) + Math.round(blue / 255 * 5);
9760
+ },
9761
+ enumerable: false
9762
+ },
9763
+ hexToRgb: {
9764
+ value(hex) {
9765
+ const matches = /[a-f\d]{6}|[a-f\d]{3}/i.exec(hex.toString(16));
9766
+ if (!matches) {
9767
+ return [0, 0, 0];
9768
+ }
9769
+ let [colorString] = matches;
9770
+ if (colorString.length === 3) {
9771
+ colorString = [...colorString].map((character) => character + character).join("");
9772
+ }
9773
+ const integer = Number.parseInt(colorString, 16);
9774
+ return [
9775
+ integer >> 16 & 255,
9776
+ integer >> 8 & 255,
9777
+ integer & 255
9778
+ ];
9779
+ },
9780
+ enumerable: false
9781
+ },
9782
+ hexToAnsi256: {
9783
+ value: (hex) => styles.rgbToAnsi256(...styles.hexToRgb(hex)),
9784
+ enumerable: false
9785
+ },
9786
+ ansi256ToAnsi: {
9787
+ value(code) {
9788
+ if (code < 8) {
9789
+ return 30 + code;
9790
+ }
9791
+ if (code < 16) {
9792
+ return 90 + (code - 8);
9793
+ }
9794
+ let red;
9795
+ let green;
9796
+ let blue;
9797
+ if (code >= 232) {
9798
+ red = ((code - 232) * 10 + 8) / 255;
9799
+ green = red;
9800
+ blue = red;
9801
+ } else {
9802
+ code -= 16;
9803
+ const remainder = code % 36;
9804
+ red = Math.floor(code / 36) / 5;
9805
+ green = Math.floor(remainder / 6) / 5;
9806
+ blue = remainder % 6 / 5;
9807
+ }
9808
+ const value = Math.max(red, green, blue) * 2;
9809
+ if (value === 0) {
9810
+ return 30;
9811
+ }
9812
+ let result = 30 + (Math.round(blue) << 2 | Math.round(green) << 1 | Math.round(red));
9813
+ if (value === 2) {
9814
+ result += 60;
9815
+ }
9816
+ return result;
9817
+ },
9818
+ enumerable: false
9819
+ },
9820
+ rgbToAnsi: {
9821
+ value: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)),
9822
+ enumerable: false
9823
+ },
9824
+ hexToAnsi: {
9825
+ value: (hex) => styles.ansi256ToAnsi(styles.hexToAnsi256(hex)),
9826
+ enumerable: false
9737
9827
  }
9738
- return 0;
9739
- }
9828
+ });
9829
+ return styles;
9740
9830
  }
9831
+ var ansiStyles = assembleStyles();
9832
+ var ansi_styles_default = ansiStyles;
9741
9833
 
9742
- // src/commands/completion.ts
9743
- function parseShell(value) {
9744
- if (value === "bash" || value === "zsh" || value === "fish") {
9745
- return value;
9746
- }
9747
- throw new UserInputError(`Unsupported shell "${value}". Use bash, zsh, or fish.`);
9748
- }
9749
- function bashScript() {
9750
- return [
9751
- "_rex_complete() {",
9752
- " local cur prev",
9753
- " COMPREPLY=()",
9754
- ' cur="${COMP_WORDS[COMP_CWORD]}"',
9755
- ' prev="${COMP_WORDS[COMP_CWORD-1]}"',
9756
- "",
9757
- " if [[ ${COMP_CWORD} -eq 1 ]]; then",
9758
- ' COMPREPLY=( $(compgen -W "install run continue complete completion --help --version" -- "${cur}") )',
9759
- " return 0",
9760
- " fi",
9761
- "",
9762
- ' if [[ "${prev}" == "continue" ]]; then',
9763
- ' COMPREPLY=( $(rmr complete run-id "${cur}") )',
9764
- " return 0",
9765
- " fi",
9766
- "",
9767
- ' if [[ "${prev}" == "run" ]]; then',
9768
- ' COMPREPLY=( $(rmr complete workflow "${cur}") )',
9769
- " return 0",
9770
- " fi",
9771
- "",
9772
- ' if [[ "${prev}" == "install" ]]; then',
9773
- ' COMPREPLY=( $(compgen -W "feature-dev" -- "${cur}") )',
9774
- " return 0",
9775
- " fi",
9776
- "}",
9777
- "complete -F _rex_complete rmr"
9778
- ].join(`
9779
- `);
9780
- }
9781
- function zshScript() {
9782
- return [
9783
- "#compdef rmr",
9784
- "_rex_complete() {",
9785
- " local -a subcommands",
9786
- " subcommands=(install run continue complete completion)",
9787
- "",
9788
- " if (( CURRENT == 2 )); then",
9789
- " _describe 'command' subcommands",
9790
- " return",
9791
- " fi",
9792
- "",
9793
- " if [[ ${words[2]} == continue && $CURRENT -eq 3 ]]; then",
9794
- ' compadd -- $(rmr complete run-id "${words[CURRENT]}")',
9795
- " return",
9796
- " fi",
9797
- "",
9798
- " if [[ ${words[2]} == run && $CURRENT -eq 3 ]]; then",
9799
- ' compadd -- $(rmr complete workflow "${words[CURRENT]}")',
9800
- " return",
9801
- " fi",
9802
- "",
9803
- " if [[ ${words[2]} == install && $CURRENT -eq 3 ]]; then",
9804
- " compadd -- feature-dev",
9805
- " return",
9806
- " fi",
9807
- "}",
9808
- "compdef _rex_complete rmr"
9809
- ].join(`
9810
- `);
9834
+ // node_modules/chalk/source/vendor/supports-color/index.js
9835
+ import process2 from "node:process";
9836
+ import os from "node:os";
9837
+ import tty2 from "node:tty";
9838
+ function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process2.argv) {
9839
+ const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
9840
+ const position = argv.indexOf(prefix + flag);
9841
+ const terminatorPosition = argv.indexOf("--");
9842
+ return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);
9811
9843
  }
9812
- function fishScript() {
9813
- return [
9814
- "function __rex_complete_run_id",
9815
- " rmr complete run-id (commandline -ct)",
9816
- "end",
9817
- "",
9818
- "function __rex_complete_workflow",
9819
- " rmr complete workflow (commandline -ct)",
9820
- "end",
9821
- "",
9822
- "complete -c rmr -f",
9823
- "complete -c rmr -n '__fish_use_subcommand' -a 'install run continue complete completion'",
9824
- "complete -c rmr -n '__fish_seen_subcommand_from continue' -a '(__rex_complete_run_id)'",
9825
- "complete -c rmr -n '__fish_seen_subcommand_from run' -a '(__rex_complete_workflow)'",
9826
- "complete -c rmr -n '__fish_seen_subcommand_from install' -a 'feature-dev'"
9827
- ].join(`
9828
- `);
9844
+ var { env } = process2;
9845
+ var flagForceColor;
9846
+ if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) {
9847
+ flagForceColor = 0;
9848
+ } else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) {
9849
+ flagForceColor = 1;
9829
9850
  }
9830
-
9831
- class CompletionCommand extends Command {
9832
- static paths = [["completion"]];
9833
- static usage = Command.Usage({
9834
- category: "Workflow",
9835
- description: "Print optional shell completion setup script.",
9836
- details: "Generates completion script text for your shell. Source the output in your shell profile to enable command and dynamic argument completion.",
9837
- examples: [
9838
- ["Show Bash completion script", "$0 completion bash"],
9839
- ["Show Zsh completion script", "$0 completion zsh"],
9840
- ["Show Fish completion script", "$0 completion fish"]
9841
- ]
9842
- });
9843
- shell = exports_options.String({
9844
- name: "shell"
9845
- });
9846
- async execute() {
9847
- const shell = parseShell(this.shell);
9848
- const script = shell === "bash" ? bashScript() : shell === "zsh" ? zshScript() : fishScript();
9849
- process.stdout.write(`${script}
9850
- `);
9851
- return 0;
9851
+ function envForceColor() {
9852
+ if ("FORCE_COLOR" in env) {
9853
+ if (env.FORCE_COLOR === "true") {
9854
+ return 1;
9855
+ }
9856
+ if (env.FORCE_COLOR === "false") {
9857
+ return 0;
9858
+ }
9859
+ return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3);
9852
9860
  }
9853
9861
  }
9854
-
9855
- // src/lib/run-state.ts
9856
- import { readFile, writeFile } from "node:fs/promises";
9857
- import { resolve as resolve3 } from "node:path";
9858
- function pad(input) {
9859
- return String(input).padStart(2, "0");
9860
- }
9861
- function generateRunId(now = new Date) {
9862
- const yyyy = now.getUTCFullYear();
9863
- const mm = pad(now.getUTCMonth() + 1);
9864
- const dd = pad(now.getUTCDate());
9865
- const hh = pad(now.getUTCHours());
9866
- const min = pad(now.getUTCMinutes());
9867
- const sec = pad(now.getUTCSeconds());
9868
- return `${yyyy}${mm}${dd}-${hh}${min}${sec}Z`;
9869
- }
9870
- function runFilePath(config, runId) {
9871
- return resolve3(config.runsDir, `${runId}.json`);
9872
- }
9873
- function createInitialRunState(options) {
9874
- const firstStep = options.workflow.steps[0];
9875
- if (!firstStep) {
9876
- throw new StorageError("Cannot create run state without at least one workflow step.");
9862
+ function translateLevel(level) {
9863
+ if (level === 0) {
9864
+ return false;
9877
9865
  }
9878
9866
  return {
9879
- run_id: options.runId,
9880
- workflow_path: options.workflowPath,
9881
- status: "running",
9882
- current_step: firstStep.id,
9883
- context: {
9884
- task: options.task,
9885
- ...options.vars
9886
- },
9887
- last_harness: {
9888
- name: resolveHarnessForStep(options.workflow, firstStep.agent),
9889
- binary: resolveHarnessForStep(options.workflow, firstStep.agent),
9890
- session_id: null
9891
- },
9892
- step_history: [],
9893
- updated_at: new Date().toISOString()
9867
+ level,
9868
+ hasBasic: true,
9869
+ has256: level >= 2,
9870
+ has16m: level >= 3
9894
9871
  };
9895
9872
  }
9896
- function resolveHarnessForStep(workflow, stepAgentId) {
9897
- const agent = workflow.agents.find((item) => item.id === stepAgentId);
9898
- if (!agent) {
9899
- throw new StorageError(`Cannot resolve harness for unknown agent "${stepAgentId}".`);
9873
+ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
9874
+ const noFlagForceColor = envForceColor();
9875
+ if (noFlagForceColor !== undefined) {
9876
+ flagForceColor = noFlagForceColor;
9900
9877
  }
9901
- return agent.harness;
9902
- }
9903
- async function saveRunState(config, state) {
9904
- const path = runFilePath(config, state.run_id);
9905
- const payload = JSON.stringify({
9906
- ...state,
9907
- updated_at: new Date().toISOString()
9908
- }, null, 2);
9909
- await writeFile(path, `${payload}
9910
- `, "utf8");
9911
- return path;
9912
- }
9913
- async function loadRunState(config, runId) {
9914
- const path = runFilePath(config, runId);
9915
- try {
9916
- const raw = await readFile(path, "utf8");
9917
- const parsed = JSON.parse(raw);
9918
- if (!parsed || typeof parsed !== "object" || parsed.run_id !== runId) {
9919
- throw new StorageError(`Run state file is invalid for run id "${runId}".`);
9920
- }
9921
- if (!Array.isArray(parsed.step_history)) {
9922
- parsed.step_history = [];
9878
+ const forceColor = sniffFlags ? flagForceColor : noFlagForceColor;
9879
+ if (forceColor === 0) {
9880
+ return 0;
9881
+ }
9882
+ if (sniffFlags) {
9883
+ if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) {
9884
+ return 3;
9923
9885
  }
9924
- return parsed;
9925
- } catch (error) {
9926
- if (error instanceof StorageError) {
9927
- throw error;
9886
+ if (hasFlag("color=256")) {
9887
+ return 2;
9928
9888
  }
9929
- throw new StorageError(`Failed to load run state for "${runId}".`);
9930
9889
  }
9931
- }
9932
-
9933
- // src/lib/prompt-composer.ts
9934
- import { readFile as readFile2 } from "node:fs/promises";
9935
- import { dirname, resolve as resolve4 } from "node:path";
9936
- function stripFrontmatter(content) {
9937
- const match = content.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/);
9938
- return match ? content.slice(match[0].length) : content;
9939
- }
9940
- async function loadAgentPrompt(workflowPath, promptFileName) {
9941
- const promptPath = resolve4(dirname(workflowPath), promptFileName);
9942
- try {
9943
- const raw = await readFile2(promptPath, "utf8");
9944
- return stripFrontmatter(raw);
9945
- } catch {
9946
- throw new ConfigError(`Agent prompt file not found: ${promptPath}`);
9890
+ if ("TF_BUILD" in env && "AGENT_NAME" in env) {
9891
+ return 1;
9947
9892
  }
9948
- }
9949
- function composePrompt(agentPrompt, stepInput) {
9950
- return `${agentPrompt.trimEnd()}
9951
-
9952
- ${stepInput.trim()}`;
9953
- }
9954
-
9955
- // src/lib/harness-adapters.ts
9956
- function createPassthroughParser() {
9957
- return (line) => ({ text: line + `
9958
- ` });
9959
- }
9960
- function withModelArgs(model, args) {
9961
- if (!model) {
9962
- return args;
9893
+ if (haveStream && !streamIsTTY && forceColor === undefined) {
9894
+ return 0;
9963
9895
  }
9964
- return [...args, "--model", model];
9965
- }
9966
- function claudeStreamFlags() {
9967
- return ["--output-format", "stream-json", "--verbose", "--include-partial-messages"];
9968
- }
9969
- function createClaudeStreamParser() {
9970
- let currentToolName = null;
9971
- let currentToolInput = "";
9972
- let currentBlockIndex = null;
9973
- return (line) => {
9974
- if (!line.trim()) {
9975
- return null;
9896
+ const min = forceColor || 0;
9897
+ if (env.TERM === "dumb") {
9898
+ return min;
9899
+ }
9900
+ if (process2.platform === "win32") {
9901
+ const osRelease = os.release().split(".");
9902
+ if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
9903
+ return Number(osRelease[2]) >= 14931 ? 3 : 2;
9976
9904
  }
9977
- let obj;
9978
- try {
9979
- obj = JSON.parse(line);
9980
- } catch {
9981
- return null;
9905
+ return 1;
9906
+ }
9907
+ if ("CI" in env) {
9908
+ if (["GITHUB_ACTIONS", "GITEA_ACTIONS", "CIRCLECI"].some((key) => (key in env))) {
9909
+ return 3;
9982
9910
  }
9983
- const type = obj.type;
9984
- const sessionId = typeof obj.session_id === "string" ? obj.session_id : undefined;
9985
- if (type === "stream_event") {
9986
- const event = obj.event;
9987
- const index = typeof event?.index === "number" ? event.index : null;
9988
- if (event?.type === "content_block_start") {
9989
- const block = event.content_block;
9990
- if (block?.type === "tool_use" && typeof block.name === "string") {
9991
- currentToolName = block.name;
9992
- currentToolInput = "";
9993
- currentBlockIndex = index;
9994
- return sessionId ? { text: "", sessionId } : null;
9911
+ if (["TRAVIS", "APPVEYOR", "GITLAB_CI", "BUILDKITE", "DRONE"].some((sign) => (sign in env)) || env.CI_NAME === "codeship") {
9912
+ return 1;
9913
+ }
9914
+ return min;
9915
+ }
9916
+ if ("TEAMCITY_VERSION" in env) {
9917
+ return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;
9918
+ }
9919
+ if (env.COLORTERM === "truecolor") {
9920
+ return 3;
9921
+ }
9922
+ if (env.TERM === "xterm-kitty") {
9923
+ return 3;
9924
+ }
9925
+ if (env.TERM === "xterm-ghostty") {
9926
+ return 3;
9927
+ }
9928
+ if (env.TERM === "wezterm") {
9929
+ return 3;
9930
+ }
9931
+ if ("TERM_PROGRAM" in env) {
9932
+ const version = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
9933
+ switch (env.TERM_PROGRAM) {
9934
+ case "iTerm.app": {
9935
+ return version >= 3 ? 3 : 2;
9936
+ }
9937
+ case "Apple_Terminal": {
9938
+ return 2;
9939
+ }
9940
+ }
9941
+ }
9942
+ if (/-256(color)?$/i.test(env.TERM)) {
9943
+ return 2;
9944
+ }
9945
+ if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {
9946
+ return 1;
9947
+ }
9948
+ if ("COLORTERM" in env) {
9949
+ return 1;
9950
+ }
9951
+ return min;
9952
+ }
9953
+ function createSupportsColor(stream, options = {}) {
9954
+ const level = _supportsColor(stream, {
9955
+ streamIsTTY: stream && stream.isTTY,
9956
+ ...options
9957
+ });
9958
+ return translateLevel(level);
9959
+ }
9960
+ var supportsColor = {
9961
+ stdout: createSupportsColor({ isTTY: tty2.isatty(1) }),
9962
+ stderr: createSupportsColor({ isTTY: tty2.isatty(2) })
9963
+ };
9964
+ var supports_color_default = supportsColor;
9965
+
9966
+ // node_modules/chalk/source/utilities.js
9967
+ function stringReplaceAll(string, substring, replacer) {
9968
+ let index = string.indexOf(substring);
9969
+ if (index === -1) {
9970
+ return string;
9971
+ }
9972
+ const substringLength = substring.length;
9973
+ let endIndex = 0;
9974
+ let returnValue = "";
9975
+ do {
9976
+ returnValue += string.slice(endIndex, index) + substring + replacer;
9977
+ endIndex = index + substringLength;
9978
+ index = string.indexOf(substring, endIndex);
9979
+ } while (index !== -1);
9980
+ returnValue += string.slice(endIndex);
9981
+ return returnValue;
9982
+ }
9983
+ function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) {
9984
+ let endIndex = 0;
9985
+ let returnValue = "";
9986
+ do {
9987
+ const gotCR = string[index - 1] === "\r";
9988
+ returnValue += string.slice(endIndex, gotCR ? index - 1 : index) + prefix + (gotCR ? `\r
9989
+ ` : `
9990
+ `) + postfix;
9991
+ endIndex = index + 1;
9992
+ index = string.indexOf(`
9993
+ `, endIndex);
9994
+ } while (index !== -1);
9995
+ returnValue += string.slice(endIndex);
9996
+ return returnValue;
9997
+ }
9998
+
9999
+ // node_modules/chalk/source/index.js
10000
+ var { stdout: stdoutColor, stderr: stderrColor } = supports_color_default;
10001
+ var GENERATOR = Symbol("GENERATOR");
10002
+ var STYLER = Symbol("STYLER");
10003
+ var IS_EMPTY = Symbol("IS_EMPTY");
10004
+ var levelMapping = [
10005
+ "ansi",
10006
+ "ansi",
10007
+ "ansi256",
10008
+ "ansi16m"
10009
+ ];
10010
+ var styles2 = Object.create(null);
10011
+ var applyOptions = (object, options = {}) => {
10012
+ if (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) {
10013
+ throw new Error("The `level` option should be an integer from 0 to 3");
10014
+ }
10015
+ const colorLevel = stdoutColor ? stdoutColor.level : 0;
10016
+ object.level = options.level === undefined ? colorLevel : options.level;
10017
+ };
10018
+ var chalkFactory = (options) => {
10019
+ const chalk = (...strings) => strings.join(" ");
10020
+ applyOptions(chalk, options);
10021
+ Object.setPrototypeOf(chalk, createChalk.prototype);
10022
+ return chalk;
10023
+ };
10024
+ function createChalk(options) {
10025
+ return chalkFactory(options);
10026
+ }
10027
+ Object.setPrototypeOf(createChalk.prototype, Function.prototype);
10028
+ for (const [styleName, style] of Object.entries(ansi_styles_default)) {
10029
+ styles2[styleName] = {
10030
+ get() {
10031
+ const builder = createBuilder(this, createStyler(style.open, style.close, this[STYLER]), this[IS_EMPTY]);
10032
+ Object.defineProperty(this, styleName, { value: builder });
10033
+ return builder;
10034
+ }
10035
+ };
10036
+ }
10037
+ styles2.visible = {
10038
+ get() {
10039
+ const builder = createBuilder(this, this[STYLER], true);
10040
+ Object.defineProperty(this, "visible", { value: builder });
10041
+ return builder;
10042
+ }
10043
+ };
10044
+ var getModelAnsi = (model, level, type, ...arguments_) => {
10045
+ if (model === "rgb") {
10046
+ if (level === "ansi16m") {
10047
+ return ansi_styles_default[type].ansi16m(...arguments_);
10048
+ }
10049
+ if (level === "ansi256") {
10050
+ return ansi_styles_default[type].ansi256(ansi_styles_default.rgbToAnsi256(...arguments_));
10051
+ }
10052
+ return ansi_styles_default[type].ansi(ansi_styles_default.rgbToAnsi(...arguments_));
10053
+ }
10054
+ if (model === "hex") {
10055
+ return getModelAnsi("rgb", level, type, ...ansi_styles_default.hexToRgb(...arguments_));
10056
+ }
10057
+ return ansi_styles_default[type][model](...arguments_);
10058
+ };
10059
+ var usedModels = ["rgb", "hex", "ansi256"];
10060
+ for (const model of usedModels) {
10061
+ styles2[model] = {
10062
+ get() {
10063
+ const { level } = this;
10064
+ return function(...arguments_) {
10065
+ const styler = createStyler(getModelAnsi(model, levelMapping[level], "color", ...arguments_), ansi_styles_default.color.close, this[STYLER]);
10066
+ return createBuilder(this, styler, this[IS_EMPTY]);
10067
+ };
10068
+ }
10069
+ };
10070
+ const bgModel = "bg" + model[0].toUpperCase() + model.slice(1);
10071
+ styles2[bgModel] = {
10072
+ get() {
10073
+ const { level } = this;
10074
+ return function(...arguments_) {
10075
+ const styler = createStyler(getModelAnsi(model, levelMapping[level], "bgColor", ...arguments_), ansi_styles_default.bgColor.close, this[STYLER]);
10076
+ return createBuilder(this, styler, this[IS_EMPTY]);
10077
+ };
10078
+ }
10079
+ };
10080
+ }
10081
+ var proto = Object.defineProperties(() => {}, {
10082
+ ...styles2,
10083
+ level: {
10084
+ enumerable: true,
10085
+ get() {
10086
+ return this[GENERATOR].level;
10087
+ },
10088
+ set(level) {
10089
+ this[GENERATOR].level = level;
10090
+ }
10091
+ }
10092
+ });
10093
+ var createStyler = (open, close, parent) => {
10094
+ let openAll;
10095
+ let closeAll;
10096
+ if (parent === undefined) {
10097
+ openAll = open;
10098
+ closeAll = close;
10099
+ } else {
10100
+ openAll = parent.openAll + open;
10101
+ closeAll = close + parent.closeAll;
10102
+ }
10103
+ return {
10104
+ open,
10105
+ close,
10106
+ openAll,
10107
+ closeAll,
10108
+ parent
10109
+ };
10110
+ };
10111
+ var createBuilder = (self, _styler, _isEmpty) => {
10112
+ const builder = (...arguments_) => applyStyle(builder, arguments_.length === 1 ? "" + arguments_[0] : arguments_.join(" "));
10113
+ Object.setPrototypeOf(builder, proto);
10114
+ builder[GENERATOR] = self;
10115
+ builder[STYLER] = _styler;
10116
+ builder[IS_EMPTY] = _isEmpty;
10117
+ return builder;
10118
+ };
10119
+ var applyStyle = (self, string) => {
10120
+ if (self.level <= 0 || !string) {
10121
+ return self[IS_EMPTY] ? "" : string;
10122
+ }
10123
+ let styler = self[STYLER];
10124
+ if (styler === undefined) {
10125
+ return string;
10126
+ }
10127
+ const { openAll, closeAll } = styler;
10128
+ if (string.includes("\x1B")) {
10129
+ while (styler !== undefined) {
10130
+ string = stringReplaceAll(string, styler.close, styler.open);
10131
+ styler = styler.parent;
10132
+ }
10133
+ }
10134
+ const lfIndex = string.indexOf(`
10135
+ `);
10136
+ if (lfIndex !== -1) {
10137
+ string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex);
10138
+ }
10139
+ return openAll + string + closeAll;
10140
+ };
10141
+ Object.defineProperties(createChalk.prototype, styles2);
10142
+ var chalk = createChalk();
10143
+ var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
10144
+ var source_default = chalk;
10145
+
10146
+ // src/lib/ui.ts
10147
+ import * as readline from "node:readline";
10148
+
10149
+ // src/lib/binary-name.ts
10150
+ import { basename } from "node:path";
10151
+ function detectBinaryName() {
10152
+ const invokedPath = process.argv[1];
10153
+ const invokedName = invokedPath ? basename(invokedPath) : "";
10154
+ if (invokedName !== "" && !invokedName.endsWith(".js") && !invokedName.endsWith(".ts")) {
10155
+ return invokedName;
10156
+ }
10157
+ return "rmr";
10158
+ }
10159
+ var binaryName = detectBinaryName();
10160
+
10161
+ // src/lib/ui.ts
10162
+ var isTTY = process.stdout.isTTY === true;
10163
+ function getTerminalWidth() {
10164
+ return process.stdout.columns ?? 80;
10165
+ }
10166
+ function getBoxWidth() {
10167
+ const termWidth = getTerminalWidth();
10168
+ return Math.max(40, termWidth - 2);
10169
+ }
10170
+ function truncate(text, maxLength) {
10171
+ if (text.length <= maxLength) {
10172
+ return text;
10173
+ }
10174
+ return text.slice(0, maxLength - 3) + "...";
10175
+ }
10176
+ function wrapText(text, maxWidth) {
10177
+ if (text.length <= maxWidth) {
10178
+ return [text];
10179
+ }
10180
+ const lines = [];
10181
+ let remaining = text;
10182
+ while (remaining.length > maxWidth) {
10183
+ let breakAt = remaining.lastIndexOf(" ", maxWidth);
10184
+ if (breakAt <= 0) {
10185
+ breakAt = maxWidth;
10186
+ }
10187
+ lines.push(remaining.slice(0, breakAt));
10188
+ remaining = remaining.slice(breakAt).trimStart();
10189
+ }
10190
+ if (remaining) {
10191
+ lines.push(remaining);
10192
+ }
10193
+ return lines;
10194
+ }
10195
+ function stripAnsi(text) {
10196
+ return text.replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "").replace(/\x1b\][^\x07]*(?:\x07|\x1b\\)/g, "");
10197
+ }
10198
+ function displayWidth(text) {
10199
+ return Array.from(text).length;
10200
+ }
10201
+ var ui = {
10202
+ get isTTY() {
10203
+ return isTTY;
10204
+ },
10205
+ workflowHeader(info) {
10206
+ const line = isTTY ? "─" : "-";
10207
+ const corner = {
10208
+ tl: isTTY ? "╭" : "+",
10209
+ tr: isTTY ? "╮" : "+",
10210
+ bl: isTTY ? "╰" : "+",
10211
+ br: isTTY ? "╯" : "+"
10212
+ };
10213
+ const width = getBoxWidth();
10214
+ const contentWidth = width - 4;
10215
+ const border = line.repeat(width - 2);
10216
+ const labelWidth = 10;
10217
+ const valueWidth = contentWidth - labelWidth;
10218
+ const formatLine = (label, value) => {
10219
+ const paddedLabel = label ? `${label}:`.padEnd(labelWidth) : " ".repeat(labelWidth);
10220
+ const truncatedValue = truncate(value, valueWidth);
10221
+ return `${paddedLabel}${truncatedValue}`.padEnd(contentWidth);
10222
+ };
10223
+ process.stdout.write(`
10224
+ `);
10225
+ process.stdout.write(isTTY ? source_default.cyan(`${corner.tl}${line} ${info.title} ${border.slice(info.title.length + 3)}${corner.tr}
10226
+ `) : `${corner.tl}${line} ${info.title} ${border.slice(info.title.length + 3)}${corner.tr}
10227
+ `);
10228
+ process.stdout.write(isTTY ? source_default.dim(`│ ${formatLine("workflow", info.workflow)} │
10229
+ `) : `│ ${formatLine("workflow", info.workflow)} │
10230
+ `);
10231
+ process.stdout.write(isTTY ? source_default.dim(`│ ${formatLine("run-id", info.runId)} │
10232
+ `) : `│ ${formatLine("run-id", info.runId)} │
10233
+ `);
10234
+ const taskLines = info.task.split(`
10235
+ `).flatMap((line2) => {
10236
+ if (line2 === "") {
10237
+ return [""];
10238
+ }
10239
+ return wrapText(line2, valueWidth);
10240
+ });
10241
+ for (let i = 0;i < taskLines.length; i++) {
10242
+ const label = i === 0 ? "task" : "";
10243
+ const content = formatLine(label, taskLines[i] ?? "");
10244
+ process.stdout.write(isTTY ? source_default.dim(`│ ${content} │
10245
+ `) : `│ ${content} │
10246
+ `);
10247
+ }
10248
+ process.stdout.write(isTTY ? source_default.cyan(`${corner.bl}${border}${corner.br}
10249
+ `) : `${corner.bl}${border}${corner.br}
10250
+ `);
10251
+ process.stdout.write(`
10252
+ `);
10253
+ },
10254
+ stepStart(stepNumber, stepId, harness, model) {
10255
+ const line = isTTY ? "─" : "-";
10256
+ const corner = { tl: isTTY ? "┌" : "+", tr: isTTY ? "┐" : "+" };
10257
+ const label = ` Step ${stepNumber}: ${stepId} `;
10258
+ const width = getBoxWidth();
10259
+ const remaining = Math.max(0, width - label.length - 2);
10260
+ const border = line.repeat(remaining);
10261
+ process.stdout.write(`
10262
+ `);
10263
+ process.stdout.write(isTTY ? source_default.cyan.bold(`${corner.tl}${line}${label}${border}${corner.tr}
10264
+ `) : `${corner.tl}${line}${label}${border}${corner.tr}
10265
+ `);
10266
+ const metaLine = ` harness: ${harness} model: ${model ?? "(default)"}`;
10267
+ process.stdout.write(isTTY ? source_default.dim(`${metaLine}
10268
+ `) : `${metaLine}
10269
+ `);
10270
+ process.stdout.write(`
10271
+ `);
10272
+ },
10273
+ stepEnd() {
10274
+ const line = isTTY ? "─" : "-";
10275
+ const corner = { bl: isTTY ? "└" : "+", br: isTTY ? "┘" : "+" };
10276
+ const width = getBoxWidth();
10277
+ const border = line.repeat(width - 2);
10278
+ process.stdout.write(`
10279
+ `);
10280
+ process.stdout.write(isTTY ? source_default.cyan(`${corner.bl}${border}${corner.br}
10281
+ `) : `${corner.bl}${border}${corner.br}
10282
+ `);
10283
+ },
10284
+ printToolCall(toolName, toolInput) {
10285
+ const width = getBoxWidth();
10286
+ const maxInputLength = width - 10;
10287
+ const truncatedInput = truncate(toolInput, maxInputLength);
10288
+ if (isTTY) {
10289
+ process.stderr.write(source_default.cyan(` ${toolName} `) + source_default.dim(truncatedInput) + `
10290
+ `);
10291
+ } else {
10292
+ process.stderr.write(` ${toolName} ${truncatedInput}
10293
+ `);
10294
+ }
10295
+ },
10296
+ stepOutputs(values) {
10297
+ const entries = Object.entries(values);
10298
+ if (entries.length === 0) {
10299
+ return;
10300
+ }
10301
+ const width = getBoxWidth();
10302
+ const labelPrefix = " ";
10303
+ const separator = ": ";
10304
+ process.stdout.write(`
10305
+ `);
10306
+ for (const [key, value] of entries) {
10307
+ const label = `rmr:${key}`;
10308
+ const firstLineIndent = labelPrefix.length + label.length + separator.length;
10309
+ const continuationIndent = " ".repeat(firstLineIndent);
10310
+ const maxValueWidth = width - firstLineIndent;
10311
+ const valueLines = value.split(`
10312
+ `);
10313
+ const wrappedLines = [];
10314
+ for (const vline of valueLines) {
10315
+ if (vline.length <= maxValueWidth) {
10316
+ wrappedLines.push(vline);
10317
+ } else {
10318
+ let remaining = vline;
10319
+ while (remaining.length > maxValueWidth) {
10320
+ let breakAt = remaining.lastIndexOf(" ", maxValueWidth);
10321
+ if (breakAt <= 0) {
10322
+ breakAt = maxValueWidth;
10323
+ }
10324
+ wrappedLines.push(remaining.slice(0, breakAt));
10325
+ remaining = remaining.slice(breakAt).trimStart();
10326
+ }
10327
+ if (remaining) {
10328
+ wrappedLines.push(remaining);
10329
+ }
9995
10330
  }
9996
10331
  }
9997
- if (event?.type === "content_block_delta") {
9998
- const delta = event.delta;
9999
- if (delta?.type === "text_delta" && typeof delta.text === "string") {
10000
- return { text: delta.text, sessionId };
10001
- }
10002
- if (delta?.type === "input_json_delta" && typeof delta.partial_json === "string") {
10003
- currentToolInput += delta.partial_json;
10004
- return sessionId ? { text: "", sessionId } : null;
10005
- }
10006
- return sessionId ? { text: "", sessionId } : null;
10332
+ const firstLine = wrappedLines[0] ?? "";
10333
+ if (isTTY) {
10334
+ process.stdout.write(source_default.cyan(`${labelPrefix}${label}`) + source_default.dim(`${separator}${firstLine}`) + `
10335
+ `);
10336
+ } else {
10337
+ process.stdout.write(`${labelPrefix}${label}${separator}${firstLine}
10338
+ `);
10007
10339
  }
10008
- if (event?.type === "content_block_stop") {
10009
- if (currentToolName && index === currentBlockIndex) {
10010
- const result = {
10011
- text: "",
10012
- sessionId,
10013
- toolName: currentToolName,
10014
- toolInput: currentToolInput || undefined
10015
- };
10016
- currentToolName = null;
10017
- currentToolInput = "";
10018
- currentBlockIndex = null;
10019
- return result;
10340
+ for (let i = 1;i < wrappedLines.length; i++) {
10341
+ if (isTTY) {
10342
+ process.stdout.write(source_default.dim(`${continuationIndent}${wrappedLines[i]}`) + `
10343
+ `);
10344
+ } else {
10345
+ process.stdout.write(`${continuationIndent}${wrappedLines[i]}
10346
+ `);
10020
10347
  }
10021
10348
  }
10022
- return sessionId ? { text: "", sessionId } : null;
10023
- }
10024
- if (type === "result") {
10025
- return sessionId ? { text: "", sessionId } : null;
10026
- }
10027
- if (type === "system") {
10028
- return sessionId ? { text: "", sessionId } : null;
10029
- }
10030
- return null;
10031
- };
10032
- }
10033
- var adapters = {
10034
- claude: {
10035
- name: "claude",
10036
- buildRunCommand(prompt, options) {
10037
- const allowArgs = options.allowAll ? ["--dangerously-skip-permissions"] : [];
10038
- const base = ["-p", prompt, ...claudeStreamFlags()];
10039
- return { binary: "claude", args: withModelArgs(options.model, [...allowArgs, ...base]) };
10040
- },
10041
- buildResumeCommand(sessionId, prompt, options) {
10042
- const allowArgs = options.allowAll ? ["--dangerously-skip-permissions"] : [];
10043
- const base = [...allowArgs, "--resume", sessionId, "-p", prompt, ...claudeStreamFlags()];
10044
- return { binary: "claude", args: withModelArgs(options.model, base) };
10045
- },
10046
- createStreamParser: createClaudeStreamParser,
10047
- resumeTemplate(sessionId) {
10048
- return `claude --resume ${sessionId}`;
10049
10349
  }
10050
10350
  },
10051
- opencode: {
10052
- name: "opencode",
10053
- buildRunCommand(prompt, options) {
10054
- const args = ["run", prompt];
10055
- return { binary: "opencode", args: withModelArgs(options.model, args) };
10056
- },
10057
- buildResumeCommand(sessionId, prompt, options) {
10058
- const args = ["--resume", sessionId, "run", prompt];
10059
- return { binary: "opencode", args: withModelArgs(options.model, args) };
10060
- },
10061
- createStreamParser: createPassthroughParser,
10062
- resumeTemplate(sessionId) {
10063
- return `opencode --resume ${sessionId}`;
10064
- }
10351
+ content(text) {
10352
+ process.stdout.write(text);
10065
10353
  },
10066
- codex: {
10067
- name: "codex",
10068
- buildRunCommand(prompt, options) {
10069
- const auto = options.allowAll ? ["--full-auto"] : [];
10070
- const args = [...auto, "exec", prompt];
10071
- return { binary: "codex", args: withModelArgs(options.model, args) };
10072
- },
10073
- buildResumeCommand(sessionId, prompt, options) {
10074
- const args = ["exec", "resume", sessionId, prompt];
10075
- return { binary: "codex", args: withModelArgs(options.model, args) };
10076
- },
10077
- createStreamParser: createPassthroughParser,
10078
- resumeTemplate(sessionId) {
10079
- return `codex exec resume ${sessionId} "<prompt>"`;
10080
- }
10354
+ success(text) {
10355
+ const icon = isTTY ? "✓ " : "";
10356
+ process.stdout.write(isTTY ? source_default.green(`${icon}${text}
10357
+ `) : `${icon}${text}
10358
+ `);
10081
10359
  },
10082
- copilot: {
10083
- name: "copilot",
10084
- buildRunCommand(prompt, options) {
10085
- const auto = options.allowAll ? ["--allow-all", "--no-ask-user"] : [];
10086
- const args = [...auto, "-p", prompt];
10087
- return { binary: "copilot", args: withModelArgs(options.model, args) };
10088
- },
10089
- buildResumeCommand(_sessionId, prompt, options) {
10090
- const args = ["-p", prompt];
10091
- return { binary: "copilot", args: withModelArgs(options.model, args) };
10092
- },
10093
- createStreamParser: createPassthroughParser,
10094
- resumeTemplate(sessionId) {
10095
- return `copilot --resume ${sessionId}`;
10096
- }
10097
- }
10098
- };
10099
- function getHarnessAdapter(name) {
10100
- const adapter = adapters[name];
10101
- if (!adapter) {
10102
- throw new ValidationError(`Unknown harness "${name}".`);
10103
- }
10104
- return adapter;
10105
- }
10106
-
10107
- // node_modules/chalk/source/vendor/ansi-styles/index.js
10108
- var ANSI_BACKGROUND_OFFSET = 10;
10109
- var wrapAnsi16 = (offset = 0) => (code) => `\x1B[${code + offset}m`;
10110
- var wrapAnsi256 = (offset = 0) => (code) => `\x1B[${38 + offset};5;${code}m`;
10111
- var wrapAnsi16m = (offset = 0) => (red, green, blue) => `\x1B[${38 + offset};2;${red};${green};${blue}m`;
10112
- var styles = {
10113
- modifier: {
10114
- reset: [0, 0],
10115
- bold: [1, 22],
10116
- dim: [2, 22],
10117
- italic: [3, 23],
10118
- underline: [4, 24],
10119
- overline: [53, 55],
10120
- inverse: [7, 27],
10121
- hidden: [8, 28],
10122
- strikethrough: [9, 29]
10360
+ warning(text) {
10361
+ const icon = isTTY ? "⚠ " : "";
10362
+ process.stderr.write(isTTY ? source_default.yellow(`${icon}${text}
10363
+ `) : `${icon}${text}
10364
+ `);
10123
10365
  },
10124
- color: {
10125
- black: [30, 39],
10126
- red: [31, 39],
10127
- green: [32, 39],
10128
- yellow: [33, 39],
10129
- blue: [34, 39],
10130
- magenta: [35, 39],
10131
- cyan: [36, 39],
10132
- white: [37, 39],
10133
- blackBright: [90, 39],
10134
- gray: [90, 39],
10135
- grey: [90, 39],
10136
- redBright: [91, 39],
10137
- greenBright: [92, 39],
10138
- yellowBright: [93, 39],
10139
- blueBright: [94, 39],
10140
- magentaBright: [95, 39],
10141
- cyanBright: [96, 39],
10142
- whiteBright: [97, 39]
10366
+ error(text) {
10367
+ const icon = isTTY ? "✗ " : "";
10368
+ process.stderr.write(isTTY ? source_default.red(`${icon}${text}
10369
+ `) : `${icon}${text}
10370
+ `);
10143
10371
  },
10144
- bgColor: {
10145
- bgBlack: [40, 49],
10146
- bgRed: [41, 49],
10147
- bgGreen: [42, 49],
10148
- bgYellow: [43, 49],
10149
- bgBlue: [44, 49],
10150
- bgMagenta: [45, 49],
10151
- bgCyan: [46, 49],
10152
- bgWhite: [47, 49],
10153
- bgBlackBright: [100, 49],
10154
- bgGray: [100, 49],
10155
- bgGrey: [100, 49],
10156
- bgRedBright: [101, 49],
10157
- bgGreenBright: [102, 49],
10158
- bgYellowBright: [103, 49],
10159
- bgBlueBright: [104, 49],
10160
- bgMagentaBright: [105, 49],
10161
- bgCyanBright: [106, 49],
10162
- bgWhiteBright: [107, 49]
10163
- }
10164
- };
10165
- var modifierNames = Object.keys(styles.modifier);
10166
- var foregroundColorNames = Object.keys(styles.color);
10167
- var backgroundColorNames = Object.keys(styles.bgColor);
10168
- var colorNames = [...foregroundColorNames, ...backgroundColorNames];
10169
- function assembleStyles() {
10170
- const codes = new Map;
10171
- for (const [groupName, group] of Object.entries(styles)) {
10172
- for (const [styleName, style] of Object.entries(group)) {
10173
- styles[styleName] = {
10174
- open: `\x1B[${style[0]}m`,
10175
- close: `\x1B[${style[1]}m`
10176
- };
10177
- group[styleName] = styles[styleName];
10178
- codes.set(style[0], style[1]);
10179
- }
10180
- Object.defineProperty(styles, groupName, {
10181
- value: group,
10182
- enumerable: false
10372
+ info(text) {
10373
+ process.stdout.write(`${text}
10374
+ `);
10375
+ },
10376
+ dim(text) {
10377
+ process.stdout.write(isTTY ? source_default.gray(text) : text);
10378
+ },
10379
+ pauseInstructions(info) {
10380
+ process.stderr.write(`
10381
+ `);
10382
+ ui.warning(`Paused: ${info.reason}`);
10383
+ process.stderr.write(`
10384
+ `);
10385
+ process.stdout.write(isTTY ? source_default.dim(`Resume workflow:
10386
+ `) : `Resume workflow:
10387
+ `);
10388
+ process.stdout.write(` ${binaryName} continue ${info.runId}
10389
+ `);
10390
+ process.stdout.write(`
10391
+ `);
10392
+ process.stdout.write(isTTY ? source_default.dim(`Resume with a hint:
10393
+ `) : `Resume with a hint:
10394
+ `);
10395
+ process.stdout.write(` ${binaryName} continue ${info.runId} --hint "your guidance here"
10396
+ `);
10397
+ process.stdout.write(`
10398
+ `);
10399
+ process.stdout.write(isTTY ? source_default.dim(`Resume agent session directly:
10400
+ `) : `Resume agent session directly:
10401
+ `);
10402
+ process.stdout.write(` ${info.resumeCommand}
10403
+ `);
10404
+ process.stdout.write(`
10405
+ `);
10406
+ },
10407
+ prompt(message) {
10408
+ return new Promise((resolve) => {
10409
+ const rl = readline.createInterface({
10410
+ input: process.stdin,
10411
+ output: process.stdout
10412
+ });
10413
+ const styledMessage = isTTY ? source_default.cyan(message) : message;
10414
+ rl.question(styledMessage, (answer) => {
10415
+ rl.close();
10416
+ resolve(answer);
10417
+ });
10183
10418
  });
10184
- }
10185
- Object.defineProperty(styles, "codes", {
10186
- value: codes,
10187
- enumerable: false
10188
- });
10189
- styles.color.close = "\x1B[39m";
10190
- styles.bgColor.close = "\x1B[49m";
10191
- styles.color.ansi = wrapAnsi16();
10192
- styles.color.ansi256 = wrapAnsi256();
10193
- styles.color.ansi16m = wrapAnsi16m();
10194
- styles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET);
10195
- styles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET);
10196
- styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET);
10197
- Object.defineProperties(styles, {
10198
- rgbToAnsi256: {
10199
- value(red, green, blue) {
10200
- if (red === green && green === blue) {
10201
- if (red < 8) {
10202
- return 16;
10203
- }
10204
- if (red > 248) {
10205
- return 231;
10419
+ },
10420
+ multilinePrompt(message) {
10421
+ return new Promise((resolve) => {
10422
+ const input = process.stdin;
10423
+ const output = process.stdout;
10424
+ const supportsRawMode = input.isTTY && typeof input.setRawMode === "function";
10425
+ const styledMessage = isTTY ? source_default.cyan(message) : message;
10426
+ const linePrompt = isTTY ? source_default.cyan("> ") : "> ";
10427
+ const promptWidth = displayWidth(stripAnsi(linePrompt));
10428
+ let buffer = "";
10429
+ let cursor = 0;
10430
+ let settled = false;
10431
+ let renderedRowCount = 0;
10432
+ if (!supportsRawMode) {
10433
+ const rl = readline.createInterface({
10434
+ input,
10435
+ output
10436
+ });
10437
+ rl.question(`${styledMessage} `, (answer) => {
10438
+ rl.close();
10439
+ resolve(answer);
10440
+ });
10441
+ return;
10442
+ }
10443
+ const cleanup = () => {
10444
+ input.off("data", onData);
10445
+ input.off("error", onError);
10446
+ input.setRawMode(false);
10447
+ input.pause();
10448
+ };
10449
+ const finish = (value) => {
10450
+ if (settled) {
10451
+ return;
10452
+ }
10453
+ settled = true;
10454
+ cleanup();
10455
+ resolve(value);
10456
+ };
10457
+ const clearRenderedBuffer = () => {
10458
+ if (renderedRowCount === 0) {
10459
+ return;
10460
+ }
10461
+ readline.cursorTo(output, 0);
10462
+ if (renderedRowCount > 1) {
10463
+ readline.moveCursor(output, 0, -(renderedRowCount - 1));
10464
+ }
10465
+ readline.clearScreenDown(output);
10466
+ };
10467
+ const getRenderedRowCount = (lines) => {
10468
+ const columns = Math.max(1, getTerminalWidth());
10469
+ let rows = 0;
10470
+ for (const line of lines) {
10471
+ const lineWidth = promptWidth + displayWidth(line);
10472
+ rows += Math.max(1, Math.ceil(lineWidth / columns));
10473
+ }
10474
+ return rows;
10475
+ };
10476
+ const getCursorPosition = () => {
10477
+ const textBeforeCursor = buffer.slice(0, cursor);
10478
+ const linesBeforeCursor = textBeforeCursor.split(`
10479
+ `);
10480
+ const line = linesBeforeCursor.length - 1;
10481
+ const col = linesBeforeCursor[line]?.length ?? 0;
10482
+ return { line, col };
10483
+ };
10484
+ const getLineStart = (lineNum) => {
10485
+ const lines = buffer.split(`
10486
+ `);
10487
+ let idx = 0;
10488
+ for (let i = 0;i < lineNum && i < lines.length; i++) {
10489
+ idx += (lines[i]?.length ?? 0) + 1;
10490
+ }
10491
+ return idx;
10492
+ };
10493
+ const getLineEnd = (lineNum) => {
10494
+ const lines = buffer.split(`
10495
+ `);
10496
+ if (lineNum >= lines.length) {
10497
+ return buffer.length;
10498
+ }
10499
+ return getLineStart(lineNum) + (lines[lineNum]?.length ?? 0);
10500
+ };
10501
+ const renderBuffer = () => {
10502
+ clearRenderedBuffer();
10503
+ const lines = buffer.split(`
10504
+ `);
10505
+ for (let i = 0;i < lines.length; i++) {
10506
+ output.write(`${linePrompt}${lines[i] ?? ""}`);
10507
+ if (i < lines.length - 1) {
10508
+ output.write(`
10509
+ `);
10206
10510
  }
10207
- return Math.round((red - 8) / 247 * 24) + 232;
10208
10511
  }
10209
- return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green / 255 * 5) + Math.round(blue / 255 * 5);
10210
- },
10211
- enumerable: false
10212
- },
10213
- hexToRgb: {
10214
- value(hex) {
10215
- const matches = /[a-f\d]{6}|[a-f\d]{3}/i.exec(hex.toString(16));
10216
- if (!matches) {
10217
- return [0, 0, 0];
10512
+ renderedRowCount = getRenderedRowCount(lines);
10513
+ const { line: cursorLine, col: cursorCol } = getCursorPosition();
10514
+ const columns = Math.max(1, getTerminalWidth());
10515
+ let rowsFromEnd = 0;
10516
+ for (let i = lines.length - 1;i > cursorLine; i--) {
10517
+ const lineWidth = promptWidth + displayWidth(lines[i] ?? "");
10518
+ rowsFromEnd += Math.max(1, Math.ceil(lineWidth / columns));
10519
+ }
10520
+ const cursorLineWidth = promptWidth + cursorCol;
10521
+ const totalLineWidth = promptWidth + displayWidth(lines[cursorLine] ?? "");
10522
+ const totalRowsInCursorLine = Math.max(1, Math.ceil(totalLineWidth / columns));
10523
+ const cursorRowInLine = Math.floor(cursorLineWidth / columns);
10524
+ const rowsAfterCursorInLine = totalRowsInCursorLine - cursorRowInLine - 1;
10525
+ rowsFromEnd += rowsAfterCursorInLine;
10526
+ if (rowsFromEnd > 0) {
10527
+ readline.moveCursor(output, 0, -rowsFromEnd);
10528
+ }
10529
+ readline.cursorTo(output, cursorLineWidth % columns);
10530
+ };
10531
+ const insertText = (text) => {
10532
+ const normalized = text.replace(/\r\n/g, `
10533
+ `).replace(/\r/g, `
10534
+ `);
10535
+ if (!normalized) {
10536
+ return;
10218
10537
  }
10219
- let [colorString] = matches;
10220
- if (colorString.length === 3) {
10221
- colorString = [...colorString].map((character) => character + character).join("");
10538
+ buffer = buffer.slice(0, cursor) + normalized + buffer.slice(cursor);
10539
+ cursor += normalized.length;
10540
+ renderBuffer();
10541
+ };
10542
+ const submit = () => {
10543
+ const lines = buffer.split(`
10544
+ `);
10545
+ const lastLineIdx = lines.length - 1;
10546
+ const columns = Math.max(1, getTerminalWidth());
10547
+ const { line: cursorLine } = getCursorPosition();
10548
+ let rowsToEnd = 0;
10549
+ for (let i = cursorLine + 1;i < lines.length; i++) {
10550
+ const lineWidth = promptWidth + displayWidth(lines[i] ?? "");
10551
+ rowsToEnd += Math.max(1, Math.ceil(lineWidth / columns));
10552
+ }
10553
+ const cursorLineWidth = promptWidth + displayWidth(lines[cursorLine] ?? "");
10554
+ const totalRowsInCursorLine = Math.max(1, Math.ceil(cursorLineWidth / columns));
10555
+ const { col: cursorCol } = getCursorPosition();
10556
+ const cursorRowInLine = Math.floor((promptWidth + cursorCol) / columns);
10557
+ rowsToEnd += totalRowsInCursorLine - cursorRowInLine - 1;
10558
+ if (rowsToEnd > 0) {
10559
+ readline.moveCursor(output, 0, rowsToEnd);
10560
+ }
10561
+ const lastLineWidth = promptWidth + displayWidth(lines[lastLineIdx] ?? "");
10562
+ readline.cursorTo(output, lastLineWidth % columns);
10563
+ output.write(`
10564
+ `);
10565
+ finish(buffer);
10566
+ };
10567
+ const cancel = () => {
10568
+ output.write(`
10569
+ `);
10570
+ finish("");
10571
+ };
10572
+ const onError = () => {
10573
+ finish(buffer);
10574
+ };
10575
+ const onData = (chunk) => {
10576
+ const value = typeof chunk === "string" ? chunk : chunk.toString("utf8");
10577
+ if (value === "\x03") {
10578
+ cancel();
10579
+ return;
10222
10580
  }
10223
- const integer = Number.parseInt(colorString, 16);
10224
- return [
10225
- integer >> 16 & 255,
10226
- integer >> 8 & 255,
10227
- integer & 255
10228
- ];
10229
- },
10230
- enumerable: false
10231
- },
10232
- hexToAnsi256: {
10233
- value: (hex) => styles.rgbToAnsi256(...styles.hexToRgb(hex)),
10234
- enumerable: false
10235
- },
10236
- ansi256ToAnsi: {
10237
- value(code) {
10238
- if (code < 8) {
10239
- return 30 + code;
10581
+ if (value === "\x04") {
10582
+ submit();
10583
+ return;
10240
10584
  }
10241
- if (code < 16) {
10242
- return 90 + (code - 8);
10585
+ if (value === "\x01") {
10586
+ const { line } = getCursorPosition();
10587
+ cursor = getLineStart(line);
10588
+ renderBuffer();
10589
+ return;
10243
10590
  }
10244
- let red;
10245
- let green;
10246
- let blue;
10247
- if (code >= 232) {
10248
- red = ((code - 232) * 10 + 8) / 255;
10249
- green = red;
10250
- blue = red;
10251
- } else {
10252
- code -= 16;
10253
- const remainder = code % 36;
10254
- red = Math.floor(code / 36) / 5;
10255
- green = Math.floor(remainder / 6) / 5;
10256
- blue = remainder % 6 / 5;
10591
+ if (value === "\x05") {
10592
+ const { line } = getCursorPosition();
10593
+ cursor = getLineEnd(line);
10594
+ renderBuffer();
10595
+ return;
10257
10596
  }
10258
- const value = Math.max(red, green, blue) * 2;
10259
- if (value === 0) {
10260
- return 30;
10597
+ if (value === "\x15") {
10598
+ const { line } = getCursorPosition();
10599
+ const lineStart = getLineStart(line);
10600
+ buffer = buffer.slice(0, lineStart) + buffer.slice(cursor);
10601
+ cursor = lineStart;
10602
+ renderBuffer();
10603
+ return;
10261
10604
  }
10262
- let result = 30 + (Math.round(blue) << 2 | Math.round(green) << 1 | Math.round(red));
10263
- if (value === 2) {
10264
- result += 60;
10605
+ if (value === "\x17") {
10606
+ if (cursor === 0) {
10607
+ return;
10608
+ }
10609
+ let newCursor = cursor - 1;
10610
+ while (newCursor > 0 && /\s/.test(buffer[newCursor] ?? "")) {
10611
+ newCursor--;
10612
+ }
10613
+ while (newCursor > 0 && !/\s/.test(buffer[newCursor - 1] ?? "")) {
10614
+ newCursor--;
10615
+ }
10616
+ buffer = buffer.slice(0, newCursor) + buffer.slice(cursor);
10617
+ cursor = newCursor;
10618
+ renderBuffer();
10619
+ return;
10265
10620
  }
10266
- return result;
10267
- },
10268
- enumerable: false
10269
- },
10270
- rgbToAnsi: {
10271
- value: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)),
10272
- enumerable: false
10273
- },
10274
- hexToAnsi: {
10275
- value: (hex) => styles.ansi256ToAnsi(styles.hexToAnsi256(hex)),
10276
- enumerable: false
10277
- }
10278
- });
10279
- return styles;
10280
- }
10281
- var ansiStyles = assembleStyles();
10282
- var ansi_styles_default = ansiStyles;
10283
-
10284
- // node_modules/chalk/source/vendor/supports-color/index.js
10285
- import process2 from "node:process";
10286
- import os from "node:os";
10287
- import tty2 from "node:tty";
10288
- function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process2.argv) {
10289
- const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
10290
- const position = argv.indexOf(prefix + flag);
10291
- const terminatorPosition = argv.indexOf("--");
10292
- return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);
10293
- }
10294
- var { env } = process2;
10295
- var flagForceColor;
10296
- if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) {
10297
- flagForceColor = 0;
10298
- } else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) {
10299
- flagForceColor = 1;
10300
- }
10301
- function envForceColor() {
10302
- if ("FORCE_COLOR" in env) {
10303
- if (env.FORCE_COLOR === "true") {
10304
- return 1;
10305
- }
10306
- if (env.FORCE_COLOR === "false") {
10307
- return 0;
10308
- }
10309
- return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3);
10310
- }
10311
- }
10312
- function translateLevel(level) {
10313
- if (level === 0) {
10314
- return false;
10315
- }
10316
- return {
10317
- level,
10318
- hasBasic: true,
10319
- has256: level >= 2,
10320
- has16m: level >= 3
10321
- };
10322
- }
10323
- function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
10324
- const noFlagForceColor = envForceColor();
10325
- if (noFlagForceColor !== undefined) {
10326
- flagForceColor = noFlagForceColor;
10327
- }
10328
- const forceColor = sniffFlags ? flagForceColor : noFlagForceColor;
10329
- if (forceColor === 0) {
10330
- return 0;
10331
- }
10332
- if (sniffFlags) {
10333
- if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) {
10334
- return 3;
10335
- }
10336
- if (hasFlag("color=256")) {
10337
- return 2;
10338
- }
10339
- }
10340
- if ("TF_BUILD" in env && "AGENT_NAME" in env) {
10341
- return 1;
10342
- }
10343
- if (haveStream && !streamIsTTY && forceColor === undefined) {
10344
- return 0;
10345
- }
10346
- const min = forceColor || 0;
10347
- if (env.TERM === "dumb") {
10348
- return min;
10349
- }
10350
- if (process2.platform === "win32") {
10351
- const osRelease = os.release().split(".");
10352
- if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
10353
- return Number(osRelease[2]) >= 14931 ? 3 : 2;
10354
- }
10355
- return 1;
10621
+ if (value === "\r" || value === `
10622
+ `) {
10623
+ insertText(`
10624
+ `);
10625
+ return;
10626
+ }
10627
+ if (value === "" || value === "\b") {
10628
+ if (cursor > 0) {
10629
+ buffer = buffer.slice(0, cursor - 1) + buffer.slice(cursor);
10630
+ cursor--;
10631
+ renderBuffer();
10632
+ }
10633
+ return;
10634
+ }
10635
+ if (value.length > 1) {
10636
+ const withoutBracketedPasteMarkers = value.replace(/\x1b\[200~/g, "").replace(/\x1b\[201~/g, "");
10637
+ if (withoutBracketedPasteMarkers.startsWith("\x1B")) {
10638
+ const seq = withoutBracketedPasteMarkers;
10639
+ if (seq === "\x1B[D") {
10640
+ if (cursor > 0) {
10641
+ cursor--;
10642
+ renderBuffer();
10643
+ }
10644
+ return;
10645
+ }
10646
+ if (seq === "\x1B[C") {
10647
+ if (cursor < buffer.length) {
10648
+ cursor++;
10649
+ renderBuffer();
10650
+ }
10651
+ return;
10652
+ }
10653
+ if (seq === "\x1B[A") {
10654
+ const { line, col } = getCursorPosition();
10655
+ if (line > 0) {
10656
+ const prevLineStart = getLineStart(line - 1);
10657
+ const prevLineEnd = getLineEnd(line - 1);
10658
+ const prevLineLen = prevLineEnd - prevLineStart;
10659
+ cursor = prevLineStart + Math.min(col, prevLineLen);
10660
+ renderBuffer();
10661
+ }
10662
+ return;
10663
+ }
10664
+ if (seq === "\x1B[B") {
10665
+ const lines = buffer.split(`
10666
+ `);
10667
+ const { line, col } = getCursorPosition();
10668
+ if (line < lines.length - 1) {
10669
+ const nextLineStart = getLineStart(line + 1);
10670
+ const nextLineEnd = getLineEnd(line + 1);
10671
+ const nextLineLen = nextLineEnd - nextLineStart;
10672
+ cursor = nextLineStart + Math.min(col, nextLineLen);
10673
+ renderBuffer();
10674
+ }
10675
+ return;
10676
+ }
10677
+ if (seq === "\x1B[H" || seq === "\x1B[1~" || seq === "\x1BOH") {
10678
+ const { line } = getCursorPosition();
10679
+ cursor = getLineStart(line);
10680
+ renderBuffer();
10681
+ return;
10682
+ }
10683
+ if (seq === "\x1B[F" || seq === "\x1B[4~" || seq === "\x1BOF") {
10684
+ const { line } = getCursorPosition();
10685
+ cursor = getLineEnd(line);
10686
+ renderBuffer();
10687
+ return;
10688
+ }
10689
+ if (!/[\r\n]/.test(withoutBracketedPasteMarkers)) {
10690
+ return;
10691
+ }
10692
+ }
10693
+ insertText(withoutBracketedPasteMarkers);
10694
+ return;
10695
+ }
10696
+ if (value >= " ") {
10697
+ insertText(value);
10698
+ }
10699
+ };
10700
+ output.write(styledMessage);
10701
+ output.write(`
10702
+ `);
10703
+ input.setRawMode(true);
10704
+ input.setEncoding("utf8");
10705
+ input.resume();
10706
+ input.on("data", onData);
10707
+ input.on("error", onError);
10708
+ renderBuffer();
10709
+ });
10356
10710
  }
10357
- if ("CI" in env) {
10358
- if (["GITHUB_ACTIONS", "GITEA_ACTIONS", "CIRCLECI"].some((key) => (key in env))) {
10359
- return 3;
10360
- }
10361
- if (["TRAVIS", "APPVEYOR", "GITLAB_CI", "BUILDKITE", "DRONE"].some((sign) => (sign in env)) || env.CI_NAME === "codeship") {
10362
- return 1;
10711
+ };
10712
+
10713
+ // src/commands/base.ts
10714
+ class BaseCommand extends Command {
10715
+ async catch(error) {
10716
+ if (error instanceof RmrError) {
10717
+ ui.error(error.message);
10718
+ process.exitCode = 1;
10719
+ return;
10363
10720
  }
10364
- return min;
10365
- }
10366
- if ("TEAMCITY_VERSION" in env) {
10367
- return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;
10368
- }
10369
- if (env.COLORTERM === "truecolor") {
10370
- return 3;
10371
- }
10372
- if (env.TERM === "xterm-kitty") {
10373
- return 3;
10374
- }
10375
- if (env.TERM === "xterm-ghostty") {
10376
- return 3;
10721
+ throw error;
10377
10722
  }
10378
- if (env.TERM === "wezterm") {
10379
- return 3;
10723
+ }
10724
+
10725
+ // src/lib/config.ts
10726
+ import { mkdir } from "node:fs/promises";
10727
+ import { resolve } from "node:path";
10728
+ async function loadConfig(workspaceRoot = process.cwd()) {
10729
+ const root = resolve(workspaceRoot);
10730
+ const rexDir = resolve(root, ".rmr");
10731
+ const config = {
10732
+ workspaceRoot: root,
10733
+ rexDir,
10734
+ runsDir: resolve(rexDir, "runs"),
10735
+ workflowsDir: resolve(rexDir, "workflows")
10736
+ };
10737
+ await Promise.all([
10738
+ mkdir(config.rexDir, { recursive: true }),
10739
+ mkdir(config.runsDir, { recursive: true }),
10740
+ mkdir(config.workflowsDir, { recursive: true })
10741
+ ]);
10742
+ return config;
10743
+ }
10744
+
10745
+ // src/lib/completions.ts
10746
+ import { access, readdir } from "node:fs/promises";
10747
+ import { resolve as resolve2 } from "node:path";
10748
+ function matchesPartial(value, partial) {
10749
+ if (!partial) {
10750
+ return true;
10380
10751
  }
10381
- if ("TERM_PROGRAM" in env) {
10382
- const version = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
10383
- switch (env.TERM_PROGRAM) {
10384
- case "iTerm.app": {
10385
- return version >= 3 ? 3 : 2;
10386
- }
10387
- case "Apple_Terminal": {
10388
- return 2;
10389
- }
10752
+ return value.startsWith(partial);
10753
+ }
10754
+ async function listRunIdCompletions(config, partial = "") {
10755
+ const entries = await readdir(config.runsDir, { withFileTypes: true });
10756
+ return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -".json".length)).filter((id) => matchesPartial(id, partial)).sort();
10757
+ }
10758
+ async function listWorkflowCompletions(config, partial = "") {
10759
+ const entries = await readdir(config.workflowsDir, { withFileTypes: true });
10760
+ const workflows = [];
10761
+ for (const entry of entries) {
10762
+ if (!entry.isDirectory()) {
10763
+ continue;
10390
10764
  }
10765
+ const workflowYaml = resolve2(config.workflowsDir, entry.name, "workflow.yaml");
10766
+ const workflowYml = resolve2(config.workflowsDir, entry.name, "workflow.yml");
10767
+ try {
10768
+ await access(workflowYaml);
10769
+ workflows.push(workflowYaml);
10770
+ continue;
10771
+ } catch {}
10772
+ try {
10773
+ await access(workflowYml);
10774
+ workflows.push(workflowYml);
10775
+ } catch {}
10391
10776
  }
10392
- if (/-256(color)?$/i.test(env.TERM)) {
10393
- return 2;
10394
- }
10395
- if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {
10396
- return 1;
10397
- }
10398
- if ("COLORTERM" in env) {
10399
- return 1;
10777
+ return workflows.filter((filePath) => matchesPartial(filePath, partial)).sort();
10778
+ }
10779
+
10780
+ // src/commands/complete.ts
10781
+ function parseTarget(value) {
10782
+ if (value === "run-id" || value === "workflow") {
10783
+ return value;
10400
10784
  }
10401
- return min;
10785
+ throw new UserInputError(`Invalid completion target "${value}".`);
10402
10786
  }
10403
- function createSupportsColor(stream, options = {}) {
10404
- const level = _supportsColor(stream, {
10405
- streamIsTTY: stream && stream.isTTY,
10406
- ...options
10787
+
10788
+ class CompleteCommand extends BaseCommand {
10789
+ static paths = [["complete"]];
10790
+ target = exports_options.String({
10791
+ name: "target"
10407
10792
  });
10408
- return translateLevel(level);
10793
+ partial = exports_options.String({
10794
+ required: false,
10795
+ name: "partial"
10796
+ });
10797
+ async execute() {
10798
+ const config = await loadConfig();
10799
+ const target = parseTarget(this.target);
10800
+ const query = this.partial ?? "";
10801
+ const suggestions = target === "run-id" ? await listRunIdCompletions(config, query) : await listWorkflowCompletions(config, query);
10802
+ for (const value of suggestions) {
10803
+ process.stdout.write(`${value}
10804
+ `);
10805
+ }
10806
+ return 0;
10807
+ }
10409
10808
  }
10410
- var supportsColor = {
10411
- stdout: createSupportsColor({ isTTY: tty2.isatty(1) }),
10412
- stderr: createSupportsColor({ isTTY: tty2.isatty(2) })
10413
- };
10414
- var supports_color_default = supportsColor;
10415
10809
 
10416
- // node_modules/chalk/source/utilities.js
10417
- function stringReplaceAll(string, substring, replacer) {
10418
- let index = string.indexOf(substring);
10419
- if (index === -1) {
10420
- return string;
10810
+ // src/commands/completion.ts
10811
+ function parseShell(value) {
10812
+ if (value === "bash" || value === "zsh" || value === "fish") {
10813
+ return value;
10421
10814
  }
10422
- const substringLength = substring.length;
10423
- let endIndex = 0;
10424
- let returnValue = "";
10425
- do {
10426
- returnValue += string.slice(endIndex, index) + substring + replacer;
10427
- endIndex = index + substringLength;
10428
- index = string.indexOf(substring, endIndex);
10429
- } while (index !== -1);
10430
- returnValue += string.slice(endIndex);
10431
- return returnValue;
10815
+ throw new UserInputError(`Unsupported shell "${value}". Use bash, zsh, or fish.`);
10432
10816
  }
10433
- function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) {
10434
- let endIndex = 0;
10435
- let returnValue = "";
10436
- do {
10437
- const gotCR = string[index - 1] === "\r";
10438
- returnValue += string.slice(endIndex, gotCR ? index - 1 : index) + prefix + (gotCR ? `\r
10439
- ` : `
10440
- `) + postfix;
10441
- endIndex = index + 1;
10442
- index = string.indexOf(`
10443
- `, endIndex);
10444
- } while (index !== -1);
10445
- returnValue += string.slice(endIndex);
10446
- return returnValue;
10817
+ function bashScript() {
10818
+ return [
10819
+ "_rex_complete() {",
10820
+ " local cur prev",
10821
+ " COMPREPLY=()",
10822
+ ' cur="${COMP_WORDS[COMP_CWORD]}"',
10823
+ ' prev="${COMP_WORDS[COMP_CWORD-1]}"',
10824
+ "",
10825
+ " if [[ ${COMP_CWORD} -eq 1 ]]; then",
10826
+ ' COMPREPLY=( $(compgen -W "install run continue complete completion --help --version" -- "${cur}") )',
10827
+ " return 0",
10828
+ " fi",
10829
+ "",
10830
+ ' if [[ "${prev}" == "continue" ]]; then',
10831
+ ` COMPREPLY=( $(${binaryName} complete run-id "\${cur}") )`,
10832
+ " return 0",
10833
+ " fi",
10834
+ "",
10835
+ ' if [[ "${prev}" == "run" ]]; then',
10836
+ ` COMPREPLY=( $(${binaryName} complete workflow "\${cur}") )`,
10837
+ " return 0",
10838
+ " fi",
10839
+ "",
10840
+ ' if [[ "${prev}" == "install" ]]; then',
10841
+ ' COMPREPLY=( $(compgen -W "feature-dev" -- "${cur}") )',
10842
+ " return 0",
10843
+ " fi",
10844
+ "}",
10845
+ `complete -F _rex_complete ${binaryName}`
10846
+ ].join(`
10847
+ `);
10848
+ }
10849
+ function zshScript() {
10850
+ return [
10851
+ `#compdef ${binaryName}`,
10852
+ "_rex_complete() {",
10853
+ " local -a subcommands",
10854
+ " subcommands=(install run continue complete completion)",
10855
+ "",
10856
+ " if (( CURRENT == 2 )); then",
10857
+ " _describe 'command' subcommands",
10858
+ " return",
10859
+ " fi",
10860
+ "",
10861
+ " if [[ ${words[2]} == continue && $CURRENT -eq 3 ]]; then",
10862
+ ` compadd -- $(${binaryName} complete run-id "\${words[CURRENT]}")`,
10863
+ " return",
10864
+ " fi",
10865
+ "",
10866
+ " if [[ ${words[2]} == run && $CURRENT -eq 3 ]]; then",
10867
+ ` compadd -- $(${binaryName} complete workflow "\${words[CURRENT]}")`,
10868
+ " return",
10869
+ " fi",
10870
+ "",
10871
+ " if [[ ${words[2]} == install && $CURRENT -eq 3 ]]; then",
10872
+ " compadd -- feature-dev",
10873
+ " return",
10874
+ " fi",
10875
+ "}",
10876
+ `compdef _rex_complete ${binaryName}`
10877
+ ].join(`
10878
+ `);
10879
+ }
10880
+ function fishScript() {
10881
+ return [
10882
+ "function __rex_complete_run_id",
10883
+ ` ${binaryName} complete run-id (commandline -ct)`,
10884
+ "end",
10885
+ "",
10886
+ "function __rex_complete_workflow",
10887
+ ` ${binaryName} complete workflow (commandline -ct)`,
10888
+ "end",
10889
+ "",
10890
+ `complete -c ${binaryName} -f`,
10891
+ `complete -c ${binaryName} -n '__fish_use_subcommand' -a 'install run continue complete completion'`,
10892
+ `complete -c ${binaryName} -n '__fish_seen_subcommand_from continue' -a '(__rex_complete_run_id)'`,
10893
+ `complete -c ${binaryName} -n '__fish_seen_subcommand_from run' -a '(__rex_complete_workflow)'`,
10894
+ `complete -c ${binaryName} -n '__fish_seen_subcommand_from install' -a 'feature-dev'`
10895
+ ].join(`
10896
+ `);
10897
+ }
10898
+
10899
+ class CompletionCommand extends BaseCommand {
10900
+ static paths = [["completion"]];
10901
+ static usage = Command.Usage({
10902
+ category: "Workflow",
10903
+ description: "Print optional shell completion setup script.",
10904
+ details: "Generates completion script text for your shell. Source the output in your shell profile to enable command and dynamic argument completion.",
10905
+ examples: [
10906
+ ["Show Bash completion script", "$0 completion bash"],
10907
+ ["Show Zsh completion script", "$0 completion zsh"],
10908
+ ["Show Fish completion script", "$0 completion fish"]
10909
+ ]
10910
+ });
10911
+ shell = exports_options.String({
10912
+ name: "shell"
10913
+ });
10914
+ async execute() {
10915
+ const shell = parseShell(this.shell);
10916
+ const script = shell === "bash" ? bashScript() : shell === "zsh" ? zshScript() : fishScript();
10917
+ process.stdout.write(`${script}
10918
+ `);
10919
+ return 0;
10920
+ }
10447
10921
  }
10448
10922
 
10449
- // node_modules/chalk/source/index.js
10450
- var { stdout: stdoutColor, stderr: stderrColor } = supports_color_default;
10451
- var GENERATOR = Symbol("GENERATOR");
10452
- var STYLER = Symbol("STYLER");
10453
- var IS_EMPTY = Symbol("IS_EMPTY");
10454
- var levelMapping = [
10455
- "ansi",
10456
- "ansi",
10457
- "ansi256",
10458
- "ansi16m"
10459
- ];
10460
- var styles2 = Object.create(null);
10461
- var applyOptions = (object, options = {}) => {
10462
- if (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) {
10463
- throw new Error("The `level` option should be an integer from 0 to 3");
10464
- }
10465
- const colorLevel = stdoutColor ? stdoutColor.level : 0;
10466
- object.level = options.level === undefined ? colorLevel : options.level;
10467
- };
10468
- var chalkFactory = (options) => {
10469
- const chalk = (...strings) => strings.join(" ");
10470
- applyOptions(chalk, options);
10471
- Object.setPrototypeOf(chalk, createChalk.prototype);
10472
- return chalk;
10473
- };
10474
- function createChalk(options) {
10475
- return chalkFactory(options);
10923
+ // src/lib/run-state.ts
10924
+ import { readFile, writeFile } from "node:fs/promises";
10925
+ import { resolve as resolve3 } from "node:path";
10926
+ function pad(input) {
10927
+ return String(input).padStart(2, "0");
10476
10928
  }
10477
- Object.setPrototypeOf(createChalk.prototype, Function.prototype);
10478
- for (const [styleName, style] of Object.entries(ansi_styles_default)) {
10479
- styles2[styleName] = {
10480
- get() {
10481
- const builder = createBuilder(this, createStyler(style.open, style.close, this[STYLER]), this[IS_EMPTY]);
10482
- Object.defineProperty(this, styleName, { value: builder });
10483
- return builder;
10484
- }
10485
- };
10929
+ function generateRunId(now = new Date) {
10930
+ const yyyy = now.getUTCFullYear();
10931
+ const mm = pad(now.getUTCMonth() + 1);
10932
+ const dd = pad(now.getUTCDate());
10933
+ const hh = pad(now.getUTCHours());
10934
+ const min = pad(now.getUTCMinutes());
10935
+ const sec = pad(now.getUTCSeconds());
10936
+ return `${yyyy}${mm}${dd}-${hh}${min}${sec}Z`;
10486
10937
  }
10487
- styles2.visible = {
10488
- get() {
10489
- const builder = createBuilder(this, this[STYLER], true);
10490
- Object.defineProperty(this, "visible", { value: builder });
10491
- return builder;
10492
- }
10493
- };
10494
- var getModelAnsi = (model, level, type, ...arguments_) => {
10495
- if (model === "rgb") {
10496
- if (level === "ansi16m") {
10497
- return ansi_styles_default[type].ansi16m(...arguments_);
10498
- }
10499
- if (level === "ansi256") {
10500
- return ansi_styles_default[type].ansi256(ansi_styles_default.rgbToAnsi256(...arguments_));
10501
- }
10502
- return ansi_styles_default[type].ansi(ansi_styles_default.rgbToAnsi(...arguments_));
10503
- }
10504
- if (model === "hex") {
10505
- return getModelAnsi("rgb", level, type, ...ansi_styles_default.hexToRgb(...arguments_));
10506
- }
10507
- return ansi_styles_default[type][model](...arguments_);
10508
- };
10509
- var usedModels = ["rgb", "hex", "ansi256"];
10510
- for (const model of usedModels) {
10511
- styles2[model] = {
10512
- get() {
10513
- const { level } = this;
10514
- return function(...arguments_) {
10515
- const styler = createStyler(getModelAnsi(model, levelMapping[level], "color", ...arguments_), ansi_styles_default.color.close, this[STYLER]);
10516
- return createBuilder(this, styler, this[IS_EMPTY]);
10517
- };
10518
- }
10519
- };
10520
- const bgModel = "bg" + model[0].toUpperCase() + model.slice(1);
10521
- styles2[bgModel] = {
10522
- get() {
10523
- const { level } = this;
10524
- return function(...arguments_) {
10525
- const styler = createStyler(getModelAnsi(model, levelMapping[level], "bgColor", ...arguments_), ansi_styles_default.bgColor.close, this[STYLER]);
10526
- return createBuilder(this, styler, this[IS_EMPTY]);
10527
- };
10528
- }
10529
- };
10938
+ function runFilePath(config, runId) {
10939
+ return resolve3(config.runsDir, `${runId}.json`);
10530
10940
  }
10531
- var proto = Object.defineProperties(() => {}, {
10532
- ...styles2,
10533
- level: {
10534
- enumerable: true,
10535
- get() {
10536
- return this[GENERATOR].level;
10537
- },
10538
- set(level) {
10539
- this[GENERATOR].level = level;
10540
- }
10541
- }
10542
- });
10543
- var createStyler = (open, close, parent) => {
10544
- let openAll;
10545
- let closeAll;
10546
- if (parent === undefined) {
10547
- openAll = open;
10548
- closeAll = close;
10549
- } else {
10550
- openAll = parent.openAll + open;
10551
- closeAll = close + parent.closeAll;
10941
+ function createInitialRunState(options) {
10942
+ const firstStep = options.workflow.steps[0];
10943
+ if (!firstStep) {
10944
+ throw new StorageError("Cannot create run state without at least one workflow step.");
10552
10945
  }
10553
10946
  return {
10554
- open,
10555
- close,
10556
- openAll,
10557
- closeAll,
10558
- parent
10947
+ run_id: options.runId,
10948
+ workflow_path: options.workflowPath,
10949
+ status: "running",
10950
+ current_step: firstStep.id,
10951
+ context: {
10952
+ task: options.task,
10953
+ ...options.vars
10954
+ },
10955
+ last_harness: {
10956
+ name: firstStep.harness,
10957
+ binary: firstStep.harness,
10958
+ session_id: null
10959
+ },
10960
+ step_history: [],
10961
+ updated_at: new Date().toISOString()
10559
10962
  };
10560
- };
10561
- var createBuilder = (self, _styler, _isEmpty) => {
10562
- const builder = (...arguments_) => applyStyle(builder, arguments_.length === 1 ? "" + arguments_[0] : arguments_.join(" "));
10563
- Object.setPrototypeOf(builder, proto);
10564
- builder[GENERATOR] = self;
10565
- builder[STYLER] = _styler;
10566
- builder[IS_EMPTY] = _isEmpty;
10567
- return builder;
10568
- };
10569
- var applyStyle = (self, string) => {
10570
- if (self.level <= 0 || !string) {
10571
- return self[IS_EMPTY] ? "" : string;
10572
- }
10573
- let styler = self[STYLER];
10574
- if (styler === undefined) {
10575
- return string;
10576
- }
10577
- const { openAll, closeAll } = styler;
10578
- if (string.includes("\x1B")) {
10579
- while (styler !== undefined) {
10580
- string = stringReplaceAll(string, styler.close, styler.open);
10581
- styler = styler.parent;
10963
+ }
10964
+ async function saveRunState(config, state) {
10965
+ const path = runFilePath(config, state.run_id);
10966
+ const payload = JSON.stringify({
10967
+ ...state,
10968
+ updated_at: new Date().toISOString()
10969
+ }, null, 2);
10970
+ await writeFile(path, `${payload}
10971
+ `, "utf8");
10972
+ return path;
10973
+ }
10974
+ async function loadRunState(config, runId) {
10975
+ const path = runFilePath(config, runId);
10976
+ try {
10977
+ const raw = await readFile(path, "utf8");
10978
+ const parsed = JSON.parse(raw);
10979
+ if (!parsed || typeof parsed !== "object" || parsed.run_id !== runId) {
10980
+ throw new StorageError(`Run state file is invalid for run id "${runId}".`);
10582
10981
  }
10982
+ if (!Array.isArray(parsed.step_history)) {
10983
+ parsed.step_history = [];
10984
+ }
10985
+ return parsed;
10986
+ } catch (error) {
10987
+ if (error instanceof StorageError) {
10988
+ throw error;
10989
+ }
10990
+ throw new StorageError(`Failed to load run state for "${runId}".`);
10583
10991
  }
10584
- const lfIndex = string.indexOf(`
10585
- `);
10586
- if (lfIndex !== -1) {
10587
- string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex);
10588
- }
10589
- return openAll + string + closeAll;
10590
- };
10591
- Object.defineProperties(createChalk.prototype, styles2);
10592
- var chalk = createChalk();
10593
- var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
10594
- var source_default = chalk;
10595
-
10596
- // src/lib/ui.ts
10597
- var isTTY = process.stdout.isTTY === true;
10598
- function getTerminalWidth() {
10599
- return process.stdout.columns ?? 80;
10600
10992
  }
10601
- function getBoxWidth() {
10602
- const termWidth = getTerminalWidth();
10603
- return Math.max(40, termWidth - 2);
10993
+
10994
+ // src/lib/prompt-composer.ts
10995
+ import { readFile as readFile2 } from "node:fs/promises";
10996
+ import { dirname, resolve as resolve4 } from "node:path";
10997
+ function stripFrontmatter(content) {
10998
+ const match = content.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/);
10999
+ return match ? content.slice(match[0].length) : content;
10604
11000
  }
10605
- function truncate(text, maxLength) {
10606
- if (text.length <= maxLength) {
10607
- return text;
11001
+ async function loadPromptFile(workflowPath, promptFileName) {
11002
+ const promptPath = resolve4(dirname(workflowPath), promptFileName);
11003
+ try {
11004
+ const raw = await readFile2(promptPath, "utf8");
11005
+ return stripFrontmatter(raw);
11006
+ } catch {
11007
+ throw new ConfigError(`Prompt file not found: ${promptPath}`);
10608
11008
  }
10609
- return text.slice(0, maxLength - 3) + "...";
10610
11009
  }
10611
- function wrapText(text, maxWidth) {
10612
- if (text.length <= maxWidth) {
10613
- return [text];
11010
+ function composePrompt(promptFile, promptInline) {
11011
+ const parts = [];
11012
+ if (promptFile) {
11013
+ parts.push(promptFile.trimEnd());
10614
11014
  }
10615
- const lines = [];
10616
- let remaining = text;
10617
- while (remaining.length > maxWidth) {
10618
- let breakAt = remaining.lastIndexOf(" ", maxWidth);
10619
- if (breakAt <= 0) {
10620
- breakAt = maxWidth;
10621
- }
10622
- lines.push(remaining.slice(0, breakAt));
10623
- remaining = remaining.slice(breakAt).trimStart();
11015
+ if (promptInline) {
11016
+ parts.push(promptInline.trimEnd());
10624
11017
  }
10625
- if (remaining) {
10626
- lines.push(remaining);
11018
+ return parts.join(`
11019
+
11020
+ `);
11021
+ }
11022
+
11023
+ // src/lib/harness-adapters.ts
11024
+ function createPassthroughParser() {
11025
+ return (line) => ({ text: line + `
11026
+ ` });
11027
+ }
11028
+ function withModelArgs(model, args) {
11029
+ if (!model) {
11030
+ return args;
10627
11031
  }
10628
- return lines;
11032
+ return [...args, "--model", model];
10629
11033
  }
10630
- var ui = {
10631
- get isTTY() {
10632
- return isTTY;
10633
- },
10634
- workflowHeader(info) {
10635
- const line = isTTY ? "─" : "-";
10636
- const corner = {
10637
- tl: isTTY ? "╭" : "+",
10638
- tr: isTTY ? "╮" : "+",
10639
- bl: isTTY ? "╰" : "+",
10640
- br: isTTY ? "" : "+"
10641
- };
10642
- const width = getBoxWidth();
10643
- const contentWidth = width - 4;
10644
- const border = line.repeat(width - 2);
10645
- const labelWidth = 10;
10646
- const valueWidth = contentWidth - labelWidth;
10647
- const formatLine = (label, value) => {
10648
- const paddedLabel = label ? `${label}:`.padEnd(labelWidth) : " ".repeat(labelWidth);
10649
- const truncatedValue = truncate(value, valueWidth);
10650
- return `${paddedLabel}${truncatedValue}`.padEnd(contentWidth);
10651
- };
10652
- process.stdout.write(`
10653
- `);
10654
- process.stdout.write(isTTY ? source_default.cyan(`${corner.tl}${line} ${info.title} ${border.slice(info.title.length + 3)}${corner.tr}
10655
- `) : `${corner.tl}${line} ${info.title} ${border.slice(info.title.length + 3)}${corner.tr}
10656
- `);
10657
- process.stdout.write(isTTY ? source_default.dim(`│ ${formatLine("workflow", info.workflow)} │
10658
- `) : `│ ${formatLine("workflow", info.workflow)} │
10659
- `);
10660
- process.stdout.write(isTTY ? source_default.dim(`│ ${formatLine("run-id", info.runId)} │
10661
- `) : `│ ${formatLine("run-id", info.runId)} │
10662
- `);
10663
- process.stdout.write(isTTY ? source_default.dim(`│ ${formatLine("step", info.currentStep)} │
10664
- `) : `│ ${formatLine("step", info.currentStep)} │
10665
- `);
10666
- const taskLines = wrapText(info.task, valueWidth);
10667
- for (let i = 0;i < taskLines.length; i++) {
10668
- const label = i === 0 ? "task" : "";
10669
- const content = formatLine(label, taskLines[i] ?? "");
10670
- process.stdout.write(isTTY ? source_default.dim(`│ ${content} │
10671
- `) : `│ ${content} │
10672
- `);
11034
+ function opencodePermissionEnv() {
11035
+ return {
11036
+ OPENCODE_PERMISSION: JSON.stringify({
11037
+ "*": "allow",
11038
+ external_directory: "allow",
11039
+ doom_loop: "allow"
11040
+ })
11041
+ };
11042
+ }
11043
+ function claudeStreamFlags() {
11044
+ return ["--output-format", "stream-json", "--verbose", "--include-partial-messages"];
11045
+ }
11046
+ function createOpenCodeStreamParser() {
11047
+ return (line) => {
11048
+ if (!line.trim()) {
11049
+ return null;
10673
11050
  }
10674
- process.stdout.write(isTTY ? source_default.cyan(`${corner.bl}${border}${corner.br}
10675
- `) : `${corner.bl}${border}${corner.br}
10676
- `);
10677
- process.stdout.write(`
10678
- `);
10679
- },
10680
- stepStart(stepNumber, stepId, agentId) {
10681
- const line = isTTY ? "─" : "-";
10682
- const corner = { tl: isTTY ? "┌" : "+", tr: isTTY ? "┐" : "+" };
10683
- const label = ` Step ${stepNumber}: ${stepId} (${agentId}) `;
10684
- const width = getBoxWidth();
10685
- const remaining = Math.max(0, width - label.length - 2);
10686
- const border = line.repeat(remaining);
10687
- process.stdout.write(`
10688
- `);
10689
- process.stdout.write(isTTY ? source_default.cyan.bold(`${corner.tl}${line}${label}${border}${corner.tr}
10690
- `) : `${corner.tl}${line}${label}${border}${corner.tr}
10691
- `);
10692
- process.stdout.write(`
10693
- `);
10694
- },
10695
- stepEnd() {
10696
- const line = isTTY ? "─" : "-";
10697
- const corner = { bl: isTTY ? "└" : "+", br: isTTY ? "┘" : "+" };
10698
- const width = getBoxWidth();
10699
- const border = line.repeat(width - 2);
10700
- process.stdout.write(`
10701
- `);
10702
- process.stdout.write(isTTY ? source_default.cyan(`${corner.bl}${border}${corner.br}
10703
- `) : `${corner.bl}${border}${corner.br}
10704
- `);
10705
- },
10706
- printToolCall(toolName, toolInput) {
10707
- const width = getBoxWidth();
10708
- const maxInputLength = width - 10;
10709
- const truncatedInput = truncate(toolInput, maxInputLength);
10710
- if (isTTY) {
10711
- process.stderr.write(source_default.cyan(` ${toolName} `) + source_default.dim(truncatedInput) + `
10712
- `);
10713
- } else {
10714
- process.stderr.write(` ${toolName} ${truncatedInput}
10715
- `);
11051
+ let obj;
11052
+ try {
11053
+ obj = JSON.parse(line);
11054
+ } catch {
11055
+ return null;
10716
11056
  }
10717
- },
10718
- stepOutputs(values) {
10719
- const entries = Object.entries(values);
10720
- if (entries.length === 0) {
10721
- return;
11057
+ const type = obj.type;
11058
+ const sessionId = typeof obj.sessionID === "string" ? obj.sessionID : undefined;
11059
+ if (type === "text") {
11060
+ const part = obj.part;
11061
+ const text = typeof part?.text === "string" ? part.text : "";
11062
+ return { text, sessionId };
11063
+ }
11064
+ if (type === "tool_use") {
11065
+ const part = obj.part;
11066
+ const toolName = typeof part?.tool === "string" ? part.tool : undefined;
11067
+ const state = part?.state;
11068
+ const input = state?.input;
11069
+ const toolInput = input ? JSON.stringify(input) : undefined;
11070
+ return { text: "", sessionId, toolName, toolInput };
11071
+ }
11072
+ if (sessionId) {
11073
+ return { text: "", sessionId };
10722
11074
  }
10723
- const width = getBoxWidth();
10724
- const labelPrefix = " ";
10725
- const separator = ": ";
10726
- process.stdout.write(`
10727
- `);
10728
- for (const [key, value] of entries) {
10729
- const label = `rmr:${key}`;
10730
- const firstLineIndent = labelPrefix.length + label.length + separator.length;
10731
- const continuationIndent = " ".repeat(firstLineIndent);
10732
- const maxValueWidth = width - firstLineIndent;
10733
- const valueLines = value.split(`
10734
- `);
10735
- const wrappedLines = [];
10736
- for (const vline of valueLines) {
10737
- if (vline.length <= maxValueWidth) {
10738
- wrappedLines.push(vline);
10739
- } else {
10740
- let remaining = vline;
10741
- while (remaining.length > maxValueWidth) {
10742
- let breakAt = remaining.lastIndexOf(" ", maxValueWidth);
10743
- if (breakAt <= 0) {
10744
- breakAt = maxValueWidth;
10745
- }
10746
- wrappedLines.push(remaining.slice(0, breakAt));
10747
- remaining = remaining.slice(breakAt).trimStart();
10748
- }
10749
- if (remaining) {
10750
- wrappedLines.push(remaining);
10751
- }
11075
+ return null;
11076
+ };
11077
+ }
11078
+ function createCodexStreamParser() {
11079
+ return (line) => {
11080
+ if (!line.trim()) {
11081
+ return null;
11082
+ }
11083
+ let obj;
11084
+ try {
11085
+ obj = JSON.parse(line);
11086
+ } catch {
11087
+ return null;
11088
+ }
11089
+ const type = obj.type;
11090
+ if (type === "thread.started") {
11091
+ const sessionId = typeof obj.thread_id === "string" ? obj.thread_id : undefined;
11092
+ return sessionId ? { text: "", sessionId } : null;
11093
+ }
11094
+ if (type === "item.completed") {
11095
+ const item = obj.item;
11096
+ if (!item) {
11097
+ return null;
11098
+ }
11099
+ if (item.type === "agent_message") {
11100
+ const text = typeof item.text === "string" ? item.text : "";
11101
+ return { text };
11102
+ }
11103
+ if (item.type === "command_execution") {
11104
+ const command = typeof item.command === "string" ? item.command : "shell";
11105
+ return { text: "", toolName: "shell", toolInput: command };
11106
+ }
11107
+ return null;
11108
+ }
11109
+ if (type === "item.started") {
11110
+ const item = obj.item;
11111
+ if (item?.type === "command_execution") {
11112
+ const command = typeof item.command === "string" ? item.command : "shell";
11113
+ return { text: "", toolName: "shell", toolInput: command };
11114
+ }
11115
+ return null;
11116
+ }
11117
+ return null;
11118
+ };
11119
+ }
11120
+ function createClaudeStreamParser() {
11121
+ let currentToolName = null;
11122
+ let currentToolInput = "";
11123
+ let currentBlockIndex = null;
11124
+ return (line) => {
11125
+ if (!line.trim()) {
11126
+ return null;
11127
+ }
11128
+ let obj;
11129
+ try {
11130
+ obj = JSON.parse(line);
11131
+ } catch {
11132
+ return null;
11133
+ }
11134
+ const type = obj.type;
11135
+ const sessionId = typeof obj.session_id === "string" ? obj.session_id : undefined;
11136
+ if (type === "stream_event") {
11137
+ const event = obj.event;
11138
+ const index = typeof event?.index === "number" ? event.index : null;
11139
+ if (event?.type === "content_block_start") {
11140
+ const block = event.content_block;
11141
+ if (block?.type === "tool_use" && typeof block.name === "string") {
11142
+ currentToolName = block.name;
11143
+ currentToolInput = "";
11144
+ currentBlockIndex = index;
11145
+ return sessionId ? { text: "", sessionId } : null;
10752
11146
  }
10753
11147
  }
10754
- const firstLine = wrappedLines[0] ?? "";
10755
- if (isTTY) {
10756
- process.stdout.write(source_default.cyan(`${labelPrefix}${label}`) + source_default.dim(`${separator}${firstLine}`) + `
10757
- `);
10758
- } else {
10759
- process.stdout.write(`${labelPrefix}${label}${separator}${firstLine}
10760
- `);
11148
+ if (event?.type === "content_block_delta") {
11149
+ const delta = event.delta;
11150
+ if (delta?.type === "text_delta" && typeof delta.text === "string") {
11151
+ return { text: delta.text, sessionId };
11152
+ }
11153
+ if (delta?.type === "input_json_delta" && typeof delta.partial_json === "string") {
11154
+ currentToolInput += delta.partial_json;
11155
+ return sessionId ? { text: "", sessionId } : null;
11156
+ }
11157
+ return sessionId ? { text: "", sessionId } : null;
10761
11158
  }
10762
- for (let i = 1;i < wrappedLines.length; i++) {
10763
- if (isTTY) {
10764
- process.stdout.write(source_default.dim(`${continuationIndent}${wrappedLines[i]}`) + `
10765
- `);
10766
- } else {
10767
- process.stdout.write(`${continuationIndent}${wrappedLines[i]}
10768
- `);
11159
+ if (event?.type === "content_block_stop") {
11160
+ if (currentToolName && index === currentBlockIndex) {
11161
+ const result = {
11162
+ text: "",
11163
+ sessionId,
11164
+ toolName: currentToolName,
11165
+ toolInput: currentToolInput || undefined
11166
+ };
11167
+ currentToolName = null;
11168
+ currentToolInput = "";
11169
+ currentBlockIndex = null;
11170
+ return result;
10769
11171
  }
10770
11172
  }
11173
+ return sessionId ? { text: "", sessionId } : null;
11174
+ }
11175
+ if (type === "result") {
11176
+ return sessionId ? { text: "", sessionId } : null;
11177
+ }
11178
+ if (type === "system") {
11179
+ return sessionId ? { text: "", sessionId } : null;
11180
+ }
11181
+ return null;
11182
+ };
11183
+ }
11184
+ var adapters = {
11185
+ claude: {
11186
+ name: "claude",
11187
+ buildRunCommand(prompt, options) {
11188
+ const allowArgs = options.allowAll ? ["--dangerously-skip-permissions"] : [];
11189
+ const base = ["-p", prompt, ...claudeStreamFlags()];
11190
+ return { binary: "claude", args: withModelArgs(options.model, [...allowArgs, ...base]) };
11191
+ },
11192
+ buildResumeCommand(sessionId, prompt, options) {
11193
+ const allowArgs = options.allowAll ? ["--dangerously-skip-permissions"] : [];
11194
+ const base = [...allowArgs, "--resume", sessionId, "-p", prompt, ...claudeStreamFlags()];
11195
+ return { binary: "claude", args: withModelArgs(options.model, base) };
11196
+ },
11197
+ createStreamParser: createClaudeStreamParser,
11198
+ resumeTemplate(sessionId) {
11199
+ return `claude --resume ${sessionId}`;
10771
11200
  }
10772
11201
  },
10773
- content(text) {
10774
- process.stdout.write(text);
10775
- },
10776
- success(text) {
10777
- const icon = isTTY ? "✓ " : "";
10778
- process.stdout.write(isTTY ? source_default.green(`${icon}${text}
10779
- `) : `${icon}${text}
10780
- `);
10781
- },
10782
- warning(text) {
10783
- const icon = isTTY ? "⚠ " : "";
10784
- process.stderr.write(isTTY ? source_default.yellow(`${icon}${text}
10785
- `) : `${icon}${text}
10786
- `);
10787
- },
10788
- error(text) {
10789
- const icon = isTTY ? "✗ " : "";
10790
- process.stderr.write(isTTY ? source_default.red(`${icon}${text}
10791
- `) : `${icon}${text}
10792
- `);
10793
- },
10794
- info(text) {
10795
- process.stdout.write(`${text}
10796
- `);
11202
+ opencode: {
11203
+ name: "opencode",
11204
+ buildRunCommand(prompt, options) {
11205
+ const args = ["run", "--format", "json", prompt];
11206
+ return {
11207
+ binary: "opencode",
11208
+ args: withModelArgs(options.model, args),
11209
+ ...options.allowAll ? { env: opencodePermissionEnv() } : {}
11210
+ };
11211
+ },
11212
+ buildResumeCommand(sessionId, prompt, options) {
11213
+ const args = ["run", "--format", "json", "--session", sessionId, prompt];
11214
+ return {
11215
+ binary: "opencode",
11216
+ args: withModelArgs(options.model, args),
11217
+ ...options.allowAll ? { env: opencodePermissionEnv() } : {}
11218
+ };
11219
+ },
11220
+ createStreamParser: createOpenCodeStreamParser,
11221
+ resumeTemplate(sessionId) {
11222
+ return `opencode run --session ${sessionId}`;
11223
+ }
10797
11224
  },
10798
- dim(text) {
10799
- process.stdout.write(isTTY ? source_default.gray(text) : text);
11225
+ codex: {
11226
+ name: "codex",
11227
+ buildRunCommand(prompt, options) {
11228
+ const auto = options.allowAll ? ["--dangerously-bypass-approvals-and-sandbox"] : [];
11229
+ const args = ["exec", "--json", ...auto, prompt];
11230
+ return { binary: "codex", args: withModelArgs(options.model, args) };
11231
+ },
11232
+ buildResumeCommand(sessionId, prompt, options) {
11233
+ const auto = options.allowAll ? ["--dangerously-bypass-approvals-and-sandbox"] : [];
11234
+ const args = ["exec", "resume", "--json", ...auto, sessionId, prompt];
11235
+ return { binary: "codex", args: withModelArgs(options.model, args) };
11236
+ },
11237
+ createStreamParser: createCodexStreamParser,
11238
+ resumeTemplate(sessionId) {
11239
+ return `codex exec resume ${sessionId} "<prompt>"`;
11240
+ }
10800
11241
  },
10801
- pauseInstructions(info) {
10802
- process.stderr.write(`
10803
- `);
10804
- ui.warning(`Paused: ${info.reason}`);
10805
- process.stderr.write(`
10806
- `);
10807
- process.stdout.write(isTTY ? source_default.dim(`Resume workflow:
10808
- `) : `Resume workflow:
10809
- `);
10810
- process.stdout.write(` rmr continue ${info.runId}
10811
- `);
10812
- process.stdout.write(`
10813
- `);
10814
- process.stdout.write(isTTY ? source_default.dim(`Resume agent session directly:
10815
- `) : `Resume agent session directly:
10816
- `);
10817
- process.stdout.write(` ${info.resumeCommand}
10818
- `);
10819
- process.stdout.write(`
10820
- `);
11242
+ copilot: {
11243
+ name: "copilot",
11244
+ buildRunCommand(prompt, options) {
11245
+ const auto = options.allowAll ? ["--allow-all", "--no-ask-user"] : [];
11246
+ const args = [...auto, "-p", prompt];
11247
+ return { binary: "copilot", args: withModelArgs(options.model, args) };
11248
+ },
11249
+ buildResumeCommand(_sessionId, prompt, options) {
11250
+ const args = ["-p", prompt];
11251
+ return { binary: "copilot", args: withModelArgs(options.model, args) };
11252
+ },
11253
+ createStreamParser: createPassthroughParser,
11254
+ resumeTemplate(sessionId) {
11255
+ return `copilot --resume ${sessionId}`;
11256
+ }
10821
11257
  }
10822
11258
  };
10823
-
10824
- // src/lib/process-runner.ts
10825
- async function consumeStream(stream, onText) {
10826
- if (!stream) {
10827
- return "";
10828
- }
10829
- const reader = stream.getReader();
10830
- const decoder = new TextDecoder;
10831
- let fullText = "";
10832
- while (true) {
10833
- const { done, value } = await reader.read();
10834
- if (done) {
10835
- break;
10836
- }
10837
- const chunk = decoder.decode(value, { stream: true });
10838
- fullText += chunk;
10839
- onText(chunk);
11259
+ function getHarnessAdapter(name) {
11260
+ const adapter = adapters[name];
11261
+ if (!adapter) {
11262
+ throw new ValidationError(`Unknown harness "${name}".`);
10840
11263
  }
10841
- fullText += decoder.decode();
10842
- return fullText;
11264
+ return adapter;
10843
11265
  }
11266
+
11267
+ // src/lib/process-runner.ts
11268
+ import { spawn } from "node:child_process";
10844
11269
  function formatToolInput(toolInput) {
10845
11270
  try {
10846
11271
  const parsed = JSON.parse(toolInput);
@@ -10858,95 +11283,84 @@ function formatToolInput(toolInput) {
10858
11283
  return toolInput.length > 100 ? toolInput.slice(0, 97) + "..." : toolInput;
10859
11284
  }
10860
11285
  }
10861
- async function consumeStreamParsed(stream, parser) {
10862
- if (!stream) {
10863
- return { rawOutput: "", displayText: "", sessionId: null };
10864
- }
10865
- const reader = stream.getReader();
10866
- const decoder = new TextDecoder;
10867
- let rawOutput = "";
10868
- let displayText = "";
10869
- let sessionId = null;
10870
- let lineBuf = "";
10871
- let lastTextWasContent = false;
10872
- function processLine(line) {
10873
- const parsed = parser(line);
10874
- if (!parsed) {
10875
- return;
10876
- }
10877
- if (parsed.toolName) {
10878
- if (lastTextWasContent) {
10879
- process.stderr.write(`
11286
+ async function runHarnessCommand(command, parseStreamLine) {
11287
+ return new Promise((resolve5, reject) => {
11288
+ const child = spawn(command.binary, command.args, {
11289
+ stdio: ["ignore", "pipe", "pipe"],
11290
+ ...command.env ? { env: { ...process.env, ...command.env } } : {}
11291
+ });
11292
+ child.on("error", (err) => {
11293
+ if (err.code === "ENOENT") {
11294
+ reject(new StorageError(`Harness binary "${command.binary}" not found. ` + `Make sure it is installed and available on your PATH.`));
11295
+ } else if (err.code === "EACCES") {
11296
+ reject(new StorageError(`Permission denied when trying to run "${command.binary}". ` + `Check that the binary is executable.`));
11297
+ } else {
11298
+ reject(new StorageError(`Failed to launch harness binary "${command.binary}": ${err.message}`));
11299
+ }
11300
+ });
11301
+ let interrupted = false;
11302
+ const onSigint = () => {
11303
+ interrupted = true;
11304
+ };
11305
+ process.on("SIGINT", onSigint);
11306
+ let displayText = "";
11307
+ let sessionId = null;
11308
+ let stderrText = "";
11309
+ let stdoutLineBuf = "";
11310
+ let lastTextWasContent = false;
11311
+ function processLine(line) {
11312
+ const parsed = parseStreamLine(line);
11313
+ if (!parsed) {
11314
+ return;
11315
+ }
11316
+ if (parsed.toolName) {
11317
+ if (lastTextWasContent) {
11318
+ process.stderr.write(`
10880
11319
  `);
10881
- lastTextWasContent = false;
11320
+ lastTextWasContent = false;
11321
+ }
11322
+ const inputDisplay = parsed.toolInput ? formatToolInput(parsed.toolInput) : "";
11323
+ ui.printToolCall(parsed.toolName, inputDisplay);
11324
+ }
11325
+ if (parsed.text) {
11326
+ displayText += parsed.text;
11327
+ ui.content(parsed.text);
11328
+ lastTextWasContent = true;
11329
+ }
11330
+ if (parsed.sessionId) {
11331
+ sessionId = parsed.sessionId;
10882
11332
  }
10883
- const inputDisplay = parsed.toolInput ? formatToolInput(parsed.toolInput) : "";
10884
- ui.printToolCall(parsed.toolName, inputDisplay);
10885
- }
10886
- if (parsed.text) {
10887
- displayText += parsed.text;
10888
- ui.content(parsed.text);
10889
- lastTextWasContent = true;
10890
- }
10891
- if (parsed.sessionId) {
10892
- sessionId = parsed.sessionId;
10893
- }
10894
- }
10895
- while (true) {
10896
- const { done, value } = await reader.read();
10897
- if (done) {
10898
- break;
10899
11333
  }
10900
- const chunk = decoder.decode(value, { stream: true });
10901
- rawOutput += chunk;
10902
- lineBuf += chunk;
10903
- const lines = lineBuf.split(`
11334
+ child.stdout.on("data", (chunk) => {
11335
+ const text = chunk.toString();
11336
+ stdoutLineBuf += text;
11337
+ const lines = stdoutLineBuf.split(`
10904
11338
  `);
10905
- lineBuf = lines.pop() ?? "";
10906
- for (const line of lines) {
10907
- processLine(line);
10908
- }
10909
- }
10910
- rawOutput += decoder.decode();
10911
- if (lineBuf.trim()) {
10912
- processLine(lineBuf);
10913
- }
10914
- return { rawOutput, displayText, sessionId };
10915
- }
10916
- async function runHarnessCommand(command, parseStreamLine) {
10917
- let processRef;
10918
- try {
10919
- processRef = Bun.spawn({
10920
- cmd: [command.binary, ...command.args],
10921
- stdin: "ignore",
10922
- stdout: "pipe",
10923
- stderr: "pipe"
11339
+ stdoutLineBuf = lines.pop() ?? "";
11340
+ for (const line of lines) {
11341
+ processLine(line);
11342
+ }
11343
+ });
11344
+ child.stderr.on("data", (chunk) => {
11345
+ const text = chunk.toString();
11346
+ stderrText += text;
11347
+ process.stderr.write(text);
11348
+ });
11349
+ child.on("close", (code) => {
11350
+ if (stdoutLineBuf.trim()) {
11351
+ processLine(stdoutLineBuf);
11352
+ }
11353
+ process.removeListener("SIGINT", onSigint);
11354
+ const exitCode = interrupted && code === 0 ? 130 : code ?? 1;
11355
+ resolve5({
11356
+ exitCode,
11357
+ stdout: displayText,
11358
+ stderr: stderrText,
11359
+ combinedOutput: `${displayText}${stderrText}`,
11360
+ sessionId
11361
+ });
10924
11362
  });
10925
- } catch {
10926
- throw new StorageError(`Failed to launch harness binary "${command.binary}".`);
10927
- }
10928
- let interrupted = false;
10929
- const onSigint = () => {
10930
- interrupted = true;
10931
- };
10932
- process.on("SIGINT", onSigint);
10933
- const stdoutPromise = consumeStreamParsed(processRef.stdout, parseStreamLine);
10934
- const stderrPromise = consumeStream(processRef.stderr, (chunk) => {
10935
- process.stderr.write(chunk);
10936
11363
  });
10937
- const [exitCode, stdoutResult, stderr] = await Promise.all([
10938
- processRef.exited,
10939
- stdoutPromise,
10940
- stderrPromise
10941
- ]);
10942
- process.removeListener("SIGINT", onSigint);
10943
- return {
10944
- exitCode: interrupted && exitCode === 0 ? 130 : exitCode,
10945
- stdout: stdoutResult.displayText,
10946
- stderr,
10947
- combinedOutput: `${stdoutResult.displayText}${stderr}`,
10948
- sessionId: stdoutResult.sessionId
10949
- };
10950
11364
  }
10951
11365
 
10952
11366
  // src/lib/rmr-output-parser.ts
@@ -11025,6 +11439,20 @@ function outputSnippet(output) {
11025
11439
  const compact = trimmed.replace(/\s+/g, " ");
11026
11440
  return compact.length > 220 ? `${compact.slice(0, 220)}...` : compact;
11027
11441
  }
11442
+ function harnessExitReason(params) {
11443
+ const stderrSnippet = outputSnippet(params.stderr);
11444
+ const outputPreview = outputSnippet(params.combinedOutput);
11445
+ const details = [`harness=${params.harness}`];
11446
+ if (params.model) {
11447
+ details.push(`model=${params.model}`);
11448
+ }
11449
+ if (stderrSnippet !== "(no output)") {
11450
+ details.push(`stderr=${stderrSnippet}`);
11451
+ } else if (outputPreview !== "(no output)") {
11452
+ details.push(`output=${outputPreview}`);
11453
+ }
11454
+ return `Harness exited with code ${params.exitCode} at step "${params.stepId}" (${details.join("; ")}).`;
11455
+ }
11028
11456
  async function pauseRun(config, runState, reason, harnessName, sessionId) {
11029
11457
  runState.status = "paused_human";
11030
11458
  await saveRunState(config, runState);
@@ -11036,12 +11464,12 @@ async function pauseRun(config, runState, reason, harnessName, sessionId) {
11036
11464
  resumeCommand: adapter.resumeTemplate(resolvedSession)
11037
11465
  });
11038
11466
  }
11039
- function applyOutputToContext(context, agentId, values) {
11467
+ function applyOutputToContext(context, stepId, values) {
11040
11468
  for (const [key, value] of Object.entries(values)) {
11041
11469
  if (key === "status" || key === "next_state") {
11042
11470
  continue;
11043
11471
  }
11044
- context[`${agentId}.${key}`] = value;
11472
+ context[`${stepId}.${key}`] = value;
11045
11473
  }
11046
11474
  }
11047
11475
  async function runWorkflow(config, workflow, runState, options) {
@@ -11056,27 +11484,23 @@ async function runWorkflow(config, workflow, runState, options) {
11056
11484
  await pauseRun(config, runState, `Current step "${runState.current_step}" not found in workflow.`, runState.last_harness?.name ?? "claude", runState.last_harness?.session_id ?? null);
11057
11485
  return runState;
11058
11486
  }
11059
- const agent = workflow.agents.find((item) => item.id === step.agent);
11060
- if (!agent) {
11061
- await pauseRun(config, runState, `Unknown agent "${step.agent}" for step "${step.id}".`, runState.last_harness?.name ?? "claude", runState.last_harness?.session_id ?? null);
11062
- return runState;
11063
- }
11064
11487
  const stepStartedAt = new Date().toISOString();
11065
- ui.stepStart(stepNumber, step.id, agent.id);
11066
11488
  try {
11067
- assertRequiredInputs(step.input_required, runState.context);
11068
- const resolvedInput = resolveTemplate(step.input, runState.context);
11489
+ assertRequiredInputs(step.requires.inputs, runState.context);
11490
+ const fileContent = step.prompt_file ? await loadPromptFile(runState.workflow_path, step.prompt_file) : undefined;
11491
+ const rawPrompt = composePrompt(fileContent, step.prompt);
11492
+ const resolvedPrompt = resolveTemplate(rawPrompt, runState.context);
11069
11493
  const injectedHint = isFirstIteration && typeof options.overrides?.hint === "string" ? options.overrides.hint.trim() : "";
11070
- const renderedInput = injectedHint ? `${resolvedInput}
11494
+ const prompt = injectedHint ? `${resolvedPrompt}
11071
11495
 
11072
- Note: ${injectedHint}` : resolvedInput;
11073
- const agentPrompt = await loadAgentPrompt(runState.workflow_path, agent.prompt);
11074
- const prompt = composePrompt(agentPrompt, renderedInput);
11075
- const harness = options.overrides?.harness ?? agent.harness;
11496
+ Note: ${injectedHint}` : resolvedPrompt;
11497
+ const harness = step.harness;
11076
11498
  const adapter = getHarnessAdapter(harness);
11077
- const effectiveModel = options.overrides?.model ?? agent.model;
11499
+ const effectiveModel = step.model;
11078
11500
  const adapterOptions = typeof effectiveModel === "string" ? { allowAll: options.allowAll, model: effectiveModel } : { allowAll: options.allowAll };
11079
- const selectedSessionId = isFirstIteration && options.overrides?.sessionId ? options.overrides.sessionId : runState.last_harness?.session_id;
11501
+ ui.stepStart(stepNumber, step.id, harness, effectiveModel);
11502
+ const lastSessionMatchesHarness = runState.last_harness?.name === harness ? runState.last_harness.session_id : null;
11503
+ const selectedSessionId = isFirstIteration && options.overrides?.sessionId ? options.overrides.sessionId : lastSessionMatchesHarness;
11080
11504
  const command = isFirstIteration && selectedSessionId ? adapter.buildResumeCommand(selectedSessionId, prompt, {
11081
11505
  ...adapterOptions
11082
11506
  }) : adapter.buildRunCommand(prompt, {
@@ -11092,7 +11516,14 @@ Note: ${injectedHint}` : resolvedInput;
11092
11516
  runState.last_harness.session_id = result.sessionId;
11093
11517
  }
11094
11518
  if (result.exitCode !== 0) {
11095
- await pauseRun(config, runState, `Harness exited with code ${result.exitCode} at step "${step.id}".`, harness, runState.last_harness.session_id);
11519
+ await pauseRun(config, runState, harnessExitReason({
11520
+ stepId: step.id,
11521
+ harness,
11522
+ exitCode: result.exitCode,
11523
+ stderr: result.stderr,
11524
+ combinedOutput: result.combinedOutput,
11525
+ ...effectiveModel ? { model: effectiveModel } : {}
11526
+ }), harness, runState.last_harness.session_id);
11096
11527
  return runState;
11097
11528
  }
11098
11529
  if (result.combinedOutput.includes(HUMAN_SENTINEL)) {
@@ -11102,15 +11533,15 @@ Note: ${injectedHint}` : resolvedInput;
11102
11533
  let stepOutput;
11103
11534
  try {
11104
11535
  stepOutput = parseRmrOutput(result.combinedOutput);
11105
- validateRequiredOutputKeys(stepOutput, step.outputs.required);
11536
+ validateRequiredOutputKeys(stepOutput, step.requires.outputs);
11106
11537
  } catch (error) {
11107
11538
  const message = error instanceof Error ? error.message : "Failed to parse step output.";
11108
- await pauseRun(config, runState, `${message} Raw output snippet: ${outputSnippet(result.combinedOutput)}`, harness, runState.last_harness.session_id);
11539
+ await pauseRun(config, runState, message, harness, runState.last_harness.session_id);
11109
11540
  return runState;
11110
11541
  }
11111
11542
  ui.stepOutputs(stepOutput.values);
11112
11543
  applyOutputToContext(runState.context, step.id, stepOutput.values);
11113
- const nextState = stepOutput.next_state ?? step.default_next;
11544
+ const nextState = stepOutput.next_state ?? step.next_step;
11114
11545
  if (!isValidTarget(workflow, nextState)) {
11115
11546
  await pauseRun(config, runState, `Invalid next_state "${nextState}" at step "${step.id}".`, harness, runState.last_harness.session_id);
11116
11547
  return runState;
@@ -11122,7 +11553,6 @@ Note: ${injectedHint}` : resolvedInput;
11122
11553
  const stepExecution = {
11123
11554
  step_number: stepNumber,
11124
11555
  step_id: step.id,
11125
- agent_id: agent.id,
11126
11556
  session_id: runState.last_harness?.session_id ?? null,
11127
11557
  started_at: stepStartedAt,
11128
11558
  completed_at: new Date().toISOString()
@@ -11142,25 +11572,13 @@ Note: ${injectedHint}` : resolvedInput;
11142
11572
  isFirstIteration = false;
11143
11573
  } catch (error) {
11144
11574
  const reason = error instanceof Error ? error.message : "Unknown execution error.";
11145
- await pauseRun(config, runState, `${reason} (step "${step.id}")`, options.overrides?.harness ?? agent.harness, runState.last_harness?.session_id ?? null);
11575
+ await pauseRun(config, runState, `${reason} (step "${step.id}")`, step.harness, runState.last_harness?.session_id ?? null);
11146
11576
  return runState;
11147
11577
  }
11148
11578
  }
11149
11579
  return runState;
11150
11580
  }
11151
11581
 
11152
- // src/lib/types.ts
11153
- var HARNESSES = ["claude", "opencode", "codex", "copilot"];
11154
- function parseHarnessOverride(value) {
11155
- if (!value) {
11156
- return;
11157
- }
11158
- if (!HARNESSES.includes(value)) {
11159
- throw new UserInputError(`Invalid harness override "${value}". Expected one of: ${HARNESSES.join(", ")}.`);
11160
- }
11161
- return value;
11162
- }
11163
-
11164
11582
  // src/lib/version.ts
11165
11583
  import { readFileSync } from "node:fs";
11166
11584
  import { dirname as dirname2, resolve as resolve5 } from "node:path";
@@ -11336,81 +11754,85 @@ async function loadWorkflowDefinition(workflowPath) {
11336
11754
  const id = ensureString(parsed.id, "id");
11337
11755
  const name = ensureString(parsed.name, "name");
11338
11756
  const version = typeof parsed.version === "string" ? parsed.version : undefined;
11339
- if (!Array.isArray(parsed.agents) || parsed.agents.length === 0) {
11340
- throw new ValidationError("Workflow must define a non-empty agents array.");
11757
+ let topLevelHarness;
11758
+ if (typeof parsed.harness === "string" && parsed.harness.trim() !== "") {
11759
+ const h = parsed.harness.trim();
11760
+ if (!SUPPORTED_HARNESSES.has(h)) {
11761
+ throw new ValidationError(`Unsupported top-level harness "${h}".`);
11762
+ }
11763
+ topLevelHarness = h;
11341
11764
  }
11765
+ const topLevelModel = typeof parsed.model === "string" && parsed.model.trim() !== "" ? parsed.model.trim() : undefined;
11342
11766
  if (!Array.isArray(parsed.steps) || parsed.steps.length === 0) {
11343
11767
  throw new ValidationError("Workflow must define a non-empty steps array.");
11344
11768
  }
11345
- const agents = parsed.agents.map((rawAgent, index) => {
11346
- if (!rawAgent || typeof rawAgent !== "object") {
11347
- throw new ValidationError(`Invalid agents[${index}] definition.`);
11348
- }
11349
- const agent = rawAgent;
11350
- const agentId = ensureString(agent.id, `agents[${index}].id`);
11351
- const harness = ensureString(agent.harness, `agents[${index}].harness`);
11352
- if (!SUPPORTED_HARNESSES.has(harness)) {
11353
- throw new ValidationError(`Unsupported harness "${harness}" for agent "${agentId}".`);
11769
+ const steps = parsed.steps.map((rawStep, index) => {
11770
+ if (!rawStep || typeof rawStep !== "object") {
11771
+ throw new ValidationError(`Invalid steps[${index}] definition.`);
11354
11772
  }
11355
- const prompt = ensureString(agent.prompt, `agents[${index}].prompt`);
11356
- const model = agent.model;
11773
+ const step = rawStep;
11774
+ const stepId = ensureString(step.id, `steps[${index}].id`);
11775
+ let harness;
11776
+ if (typeof step.harness === "string" && step.harness.trim() !== "") {
11777
+ harness = step.harness.trim();
11778
+ if (!SUPPORTED_HARNESSES.has(harness)) {
11779
+ throw new ValidationError(`Unsupported harness "${harness}" for step "${stepId}".`);
11780
+ }
11781
+ } else if (topLevelHarness) {
11782
+ harness = topLevelHarness;
11783
+ } else {
11784
+ throw new ValidationError(`Step "${stepId}" has no harness and no top-level harness default is defined.`);
11785
+ }
11786
+ const stepModel = step.model;
11787
+ const effectiveModel = typeof stepModel === "string" && stepModel.trim() !== "" ? stepModel : topLevelModel;
11788
+ const hasPromptFile = typeof step.prompt_file === "string" && step.prompt_file.trim() !== "";
11789
+ const hasPrompt = typeof step.prompt === "string" && step.prompt.trim() !== "";
11790
+ if (!hasPromptFile && !hasPrompt) {
11791
+ throw new ValidationError(`Step "${stepId}" must define at least one of "prompt_file" or "prompt".`);
11792
+ }
11793
+ const requires = step.requires;
11794
+ const requiresInputs = requires && Array.isArray(requires.inputs) ? ensureStringArray(requires.inputs, `steps[${index}].requires.inputs`) : [];
11795
+ const requiresOutputs = requires && Array.isArray(requires.outputs) ? ensureStringArray(requires.outputs, `steps[${index}].requires.outputs`) : [];
11357
11796
  const normalized = {
11358
- id: agentId,
11797
+ id: stepId,
11359
11798
  harness,
11360
- prompt
11799
+ next_step: ensureString(step.next_step, `steps[${index}].next_step`),
11800
+ requires: {
11801
+ inputs: requiresInputs,
11802
+ outputs: requiresOutputs
11803
+ }
11361
11804
  };
11362
- if (typeof model === "string" && model.trim() !== "") {
11363
- return {
11364
- ...normalized,
11365
- model
11366
- };
11805
+ if (hasPromptFile) {
11806
+ normalized.prompt_file = step.prompt_file.trim();
11367
11807
  }
11368
- return normalized;
11369
- });
11370
- const steps = parsed.steps.map((rawStep, index) => {
11371
- if (!rawStep || typeof rawStep !== "object") {
11372
- throw new ValidationError(`Invalid steps[${index}] definition.`);
11808
+ if (hasPrompt) {
11809
+ normalized.prompt = step.prompt;
11373
11810
  }
11374
- const step = rawStep;
11375
- const outputs = step.outputs;
11376
- if (!outputs || typeof outputs !== "object") {
11377
- throw new ValidationError(`Invalid step field "steps[${index}].outputs": expected object.`);
11811
+ if (effectiveModel) {
11812
+ normalized.model = effectiveModel;
11378
11813
  }
11379
- return {
11380
- id: ensureString(step.id, `steps[${index}].id`),
11381
- agent: ensureString(step.agent, `steps[${index}].agent`),
11382
- default_next: ensureString(step.default_next, `steps[${index}].default_next`),
11383
- input_required: ensureStringArray(step.input_required, `steps[${index}].input_required`),
11384
- outputs: {
11385
- required: ensureStringArray(outputs.required, `steps[${index}].outputs.required`)
11386
- },
11387
- input: ensureString(step.input, `steps[${index}].input`)
11388
- };
11814
+ return normalized;
11389
11815
  });
11390
- validateUniqueness(agents.map((agent) => agent.id), "agent");
11391
11816
  validateUniqueness(steps.map((step) => step.id), "step");
11392
- const knownAgents = new Set(agents.map((agent) => agent.id));
11393
11817
  const knownSteps = new Set(steps.map((step) => step.id));
11394
11818
  for (const step of steps) {
11395
- if (!knownAgents.has(step.agent)) {
11396
- throw new ValidationError(`Unknown agent "${step.agent}" referenced by step "${step.id}".`);
11397
- }
11398
11819
  const validTransitionTargets = new Set([...knownSteps, "done", "human_intervention"]);
11399
- if (!validTransitionTargets.has(step.default_next)) {
11400
- throw new ValidationError(`Invalid default_next "${step.default_next}" in step "${step.id}".`);
11820
+ if (!validTransitionTargets.has(step.next_step)) {
11821
+ throw new ValidationError(`Invalid next_step "${step.next_step}" in step "${step.id}".`);
11401
11822
  }
11402
11823
  }
11403
11824
  return {
11404
11825
  id,
11405
11826
  name,
11406
11827
  ...version && { version },
11407
- agents,
11828
+ ...topLevelHarness && { harness: topLevelHarness },
11829
+ ...topLevelModel && { model: topLevelModel },
11408
11830
  steps
11409
11831
  };
11410
11832
  }
11411
11833
 
11412
11834
  // src/commands/continue.ts
11413
- class ContinueCommand extends Command {
11835
+ class ContinueCommand extends BaseCommand {
11414
11836
  static paths = [["continue"]];
11415
11837
  static usage = Command.Usage({
11416
11838
  category: "Workflow",
@@ -11424,8 +11846,8 @@ class ContinueCommand extends Command {
11424
11846
  '$0 continue 20260316-153210Z --hint "Plan mode only: read and propose changes, do not edit files."'
11425
11847
  ],
11426
11848
  [
11427
- "Force harness/session override",
11428
- "$0 continue 20260316-153210Z --harness claude --session-id abc123"
11849
+ "Force session override",
11850
+ "$0 continue 20260316-153210Z --session-id abc123"
11429
11851
  ]
11430
11852
  ]
11431
11853
  });
@@ -11436,10 +11858,6 @@ class ContinueCommand extends Command {
11436
11858
  required: false,
11437
11859
  description: "Override current step id before resuming."
11438
11860
  });
11439
- harness = exports_options.String("--harness", {
11440
- required: false,
11441
- description: "Override harness for the resumed step."
11442
- });
11443
11861
  sessionId = exports_options.String("--session-id", {
11444
11862
  required: false,
11445
11863
  description: "Force harness session id for resume attempt."
@@ -11448,35 +11866,36 @@ class ContinueCommand extends Command {
11448
11866
  required: false,
11449
11867
  description: "Inject a one-time hint into the resumed harness prompt."
11450
11868
  });
11869
+ allowAll = exports_options.Boolean("--allow-all", true, {
11870
+ description: "Enable harness auto-approval flags when supported (default: true)."
11871
+ });
11872
+ noAllowAll = exports_options.Boolean("--no-allow-all", false, {
11873
+ description: "Disable harness auto-approval flags."
11874
+ });
11451
11875
  async execute() {
11452
11876
  const showUpdateNotice = startUpdateCheck();
11453
11877
  const config = await loadConfig();
11454
11878
  const runState = await loadRunState(config, this.runId);
11455
11879
  const workflow = await loadWorkflowDefinition(runState.workflow_path);
11456
- const harnessOverride = parseHarnessOverride(this.harness);
11457
11880
  runState.status = "running";
11458
11881
  if (this.step) {
11459
11882
  runState.current_step = this.step;
11460
11883
  }
11884
+ const effectiveAllowAll = this.noAllowAll ? false : this.allowAll;
11461
11885
  ui.workflowHeader({
11462
- title: "rmr continue",
11886
+ title: `${binaryName} continue`,
11463
11887
  workflow: runState.workflow_path,
11464
11888
  workflowId: workflow.id,
11465
11889
  task: runState.context["task"] ?? "(continuing)",
11466
11890
  runId: this.runId,
11467
- currentStep: runState.current_step,
11468
11891
  runFile: "",
11469
- allowAll: true,
11470
- harness: this.harness,
11892
+ allowAll: effectiveAllowAll,
11471
11893
  varsCount: 0
11472
11894
  });
11473
11895
  const overrides = {};
11474
11896
  if (this.step) {
11475
11897
  overrides.stepId = this.step;
11476
11898
  }
11477
- if (harnessOverride) {
11478
- overrides.harness = harnessOverride;
11479
- }
11480
11899
  if (this.sessionId) {
11481
11900
  overrides.sessionId = this.sessionId;
11482
11901
  }
@@ -11484,7 +11903,7 @@ class ContinueCommand extends Command {
11484
11903
  overrides.hint = this.hint;
11485
11904
  }
11486
11905
  await runWorkflow(config, workflow, runState, {
11487
- allowAll: true,
11906
+ allowAll: effectiveAllowAll,
11488
11907
  overrides
11489
11908
  });
11490
11909
  showUpdateNotice();
@@ -11508,7 +11927,7 @@ function getExamplesWorkflowsDir() {
11508
11927
  return fromSrc;
11509
11928
  }
11510
11929
 
11511
- class InstallCommand extends Command {
11930
+ class InstallCommand extends BaseCommand {
11512
11931
  static paths = [["install"]];
11513
11932
  static usage = Command.Usage({
11514
11933
  category: "Setup",
@@ -11534,76 +11953,191 @@ class InstallCommand extends Command {
11534
11953
  }
11535
11954
  if (existsSync(destinationDir)) {
11536
11955
  ui.info(`Workflow already installed at .rmr/workflows/${this.workflowName}/`);
11537
- ui.info(`Run it with: rmr run .rmr/workflows/${this.workflowName}/workflow.yaml --task "Describe your task"`);
11956
+ ui.info(`Run it with: ${binaryName} run .rmr/workflows/${this.workflowName}/workflow.yaml --task "Describe your task"`);
11538
11957
  return 0;
11539
11958
  }
11540
11959
  await cp(sourceDir, destinationDir, { recursive: true, force: false, errorOnExist: true });
11541
11960
  ui.success(`installed .rmr/workflows/${this.workflowName}/`);
11542
- ui.info(`Run it with: rmr run .rmr/workflows/${this.workflowName}/workflow.yaml --task "Describe your task"`);
11961
+ ui.info(`Run it with: ${binaryName} run .rmr/workflows/${this.workflowName}/workflow.yaml --task "Describe your task"`);
11962
+ return 0;
11963
+ }
11964
+ }
11965
+
11966
+ // src/commands/list.ts
11967
+ import { readdir as readdir3 } from "node:fs/promises";
11968
+ import { existsSync as existsSync2 } from "node:fs";
11969
+ import { dirname as dirname4, resolve as resolve8 } from "node:path";
11970
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
11971
+ function getExamplesWorkflowsDir2() {
11972
+ const thisDir = dirname4(fileURLToPath3(import.meta.url));
11973
+ const fromDist = resolve8(thisDir, "..", "examples", "workflows");
11974
+ const fromSrc = resolve8(thisDir, "..", "..", "examples", "workflows");
11975
+ if (existsSync2(fromDist))
11976
+ return fromDist;
11977
+ if (existsSync2(fromSrc))
11978
+ return fromSrc;
11979
+ return fromSrc;
11980
+ }
11981
+ async function getInstalledWorkflows(workflowsDir) {
11982
+ if (!existsSync2(workflowsDir)) {
11983
+ return [];
11984
+ }
11985
+ const entries = await readdir3(workflowsDir, { withFileTypes: true });
11986
+ const workflows = [];
11987
+ for (const entry of entries) {
11988
+ if (!entry.isDirectory())
11989
+ continue;
11990
+ const workflowPath = resolve8(workflowsDir, entry.name, "workflow.yaml");
11991
+ if (!existsSync2(workflowPath))
11992
+ continue;
11993
+ try {
11994
+ const workflow = await loadWorkflowDefinition(workflowPath);
11995
+ workflows.push({
11996
+ id: workflow.id,
11997
+ name: workflow.name,
11998
+ path: `.rmr/workflows/${entry.name}/workflow.yaml`
11999
+ });
12000
+ } catch {
12001
+ workflows.push({
12002
+ id: entry.name,
12003
+ name: "(invalid workflow.yaml)",
12004
+ path: `.rmr/workflows/${entry.name}/workflow.yaml`
12005
+ });
12006
+ }
12007
+ }
12008
+ return workflows.sort((a, b) => a.id.localeCompare(b.id));
12009
+ }
12010
+ async function getBundledWorkflows(examplesDir) {
12011
+ if (!existsSync2(examplesDir)) {
12012
+ return [];
12013
+ }
12014
+ const entries = await readdir3(examplesDir, { withFileTypes: true });
12015
+ const workflows = [];
12016
+ for (const entry of entries) {
12017
+ if (!entry.isDirectory())
12018
+ continue;
12019
+ const workflowPath = resolve8(examplesDir, entry.name, "workflow.yaml");
12020
+ if (!existsSync2(workflowPath))
12021
+ continue;
12022
+ try {
12023
+ const workflow = await loadWorkflowDefinition(workflowPath);
12024
+ workflows.push({
12025
+ id: workflow.id,
12026
+ name: workflow.name,
12027
+ path: entry.name
12028
+ });
12029
+ } catch {
12030
+ workflows.push({
12031
+ id: entry.name,
12032
+ name: "(invalid workflow.yaml)",
12033
+ path: entry.name
12034
+ });
12035
+ }
12036
+ }
12037
+ return workflows.sort((a, b) => a.id.localeCompare(b.id));
12038
+ }
12039
+
12040
+ class ListCommand extends BaseCommand {
12041
+ static paths = [["list"]];
12042
+ static usage = Command.Usage({
12043
+ category: "Setup",
12044
+ description: "List installed and available workflows.",
12045
+ details: "Shows workflows installed in `.rmr/workflows/` and bundled workflows available for installation.",
12046
+ examples: [["List all workflows", "$0 list"]]
12047
+ });
12048
+ async execute() {
12049
+ const config = await loadConfig();
12050
+ const examplesDir = getExamplesWorkflowsDir2();
12051
+ const installed = await getInstalledWorkflows(config.workflowsDir);
12052
+ const bundled = await getBundledWorkflows(examplesDir);
12053
+ const installedIds = new Set(installed.map((w) => w.id));
12054
+ if (installed.length > 0) {
12055
+ ui.info("Installed workflows:");
12056
+ for (const workflow of installed) {
12057
+ ui.info(` ${workflow.id.padEnd(16)} ${workflow.name.padEnd(24)} ${workflow.path}`);
12058
+ }
12059
+ ui.info("");
12060
+ }
12061
+ const available = bundled.filter((w) => !installedIds.has(w.id));
12062
+ if (available.length > 0) {
12063
+ ui.info("Available to install:");
12064
+ for (const workflow of available) {
12065
+ ui.info(` ${workflow.id.padEnd(16)} ${binaryName} install ${workflow.path}`);
12066
+ }
12067
+ ui.info("");
12068
+ }
12069
+ if (installed.length === 0 && bundled.length > 0) {
12070
+ ui.info("No workflows installed yet. Install one with:");
12071
+ ui.info(` ${binaryName} install ${bundled[0]?.path}`);
12072
+ ui.info("");
12073
+ }
12074
+ if (installed.length === 0 && bundled.length === 0) {
12075
+ ui.info("No workflows found.");
12076
+ }
11543
12077
  return 0;
11544
12078
  }
11545
12079
  }
11546
12080
 
11547
12081
  // src/commands/root.ts
11548
- import { basename } from "node:path";
12082
+ import { basename as basename2 } from "node:path";
11549
12083
  function detectShell() {
11550
12084
  const raw = process.env.SHELL;
11551
12085
  if (!raw) {
11552
12086
  return null;
11553
12087
  }
11554
- const shell = basename(raw);
12088
+ const shell = basename2(raw);
11555
12089
  if (shell === "bash" || shell === "zsh" || shell === "fish") {
11556
12090
  return shell;
11557
12091
  }
11558
12092
  return null;
11559
12093
  }
11560
12094
 
11561
- class RootCommand extends Command {
12095
+ class RootCommand extends BaseCommand {
11562
12096
  static paths = [Command.Default];
11563
12097
  async execute() {
11564
12098
  const shell = detectShell();
11565
- process.stdout.write(`rmr ${getVersion()} - multi-step coding workflows for AI agents
12099
+ process.stdout.write(`${binaryName} ${getVersion()} - multi-step coding workflows for AI agents
11566
12100
 
11567
12101
  `);
11568
12102
  process.stdout.write(`Setup
11569
12103
  `);
11570
- process.stdout.write(` rmr install <name> Install bundled workflow into .rmr/workflows/
12104
+ process.stdout.write(` ${binaryName} install <name> Install bundled workflow into .rmr/workflows/
11571
12105
 
11572
12106
  `);
11573
12107
  process.stdout.write(`Workflow
11574
12108
  `);
11575
- process.stdout.write(` rmr run <workflow-path> Start a new workflow run (requires --task/-t or --task-file/-f)
12109
+ process.stdout.write(` ${binaryName} run <workflow-path> Start a new workflow run (requires --task/-t or --task-file/-f)
11576
12110
  `);
11577
- process.stdout.write(` rmr continue <run-id> Resume a paused or interrupted run
12111
+ process.stdout.write(` ${binaryName} continue <run-id> Resume a paused or interrupted run
11578
12112
 
11579
12113
  `);
11580
12114
  process.stdout.write(`Shell Completion (optional)
11581
12115
  `);
11582
12116
  if (shell === "fish") {
11583
- process.stdout.write(` rmr completion fish > ~/.config/fish/completions/rmr.fish
12117
+ process.stdout.write(` ${binaryName} completion fish > ~/.config/fish/completions/${binaryName}.fish
11584
12118
 
11585
12119
  `);
11586
12120
  } else if (shell) {
11587
12121
  const rcFile = shell === "zsh" ? "~/.zshrc" : "~/.bashrc";
11588
- process.stdout.write(` echo 'eval "$(rmr completion ${shell})"' >> ${rcFile}
12122
+ process.stdout.write(` echo 'eval "$(${binaryName} completion ${shell})"' >> ${rcFile}
11589
12123
  `);
11590
12124
  process.stdout.write(` source ${rcFile}
11591
12125
 
11592
12126
  `);
11593
12127
  } else {
11594
- process.stdout.write(` echo 'eval "$(rmr completion zsh)"' >> ~/.zshrc && source ~/.zshrc
12128
+ process.stdout.write(` echo 'eval "$(${binaryName} completion zsh)"' >> ~/.zshrc && source ~/.zshrc
11595
12129
  `);
11596
- process.stdout.write(` echo 'eval "$(rmr completion bash)"' >> ~/.bashrc && source ~/.bashrc
12130
+ process.stdout.write(` echo 'eval "$(${binaryName} completion bash)"' >> ~/.bashrc && source ~/.bashrc
11597
12131
  `);
11598
- process.stdout.write(` rmr completion fish > ~/.config/fish/completions/rmr.fish
12132
+ process.stdout.write(` ${binaryName} completion fish > ~/.config/fish/completions/${binaryName}.fish
11599
12133
 
11600
12134
  `);
11601
12135
  }
11602
12136
  process.stdout.write(`More
11603
12137
  `);
11604
- process.stdout.write(` rmr --help Show full help with all options
12138
+ process.stdout.write(` ${binaryName} --help Show full help with all options
11605
12139
  `);
11606
- process.stdout.write(` rmr <command> --help Show help for a specific command
12140
+ process.stdout.write(` ${binaryName} <command> --help Show help for a specific command
11607
12141
  `);
11608
12142
  return 0;
11609
12143
  }
@@ -11645,8 +12179,8 @@ var logger = {
11645
12179
  };
11646
12180
 
11647
12181
  // src/commands/run.ts
11648
- import { readFile as readFile4 } from "node:fs/promises";
11649
- import { resolve as resolve8 } from "node:path";
12182
+ import { readFile as readFile4, stat } from "node:fs/promises";
12183
+ import { resolve as resolve9 } from "node:path";
11650
12184
  function parseVar(input) {
11651
12185
  const index = input.indexOf("=");
11652
12186
  if (index < 1 || index === input.length - 1) {
@@ -11657,8 +12191,57 @@ function parseVar(input) {
11657
12191
  value: input.slice(index + 1).trim()
11658
12192
  };
11659
12193
  }
12194
+ function getErrorMessage(error) {
12195
+ if (error instanceof Error) {
12196
+ return error.message;
12197
+ }
12198
+ if (typeof error === "string") {
12199
+ return error;
12200
+ }
12201
+ try {
12202
+ return JSON.stringify(error);
12203
+ } catch {
12204
+ return "Unknown error";
12205
+ }
12206
+ }
12207
+ function isErrnoLike(error) {
12208
+ return typeof error === "object" && error !== null;
12209
+ }
12210
+ async function resolveWorkflowPath(inputPath) {
12211
+ const absolutePath = resolve9(inputPath);
12212
+ try {
12213
+ const pathStat = await stat(absolutePath);
12214
+ if (!pathStat.isDirectory()) {
12215
+ return absolutePath;
12216
+ }
12217
+ const yamlPath = resolve9(absolutePath, "workflow.yaml");
12218
+ try {
12219
+ const yamlStat = await stat(yamlPath);
12220
+ if (yamlStat.isFile()) {
12221
+ return yamlPath;
12222
+ }
12223
+ } catch {}
12224
+ const ymlPath = resolve9(absolutePath, "workflow.yml");
12225
+ try {
12226
+ const ymlStat = await stat(ymlPath);
12227
+ if (ymlStat.isFile()) {
12228
+ return ymlPath;
12229
+ }
12230
+ } catch {}
12231
+ throw new UserInputError(`Workflow directory does not contain workflow.yaml or workflow.yml: ${absolutePath}`);
12232
+ } catch (error) {
12233
+ if (isErrnoLike(error) && error.code === "ENOENT") {
12234
+ const hint = absolutePath.includes(`${resolve9(".rmr")}/workflow/`) ? " Did you mean .rmr/workflows/?" : "";
12235
+ throw new UserInputError(`Workflow does not exist: ${absolutePath}${hint}`);
12236
+ }
12237
+ if (error instanceof UserInputError) {
12238
+ throw error;
12239
+ }
12240
+ throw new UserInputError(`Failed to access workflow path "${absolutePath}": ${getErrorMessage(error)}`);
12241
+ }
12242
+ }
11660
12243
 
11661
- class RunCommand extends Command {
12244
+ class RunCommand extends BaseCommand {
11662
12245
  static paths = [["run"]];
11663
12246
  static usage = Command.Usage({
11664
12247
  category: "Workflow",
@@ -11670,14 +12253,6 @@ class RunCommand extends Command {
11670
12253
  '$0 run .rmr/workflows/feature-dev/workflow.yaml --task "Implement auth middleware"'
11671
12254
  ],
11672
12255
  ["Run with task file", "$0 run .rmr/workflows/feature-dev/workflow.yaml --task-file task.md"],
11673
- [
11674
- "Override harness",
11675
- '$0 run .rmr/workflows/feature-dev/workflow.yaml --task "Fix bug" --harness opencode'
11676
- ],
11677
- [
11678
- "Override model",
11679
- '$0 run .rmr/workflows/feature-dev/workflow.yaml --task "Fix bug" --model openai/gpt-5.3-codex-high'
11680
- ],
11681
12256
  [
11682
12257
  "Run with extra variables",
11683
12258
  '$0 run .rmr/workflows/feature-dev/workflow.yaml --task "Ship feature" --var issue_id=123 --var env=staging'
@@ -11699,14 +12274,6 @@ class RunCommand extends Command {
11699
12274
  required: false,
11700
12275
  description: "Path to file containing task description."
11701
12276
  });
11702
- harness = exports_options.String("--harness", {
11703
- required: false,
11704
- description: "Override harness for all workflow steps."
11705
- });
11706
- model = exports_options.String("--model", {
11707
- required: false,
11708
- description: "Override model for all workflow steps (e.g., openai/gpt-5.3-codex-high)."
11709
- });
11710
12277
  vars = exports_options.Array("--var", [], {
11711
12278
  description: "Inject initial variables as key=value (repeatable)."
11712
12279
  });
@@ -11722,7 +12289,7 @@ class RunCommand extends Command {
11722
12289
  }
11723
12290
  if (this.taskFile) {
11724
12291
  try {
11725
- const content = await readFile4(resolve8(this.taskFile), "utf-8");
12292
+ const content = await readFile4(resolve9(this.taskFile), "utf-8");
11726
12293
  const task = content.trim();
11727
12294
  return { task, displayTask: `(file: ${this.taskFile})` };
11728
12295
  } catch (error) {
@@ -11733,18 +12300,34 @@ class RunCommand extends Command {
11733
12300
  if (this.taskFlag) {
11734
12301
  return { task: this.taskFlag, displayTask: this.taskFlag };
11735
12302
  }
12303
+ if (process.stdin.isTTY) {
12304
+ ui.warning("No task provided. Enter your task below. Press Ctrl+D to submit.");
12305
+ const task = await ui.multilinePrompt("Task: ");
12306
+ const trimmedTask = task.trim();
12307
+ if (!trimmedTask) {
12308
+ throw new UserInputError("Task cannot be empty.");
12309
+ }
12310
+ return { task: trimmedTask, displayTask: trimmedTask };
12311
+ }
11736
12312
  throw new UserInputError("No task provided. Use --task/-t or --task-file/-f.");
11737
12313
  }
11738
12314
  async execute() {
11739
12315
  const showUpdateNotice = startUpdateCheck();
11740
12316
  const config = await loadConfig();
11741
- const { task, displayTask } = await this.resolveTask();
11742
- const harnessOverride = parseHarnessOverride(this.harness);
11743
12317
  const parsedVars = this.vars.map(parseVar);
11744
12318
  const effectiveAllowAll = this.noAllowAll ? false : this.allowAll;
11745
12319
  const varsObject = Object.fromEntries(parsedVars.map((entry) => [entry.key, entry.value]));
11746
- const workflowPath = resolve8(this.workflowPath);
11747
- const workflow = await loadWorkflowDefinition(workflowPath);
12320
+ const workflowPath = await resolveWorkflowPath(this.workflowPath);
12321
+ let workflow;
12322
+ try {
12323
+ workflow = await loadWorkflowDefinition(workflowPath);
12324
+ } catch (error) {
12325
+ if (isErrnoLike(error) && error.code === "ENOENT") {
12326
+ throw new UserInputError(`Workflow does not exist: ${workflowPath}`);
12327
+ }
12328
+ throw new UserInputError(`Failed to load workflow "${workflowPath}": ${getErrorMessage(error)}`);
12329
+ }
12330
+ const { task, displayTask } = await this.resolveTask();
11748
12331
  const runId = generateRunId();
11749
12332
  const runState = createInitialRunState({
11750
12333
  runId,
@@ -11755,28 +12338,17 @@ class RunCommand extends Command {
11755
12338
  });
11756
12339
  const runPath = await saveRunState(config, runState);
11757
12340
  ui.workflowHeader({
11758
- title: "rmr config",
12341
+ title: `${binaryName} config`,
11759
12342
  workflow: workflowPath,
11760
12343
  workflowId: workflow.id,
11761
12344
  task: displayTask,
11762
12345
  runId: runState.run_id,
11763
- currentStep: runState.current_step,
11764
12346
  runFile: runPath,
11765
12347
  allowAll: effectiveAllowAll,
11766
- harness: this.harness,
11767
- model: this.model,
11768
12348
  varsCount: parsedVars.length
11769
12349
  });
11770
- const overrides = {};
11771
- if (harnessOverride) {
11772
- overrides.harness = harnessOverride;
11773
- }
11774
- if (this.model) {
11775
- overrides.model = this.model;
11776
- }
11777
12350
  await runWorkflow(config, workflow, runState, {
11778
- allowAll: effectiveAllowAll,
11779
- ...Object.keys(overrides).length > 0 && { overrides }
12351
+ allowAll: effectiveAllowAll
11780
12352
  });
11781
12353
  showUpdateNotice();
11782
12354
  return 0;
@@ -11786,7 +12358,7 @@ class RunCommand extends Command {
11786
12358
  // src/index.ts
11787
12359
  var [, , ...args] = process.argv;
11788
12360
  var cli = new Cli({
11789
- binaryName: "rmr",
12361
+ binaryName,
11790
12362
  binaryVersion: getVersion(),
11791
12363
  enableColors: false
11792
12364
  });
@@ -11794,13 +12366,14 @@ cli.register(exports_builtins.HelpCommand);
11794
12366
  cli.register(exports_builtins.VersionCommand);
11795
12367
  cli.register(RootCommand);
11796
12368
  cli.register(InstallCommand);
12369
+ cli.register(ListCommand);
11797
12370
  cli.register(RunCommand);
11798
12371
  cli.register(ContinueCommand);
11799
12372
  cli.register(CompleteCommand);
11800
12373
  cli.register(CompletionCommand);
11801
12374
  try {
11802
12375
  const exitCode = await cli.run(args);
11803
- process.exitCode = exitCode;
12376
+ process.exitCode = Math.max(process.exitCode ?? 0, exitCode);
11804
12377
  } catch (error) {
11805
12378
  if (error instanceof RmrError) {
11806
12379
  logger.error(`${error.code}: ${error.message}`);