@klaudworks/rmr 0.4.5 → 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,1157 +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;
9995
- }
9996
- }
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;
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;
10007
9936
  }
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;
10020
- }
9937
+ case "Apple_Terminal": {
9938
+ return 2;
10021
9939
  }
10022
- return sessionId ? { text: "", sessionId } : null;
10023
- }
10024
- if (type === "result") {
10025
- return sessionId ? { text: "", sessionId } : null;
10026
9940
  }
10027
- if (type === "system") {
10028
- return sessionId ? { text: "", sessionId } : null;
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;
10029
10034
  }
10030
- return null;
10031
10035
  };
10032
10036
  }
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
- }
10050
- },
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
- }
10065
- },
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>"`;
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_);
10080
10048
  }
10081
- },
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}`;
10049
+ if (level === "ansi256") {
10050
+ return ansi_styles_default[type].ansi256(ansi_styles_default.rgbToAnsi256(...arguments_));
10096
10051
  }
10052
+ return ansi_styles_default[type].ansi(ansi_styles_default.rgbToAnsi(...arguments_));
10097
10053
  }
10098
- };
10099
- function getHarnessAdapter(name) {
10100
- const adapter = adapters[name];
10101
- if (!adapter) {
10102
- throw new ValidationError(`Unknown harness "${name}".`);
10054
+ if (model === "hex") {
10055
+ return getModelAnsi("rgb", level, type, ...ansi_styles_default.hexToRgb(...arguments_));
10103
10056
  }
10104
- return adapter;
10105
- }
10106
-
10107
- // src/lib/process-runner.ts
10108
- import { spawn } from "node:child_process";
10109
-
10110
- // node_modules/chalk/source/vendor/ansi-styles/index.js
10111
- var ANSI_BACKGROUND_OFFSET = 10;
10112
- var wrapAnsi16 = (offset = 0) => (code) => `\x1B[${code + offset}m`;
10113
- var wrapAnsi256 = (offset = 0) => (code) => `\x1B[${38 + offset};5;${code}m`;
10114
- var wrapAnsi16m = (offset = 0) => (red, green, blue) => `\x1B[${38 + offset};2;${red};${green};${blue}m`;
10115
- var styles = {
10116
- modifier: {
10117
- reset: [0, 0],
10118
- bold: [1, 22],
10119
- dim: [2, 22],
10120
- italic: [3, 23],
10121
- underline: [4, 24],
10122
- overline: [53, 55],
10123
- inverse: [7, 27],
10124
- hidden: [8, 28],
10125
- strikethrough: [9, 29]
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
+ }
10330
+ }
10331
+ }
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
+ `);
10339
+ }
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
+ `);
10347
+ }
10348
+ }
10349
+ }
10350
+ },
10351
+ content(text) {
10352
+ process.stdout.write(text);
10353
+ },
10354
+ success(text) {
10355
+ const icon = isTTY ? "✓ " : "";
10356
+ process.stdout.write(isTTY ? source_default.green(`${icon}${text}
10357
+ `) : `${icon}${text}
10358
+ `);
10359
+ },
10360
+ warning(text) {
10361
+ const icon = isTTY ? "⚠ " : "";
10362
+ process.stderr.write(isTTY ? source_default.yellow(`${icon}${text}
10363
+ `) : `${icon}${text}
10364
+ `);
10365
+ },
10366
+ error(text) {
10367
+ const icon = isTTY ? "✗ " : "";
10368
+ process.stderr.write(isTTY ? source_default.red(`${icon}${text}
10369
+ `) : `${icon}${text}
10370
+ `);
10371
+ },
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
+ `);
10126
10406
  },
10127
- color: {
10128
- black: [30, 39],
10129
- red: [31, 39],
10130
- green: [32, 39],
10131
- yellow: [33, 39],
10132
- blue: [34, 39],
10133
- magenta: [35, 39],
10134
- cyan: [36, 39],
10135
- white: [37, 39],
10136
- blackBright: [90, 39],
10137
- gray: [90, 39],
10138
- grey: [90, 39],
10139
- redBright: [91, 39],
10140
- greenBright: [92, 39],
10141
- yellowBright: [93, 39],
10142
- blueBright: [94, 39],
10143
- magentaBright: [95, 39],
10144
- cyanBright: [96, 39],
10145
- whiteBright: [97, 39]
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
+ });
10418
+ });
10146
10419
  },
10147
- bgColor: {
10148
- bgBlack: [40, 49],
10149
- bgRed: [41, 49],
10150
- bgGreen: [42, 49],
10151
- bgYellow: [43, 49],
10152
- bgBlue: [44, 49],
10153
- bgMagenta: [45, 49],
10154
- bgCyan: [46, 49],
10155
- bgWhite: [47, 49],
10156
- bgBlackBright: [100, 49],
10157
- bgGray: [100, 49],
10158
- bgGrey: [100, 49],
10159
- bgRedBright: [101, 49],
10160
- bgGreenBright: [102, 49],
10161
- bgYellowBright: [103, 49],
10162
- bgBlueBright: [104, 49],
10163
- bgMagentaBright: [105, 49],
10164
- bgCyanBright: [106, 49],
10165
- bgWhiteBright: [107, 49]
10166
- }
10167
- };
10168
- var modifierNames = Object.keys(styles.modifier);
10169
- var foregroundColorNames = Object.keys(styles.color);
10170
- var backgroundColorNames = Object.keys(styles.bgColor);
10171
- var colorNames = [...foregroundColorNames, ...backgroundColorNames];
10172
- function assembleStyles() {
10173
- const codes = new Map;
10174
- for (const [groupName, group] of Object.entries(styles)) {
10175
- for (const [styleName, style] of Object.entries(group)) {
10176
- styles[styleName] = {
10177
- open: `\x1B[${style[0]}m`,
10178
- close: `\x1B[${style[1]}m`
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();
10179
10448
  };
10180
- group[styleName] = styles[styleName];
10181
- codes.set(style[0], style[1]);
10182
- }
10183
- Object.defineProperty(styles, groupName, {
10184
- value: group,
10185
- enumerable: false
10186
- });
10187
- }
10188
- Object.defineProperty(styles, "codes", {
10189
- value: codes,
10190
- enumerable: false
10191
- });
10192
- styles.color.close = "\x1B[39m";
10193
- styles.bgColor.close = "\x1B[49m";
10194
- styles.color.ansi = wrapAnsi16();
10195
- styles.color.ansi256 = wrapAnsi256();
10196
- styles.color.ansi16m = wrapAnsi16m();
10197
- styles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET);
10198
- styles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET);
10199
- styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET);
10200
- Object.defineProperties(styles, {
10201
- rgbToAnsi256: {
10202
- value(red, green, blue) {
10203
- if (red === green && green === blue) {
10204
- if (red < 8) {
10205
- return 16;
10206
- }
10207
- if (red > 248) {
10208
- return 231;
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
+ `);
10209
10510
  }
10210
- return Math.round((red - 8) / 247 * 24) + 232;
10211
10511
  }
10212
- return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green / 255 * 5) + Math.round(blue / 255 * 5);
10213
- },
10214
- enumerable: false
10215
- },
10216
- hexToRgb: {
10217
- value(hex) {
10218
- const matches = /[a-f\d]{6}|[a-f\d]{3}/i.exec(hex.toString(16));
10219
- if (!matches) {
10220
- 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;
10221
10537
  }
10222
- let [colorString] = matches;
10223
- if (colorString.length === 3) {
10224
- 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;
10225
10580
  }
10226
- const integer = Number.parseInt(colorString, 16);
10227
- return [
10228
- integer >> 16 & 255,
10229
- integer >> 8 & 255,
10230
- integer & 255
10231
- ];
10232
- },
10233
- enumerable: false
10234
- },
10235
- hexToAnsi256: {
10236
- value: (hex) => styles.rgbToAnsi256(...styles.hexToRgb(hex)),
10237
- enumerable: false
10238
- },
10239
- ansi256ToAnsi: {
10240
- value(code) {
10241
- if (code < 8) {
10242
- return 30 + code;
10581
+ if (value === "\x04") {
10582
+ submit();
10583
+ return;
10243
10584
  }
10244
- if (code < 16) {
10245
- return 90 + (code - 8);
10585
+ if (value === "\x01") {
10586
+ const { line } = getCursorPosition();
10587
+ cursor = getLineStart(line);
10588
+ renderBuffer();
10589
+ return;
10246
10590
  }
10247
- let red;
10248
- let green;
10249
- let blue;
10250
- if (code >= 232) {
10251
- red = ((code - 232) * 10 + 8) / 255;
10252
- green = red;
10253
- blue = red;
10254
- } else {
10255
- code -= 16;
10256
- const remainder = code % 36;
10257
- red = Math.floor(code / 36) / 5;
10258
- green = Math.floor(remainder / 6) / 5;
10259
- blue = remainder % 6 / 5;
10591
+ if (value === "\x05") {
10592
+ const { line } = getCursorPosition();
10593
+ cursor = getLineEnd(line);
10594
+ renderBuffer();
10595
+ return;
10260
10596
  }
10261
- const value = Math.max(red, green, blue) * 2;
10262
- if (value === 0) {
10263
- 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;
10264
10604
  }
10265
- let result = 30 + (Math.round(blue) << 2 | Math.round(green) << 1 | Math.round(red));
10266
- if (value === 2) {
10267
- 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;
10268
10620
  }
10269
- return result;
10270
- },
10271
- enumerable: false
10272
- },
10273
- rgbToAnsi: {
10274
- value: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)),
10275
- enumerable: false
10276
- },
10277
- hexToAnsi: {
10278
- value: (hex) => styles.ansi256ToAnsi(styles.hexToAnsi256(hex)),
10279
- enumerable: false
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
+ });
10710
+ }
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;
10280
10720
  }
10281
- });
10282
- return styles;
10721
+ throw error;
10722
+ }
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;
10283
10743
  }
10284
- var ansiStyles = assembleStyles();
10285
- var ansi_styles_default = ansiStyles;
10286
10744
 
10287
- // node_modules/chalk/source/vendor/supports-color/index.js
10288
- import process2 from "node:process";
10289
- import os from "node:os";
10290
- import tty2 from "node:tty";
10291
- function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process2.argv) {
10292
- const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
10293
- const position = argv.indexOf(prefix + flag);
10294
- const terminatorPosition = argv.indexOf("--");
10295
- return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);
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;
10751
+ }
10752
+ return value.startsWith(partial);
10296
10753
  }
10297
- var { env } = process2;
10298
- var flagForceColor;
10299
- if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) {
10300
- flagForceColor = 0;
10301
- } else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) {
10302
- flagForceColor = 1;
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();
10303
10757
  }
10304
- function envForceColor() {
10305
- if ("FORCE_COLOR" in env) {
10306
- if (env.FORCE_COLOR === "true") {
10307
- return 1;
10308
- }
10309
- if (env.FORCE_COLOR === "false") {
10310
- return 0;
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;
10311
10764
  }
10312
- return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3);
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 {}
10313
10776
  }
10777
+ return workflows.filter((filePath) => matchesPartial(filePath, partial)).sort();
10314
10778
  }
10315
- function translateLevel(level) {
10316
- if (level === 0) {
10317
- return false;
10779
+
10780
+ // src/commands/complete.ts
10781
+ function parseTarget(value) {
10782
+ if (value === "run-id" || value === "workflow") {
10783
+ return value;
10318
10784
  }
10319
- return {
10320
- level,
10321
- hasBasic: true,
10322
- has256: level >= 2,
10323
- has16m: level >= 3
10324
- };
10785
+ throw new UserInputError(`Invalid completion target "${value}".`);
10325
10786
  }
10326
- function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
10327
- const noFlagForceColor = envForceColor();
10328
- if (noFlagForceColor !== undefined) {
10329
- flagForceColor = noFlagForceColor;
10330
- }
10331
- const forceColor = sniffFlags ? flagForceColor : noFlagForceColor;
10332
- if (forceColor === 0) {
10333
- return 0;
10334
- }
10335
- if (sniffFlags) {
10336
- if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) {
10337
- return 3;
10338
- }
10339
- if (hasFlag("color=256")) {
10340
- return 2;
10787
+
10788
+ class CompleteCommand extends BaseCommand {
10789
+ static paths = [["complete"]];
10790
+ target = exports_options.String({
10791
+ name: "target"
10792
+ });
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
+ `);
10341
10805
  }
10342
- }
10343
- if ("TF_BUILD" in env && "AGENT_NAME" in env) {
10344
- return 1;
10345
- }
10346
- if (haveStream && !streamIsTTY && forceColor === undefined) {
10347
10806
  return 0;
10348
10807
  }
10349
- const min = forceColor || 0;
10350
- if (env.TERM === "dumb") {
10351
- return min;
10352
- }
10353
- if (process2.platform === "win32") {
10354
- const osRelease = os.release().split(".");
10355
- if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
10356
- return Number(osRelease[2]) >= 14931 ? 3 : 2;
10357
- }
10358
- return 1;
10359
- }
10360
- if ("CI" in env) {
10361
- if (["GITHUB_ACTIONS", "GITEA_ACTIONS", "CIRCLECI"].some((key) => (key in env))) {
10362
- return 3;
10363
- }
10364
- if (["TRAVIS", "APPVEYOR", "GITLAB_CI", "BUILDKITE", "DRONE"].some((sign) => (sign in env)) || env.CI_NAME === "codeship") {
10365
- return 1;
10366
- }
10367
- return min;
10368
- }
10369
- if ("TEAMCITY_VERSION" in env) {
10370
- return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;
10371
- }
10372
- if (env.COLORTERM === "truecolor") {
10373
- return 3;
10374
- }
10375
- if (env.TERM === "xterm-kitty") {
10376
- return 3;
10377
- }
10378
- if (env.TERM === "xterm-ghostty") {
10379
- return 3;
10380
- }
10381
- if (env.TERM === "wezterm") {
10382
- return 3;
10383
- }
10384
- if ("TERM_PROGRAM" in env) {
10385
- const version = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
10386
- switch (env.TERM_PROGRAM) {
10387
- case "iTerm.app": {
10388
- return version >= 3 ? 3 : 2;
10389
- }
10390
- case "Apple_Terminal": {
10391
- return 2;
10392
- }
10393
- }
10394
- }
10395
- if (/-256(color)?$/i.test(env.TERM)) {
10396
- return 2;
10397
- }
10398
- if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {
10399
- return 1;
10400
- }
10401
- if ("COLORTERM" in env) {
10402
- return 1;
10403
- }
10404
- return min;
10405
- }
10406
- function createSupportsColor(stream, options = {}) {
10407
- const level = _supportsColor(stream, {
10408
- streamIsTTY: stream && stream.isTTY,
10409
- ...options
10410
- });
10411
- return translateLevel(level);
10412
10808
  }
10413
- var supportsColor = {
10414
- stdout: createSupportsColor({ isTTY: tty2.isatty(1) }),
10415
- stderr: createSupportsColor({ isTTY: tty2.isatty(2) })
10416
- };
10417
- var supports_color_default = supportsColor;
10418
10809
 
10419
- // node_modules/chalk/source/utilities.js
10420
- function stringReplaceAll(string, substring, replacer) {
10421
- let index = string.indexOf(substring);
10422
- if (index === -1) {
10423
- return string;
10810
+ // src/commands/completion.ts
10811
+ function parseShell(value) {
10812
+ if (value === "bash" || value === "zsh" || value === "fish") {
10813
+ return value;
10424
10814
  }
10425
- const substringLength = substring.length;
10426
- let endIndex = 0;
10427
- let returnValue = "";
10428
- do {
10429
- returnValue += string.slice(endIndex, index) + substring + replacer;
10430
- endIndex = index + substringLength;
10431
- index = string.indexOf(substring, endIndex);
10432
- } while (index !== -1);
10433
- returnValue += string.slice(endIndex);
10434
- return returnValue;
10815
+ throw new UserInputError(`Unsupported shell "${value}". Use bash, zsh, or fish.`);
10816
+ }
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
+ `);
10435
10879
  }
10436
- function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) {
10437
- let endIndex = 0;
10438
- let returnValue = "";
10439
- do {
10440
- const gotCR = string[index - 1] === "\r";
10441
- returnValue += string.slice(endIndex, gotCR ? index - 1 : index) + prefix + (gotCR ? `\r
10442
- ` : `
10443
- `) + postfix;
10444
- endIndex = index + 1;
10445
- index = string.indexOf(`
10446
- `, endIndex);
10447
- } while (index !== -1);
10448
- returnValue += string.slice(endIndex);
10449
- return returnValue;
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
+ `);
10450
10897
  }
10451
10898
 
10452
- // node_modules/chalk/source/index.js
10453
- var { stdout: stdoutColor, stderr: stderrColor } = supports_color_default;
10454
- var GENERATOR = Symbol("GENERATOR");
10455
- var STYLER = Symbol("STYLER");
10456
- var IS_EMPTY = Symbol("IS_EMPTY");
10457
- var levelMapping = [
10458
- "ansi",
10459
- "ansi",
10460
- "ansi256",
10461
- "ansi16m"
10462
- ];
10463
- var styles2 = Object.create(null);
10464
- var applyOptions = (object, options = {}) => {
10465
- if (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) {
10466
- throw new Error("The `level` option should be an integer from 0 to 3");
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;
10467
10920
  }
10468
- const colorLevel = stdoutColor ? stdoutColor.level : 0;
10469
- object.level = options.level === undefined ? colorLevel : options.level;
10470
- };
10471
- var chalkFactory = (options) => {
10472
- const chalk = (...strings) => strings.join(" ");
10473
- applyOptions(chalk, options);
10474
- Object.setPrototypeOf(chalk, createChalk.prototype);
10475
- return chalk;
10476
- };
10477
- function createChalk(options) {
10478
- return chalkFactory(options);
10479
10921
  }
10480
- Object.setPrototypeOf(createChalk.prototype, Function.prototype);
10481
- for (const [styleName, style] of Object.entries(ansi_styles_default)) {
10482
- styles2[styleName] = {
10483
- get() {
10484
- const builder = createBuilder(this, createStyler(style.open, style.close, this[STYLER]), this[IS_EMPTY]);
10485
- Object.defineProperty(this, styleName, { value: builder });
10486
- return builder;
10487
- }
10488
- };
10922
+
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");
10489
10928
  }
10490
- styles2.visible = {
10491
- get() {
10492
- const builder = createBuilder(this, this[STYLER], true);
10493
- Object.defineProperty(this, "visible", { value: builder });
10494
- return builder;
10495
- }
10496
- };
10497
- var getModelAnsi = (model, level, type, ...arguments_) => {
10498
- if (model === "rgb") {
10499
- if (level === "ansi16m") {
10500
- return ansi_styles_default[type].ansi16m(...arguments_);
10501
- }
10502
- if (level === "ansi256") {
10503
- return ansi_styles_default[type].ansi256(ansi_styles_default.rgbToAnsi256(...arguments_));
10504
- }
10505
- return ansi_styles_default[type].ansi(ansi_styles_default.rgbToAnsi(...arguments_));
10506
- }
10507
- if (model === "hex") {
10508
- return getModelAnsi("rgb", level, type, ...ansi_styles_default.hexToRgb(...arguments_));
10509
- }
10510
- return ansi_styles_default[type][model](...arguments_);
10511
- };
10512
- var usedModels = ["rgb", "hex", "ansi256"];
10513
- for (const model of usedModels) {
10514
- styles2[model] = {
10515
- get() {
10516
- const { level } = this;
10517
- return function(...arguments_) {
10518
- const styler = createStyler(getModelAnsi(model, levelMapping[level], "color", ...arguments_), ansi_styles_default.color.close, this[STYLER]);
10519
- return createBuilder(this, styler, this[IS_EMPTY]);
10520
- };
10521
- }
10522
- };
10523
- const bgModel = "bg" + model[0].toUpperCase() + model.slice(1);
10524
- styles2[bgModel] = {
10525
- get() {
10526
- const { level } = this;
10527
- return function(...arguments_) {
10528
- const styler = createStyler(getModelAnsi(model, levelMapping[level], "bgColor", ...arguments_), ansi_styles_default.bgColor.close, this[STYLER]);
10529
- return createBuilder(this, styler, this[IS_EMPTY]);
10530
- };
10531
- }
10532
- };
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`;
10533
10937
  }
10534
- var proto = Object.defineProperties(() => {}, {
10535
- ...styles2,
10536
- level: {
10537
- enumerable: true,
10538
- get() {
10539
- return this[GENERATOR].level;
10540
- },
10541
- set(level) {
10542
- this[GENERATOR].level = level;
10543
- }
10544
- }
10545
- });
10546
- var createStyler = (open, close, parent) => {
10547
- let openAll;
10548
- let closeAll;
10549
- if (parent === undefined) {
10550
- openAll = open;
10551
- closeAll = close;
10552
- } else {
10553
- openAll = parent.openAll + open;
10554
- closeAll = close + parent.closeAll;
10938
+ function runFilePath(config, runId) {
10939
+ return resolve3(config.runsDir, `${runId}.json`);
10940
+ }
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.");
10555
10945
  }
10556
10946
  return {
10557
- open,
10558
- close,
10559
- openAll,
10560
- closeAll,
10561
- 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()
10562
10962
  };
10563
- };
10564
- var createBuilder = (self, _styler, _isEmpty) => {
10565
- const builder = (...arguments_) => applyStyle(builder, arguments_.length === 1 ? "" + arguments_[0] : arguments_.join(" "));
10566
- Object.setPrototypeOf(builder, proto);
10567
- builder[GENERATOR] = self;
10568
- builder[STYLER] = _styler;
10569
- builder[IS_EMPTY] = _isEmpty;
10570
- return builder;
10571
- };
10572
- var applyStyle = (self, string) => {
10573
- if (self.level <= 0 || !string) {
10574
- return self[IS_EMPTY] ? "" : string;
10575
- }
10576
- let styler = self[STYLER];
10577
- if (styler === undefined) {
10578
- return string;
10579
- }
10580
- const { openAll, closeAll } = styler;
10581
- if (string.includes("\x1B")) {
10582
- while (styler !== undefined) {
10583
- string = stringReplaceAll(string, styler.close, styler.open);
10584
- 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}".`);
10981
+ }
10982
+ if (!Array.isArray(parsed.step_history)) {
10983
+ parsed.step_history = [];
10585
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}".`);
10586
10991
  }
10587
- const lfIndex = string.indexOf(`
10588
- `);
10589
- if (lfIndex !== -1) {
10590
- string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex);
10992
+ }
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;
11000
+ }
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}`);
10591
11008
  }
10592
- return openAll + string + closeAll;
10593
- };
10594
- Object.defineProperties(createChalk.prototype, styles2);
10595
- var chalk = createChalk();
10596
- var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
10597
- var source_default = chalk;
11009
+ }
11010
+ function composePrompt(promptFile, promptInline) {
11011
+ const parts = [];
11012
+ if (promptFile) {
11013
+ parts.push(promptFile.trimEnd());
11014
+ }
11015
+ if (promptInline) {
11016
+ parts.push(promptInline.trimEnd());
11017
+ }
11018
+ return parts.join(`
10598
11019
 
10599
- // src/lib/ui.ts
10600
- var isTTY = process.stdout.isTTY === true;
10601
- function getTerminalWidth() {
10602
- return process.stdout.columns ?? 80;
11020
+ `);
10603
11021
  }
10604
- function getBoxWidth() {
10605
- const termWidth = getTerminalWidth();
10606
- return Math.max(40, termWidth - 2);
11022
+
11023
+ // src/lib/harness-adapters.ts
11024
+ function createPassthroughParser() {
11025
+ return (line) => ({ text: line + `
11026
+ ` });
10607
11027
  }
10608
- function truncate(text, maxLength) {
10609
- if (text.length <= maxLength) {
10610
- return text;
11028
+ function withModelArgs(model, args) {
11029
+ if (!model) {
11030
+ return args;
10611
11031
  }
10612
- return text.slice(0, maxLength - 3) + "...";
11032
+ return [...args, "--model", model];
10613
11033
  }
10614
- function wrapText(text, maxWidth) {
10615
- if (text.length <= maxWidth) {
10616
- return [text];
10617
- }
10618
- const lines = [];
10619
- let remaining = text;
10620
- while (remaining.length > maxWidth) {
10621
- let breakAt = remaining.lastIndexOf(" ", maxWidth);
10622
- if (breakAt <= 0) {
10623
- breakAt = maxWidth;
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;
10624
11050
  }
10625
- lines.push(remaining.slice(0, breakAt));
10626
- remaining = remaining.slice(breakAt).trimStart();
10627
- }
10628
- if (remaining) {
10629
- lines.push(remaining);
10630
- }
10631
- return lines;
11051
+ let obj;
11052
+ try {
11053
+ obj = JSON.parse(line);
11054
+ } catch {
11055
+ return null;
11056
+ }
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 };
11074
+ }
11075
+ return null;
11076
+ };
10632
11077
  }
10633
- var ui = {
10634
- get isTTY() {
10635
- return isTTY;
10636
- },
10637
- workflowHeader(info) {
10638
- const line = isTTY ? "─" : "-";
10639
- const corner = {
10640
- tl: isTTY ? "╭" : "+",
10641
- tr: isTTY ? "╮" : "+",
10642
- bl: isTTY ? "╰" : "+",
10643
- br: isTTY ? "╯" : "+"
10644
- };
10645
- const width = getBoxWidth();
10646
- const contentWidth = width - 4;
10647
- const border = line.repeat(width - 2);
10648
- const labelWidth = 10;
10649
- const valueWidth = contentWidth - labelWidth;
10650
- const formatLine = (label, value) => {
10651
- const paddedLabel = label ? `${label}:`.padEnd(labelWidth) : " ".repeat(labelWidth);
10652
- const truncatedValue = truncate(value, valueWidth);
10653
- return `${paddedLabel}${truncatedValue}`.padEnd(contentWidth);
10654
- };
10655
- process.stdout.write(`
10656
- `);
10657
- process.stdout.write(isTTY ? source_default.cyan(`${corner.tl}${line} ${info.title} ${border.slice(info.title.length + 3)}${corner.tr}
10658
- `) : `${corner.tl}${line} ${info.title} ${border.slice(info.title.length + 3)}${corner.tr}
10659
- `);
10660
- process.stdout.write(isTTY ? source_default.dim(`│ ${formatLine("workflow", info.workflow)} │
10661
- `) : `│ ${formatLine("workflow", info.workflow)} │
10662
- `);
10663
- process.stdout.write(isTTY ? source_default.dim(`│ ${formatLine("run-id", info.runId)} │
10664
- `) : `│ ${formatLine("run-id", info.runId)} │
10665
- `);
10666
- process.stdout.write(isTTY ? source_default.dim(`│ ${formatLine("step", info.currentStep)} │
10667
- `) : `│ ${formatLine("step", info.currentStep)} │
10668
- `);
10669
- const taskLines = wrapText(info.task, valueWidth);
10670
- for (let i = 0;i < taskLines.length; i++) {
10671
- const label = i === 0 ? "task" : "";
10672
- const content = formatLine(label, taskLines[i] ?? "");
10673
- process.stdout.write(isTTY ? source_default.dim(`│ ${content} │
10674
- `) : `│ ${content} │
10675
- `);
11078
+ function createCodexStreamParser() {
11079
+ return (line) => {
11080
+ if (!line.trim()) {
11081
+ return null;
10676
11082
  }
10677
- process.stdout.write(isTTY ? source_default.cyan(`${corner.bl}${border}${corner.br}
10678
- `) : `${corner.bl}${border}${corner.br}
10679
- `);
10680
- process.stdout.write(`
10681
- `);
10682
- },
10683
- stepStart(stepNumber, stepId, agentId) {
10684
- const line = isTTY ? "─" : "-";
10685
- const corner = { tl: isTTY ? "┌" : "+", tr: isTTY ? "┐" : "+" };
10686
- const label = ` Step ${stepNumber}: ${stepId} (${agentId}) `;
10687
- const width = getBoxWidth();
10688
- const remaining = Math.max(0, width - label.length - 2);
10689
- const border = line.repeat(remaining);
10690
- process.stdout.write(`
10691
- `);
10692
- process.stdout.write(isTTY ? source_default.cyan.bold(`${corner.tl}${line}${label}${border}${corner.tr}
10693
- `) : `${corner.tl}${line}${label}${border}${corner.tr}
10694
- `);
10695
- process.stdout.write(`
10696
- `);
10697
- },
10698
- stepEnd() {
10699
- const line = isTTY ? "─" : "-";
10700
- const corner = { bl: isTTY ? "└" : "+", br: isTTY ? "┘" : "+" };
10701
- const width = getBoxWidth();
10702
- const border = line.repeat(width - 2);
10703
- process.stdout.write(`
10704
- `);
10705
- process.stdout.write(isTTY ? source_default.cyan(`${corner.bl}${border}${corner.br}
10706
- `) : `${corner.bl}${border}${corner.br}
10707
- `);
10708
- },
10709
- printToolCall(toolName, toolInput) {
10710
- const width = getBoxWidth();
10711
- const maxInputLength = width - 10;
10712
- const truncatedInput = truncate(toolInput, maxInputLength);
10713
- if (isTTY) {
10714
- process.stderr.write(source_default.cyan(` ${toolName} `) + source_default.dim(truncatedInput) + `
10715
- `);
10716
- } else {
10717
- process.stderr.write(` ${toolName} ${truncatedInput}
10718
- `);
11083
+ let obj;
11084
+ try {
11085
+ obj = JSON.parse(line);
11086
+ } catch {
11087
+ return null;
10719
11088
  }
10720
- },
10721
- stepOutputs(values) {
10722
- const entries = Object.entries(values);
10723
- if (entries.length === 0) {
10724
- return;
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;
10725
11093
  }
10726
- const width = getBoxWidth();
10727
- const labelPrefix = " ";
10728
- const separator = ": ";
10729
- process.stdout.write(`
10730
- `);
10731
- for (const [key, value] of entries) {
10732
- const label = `rmr:${key}`;
10733
- const firstLineIndent = labelPrefix.length + label.length + separator.length;
10734
- const continuationIndent = " ".repeat(firstLineIndent);
10735
- const maxValueWidth = width - firstLineIndent;
10736
- const valueLines = value.split(`
10737
- `);
10738
- const wrappedLines = [];
10739
- for (const vline of valueLines) {
10740
- if (vline.length <= maxValueWidth) {
10741
- wrappedLines.push(vline);
10742
- } else {
10743
- let remaining = vline;
10744
- while (remaining.length > maxValueWidth) {
10745
- let breakAt = remaining.lastIndexOf(" ", maxValueWidth);
10746
- if (breakAt <= 0) {
10747
- breakAt = maxValueWidth;
10748
- }
10749
- wrappedLines.push(remaining.slice(0, breakAt));
10750
- remaining = remaining.slice(breakAt).trimStart();
10751
- }
10752
- if (remaining) {
10753
- wrappedLines.push(remaining);
10754
- }
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;
10755
11146
  }
10756
11147
  }
10757
- const firstLine = wrappedLines[0] ?? "";
10758
- if (isTTY) {
10759
- process.stdout.write(source_default.cyan(`${labelPrefix}${label}`) + source_default.dim(`${separator}${firstLine}`) + `
10760
- `);
10761
- } else {
10762
- process.stdout.write(`${labelPrefix}${label}${separator}${firstLine}
10763
- `);
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;
10764
11158
  }
10765
- for (let i = 1;i < wrappedLines.length; i++) {
10766
- if (isTTY) {
10767
- process.stdout.write(source_default.dim(`${continuationIndent}${wrappedLines[i]}`) + `
10768
- `);
10769
- } else {
10770
- process.stdout.write(`${continuationIndent}${wrappedLines[i]}
10771
- `);
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;
10772
11171
  }
10773
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}`;
10774
11200
  }
10775
11201
  },
10776
- content(text) {
10777
- process.stdout.write(text);
10778
- },
10779
- success(text) {
10780
- const icon = isTTY ? "✓ " : "";
10781
- process.stdout.write(isTTY ? source_default.green(`${icon}${text}
10782
- `) : `${icon}${text}
10783
- `);
10784
- },
10785
- warning(text) {
10786
- const icon = isTTY ? "⚠ " : "";
10787
- process.stderr.write(isTTY ? source_default.yellow(`${icon}${text}
10788
- `) : `${icon}${text}
10789
- `);
10790
- },
10791
- error(text) {
10792
- const icon = isTTY ? "✗ " : "";
10793
- process.stderr.write(isTTY ? source_default.red(`${icon}${text}
10794
- `) : `${icon}${text}
10795
- `);
10796
- },
10797
- info(text) {
10798
- process.stdout.write(`${text}
10799
- `);
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
+ }
10800
11224
  },
10801
- dim(text) {
10802
- 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
+ }
10803
11241
  },
10804
- pauseInstructions(info) {
10805
- process.stderr.write(`
10806
- `);
10807
- ui.warning(`Paused: ${info.reason}`);
10808
- process.stderr.write(`
10809
- `);
10810
- process.stdout.write(isTTY ? source_default.dim(`Resume workflow:
10811
- `) : `Resume workflow:
10812
- `);
10813
- process.stdout.write(` rmr continue ${info.runId}
10814
- `);
10815
- process.stdout.write(`
10816
- `);
10817
- process.stdout.write(isTTY ? source_default.dim(`Resume agent session directly:
10818
- `) : `Resume agent session directly:
10819
- `);
10820
- process.stdout.write(` ${info.resumeCommand}
10821
- `);
10822
- process.stdout.write(`
10823
- `);
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
+ }
10824
11257
  }
10825
11258
  };
11259
+ function getHarnessAdapter(name) {
11260
+ const adapter = adapters[name];
11261
+ if (!adapter) {
11262
+ throw new ValidationError(`Unknown harness "${name}".`);
11263
+ }
11264
+ return adapter;
11265
+ }
10826
11266
 
10827
11267
  // src/lib/process-runner.ts
11268
+ import { spawn } from "node:child_process";
10828
11269
  function formatToolInput(toolInput) {
10829
11270
  try {
10830
11271
  const parsed = JSON.parse(toolInput);
@@ -10845,7 +11286,8 @@ function formatToolInput(toolInput) {
10845
11286
  async function runHarnessCommand(command, parseStreamLine) {
10846
11287
  return new Promise((resolve5, reject) => {
10847
11288
  const child = spawn(command.binary, command.args, {
10848
- stdio: ["ignore", "pipe", "pipe"]
11289
+ stdio: ["ignore", "pipe", "pipe"],
11290
+ ...command.env ? { env: { ...process.env, ...command.env } } : {}
10849
11291
  });
10850
11292
  child.on("error", (err) => {
10851
11293
  if (err.code === "ENOENT") {
@@ -10997,6 +11439,20 @@ function outputSnippet(output) {
10997
11439
  const compact = trimmed.replace(/\s+/g, " ");
10998
11440
  return compact.length > 220 ? `${compact.slice(0, 220)}...` : compact;
10999
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
+ }
11000
11456
  async function pauseRun(config, runState, reason, harnessName, sessionId) {
11001
11457
  runState.status = "paused_human";
11002
11458
  await saveRunState(config, runState);
@@ -11008,12 +11464,12 @@ async function pauseRun(config, runState, reason, harnessName, sessionId) {
11008
11464
  resumeCommand: adapter.resumeTemplate(resolvedSession)
11009
11465
  });
11010
11466
  }
11011
- function applyOutputToContext(context, agentId, values) {
11467
+ function applyOutputToContext(context, stepId, values) {
11012
11468
  for (const [key, value] of Object.entries(values)) {
11013
11469
  if (key === "status" || key === "next_state") {
11014
11470
  continue;
11015
11471
  }
11016
- context[`${agentId}.${key}`] = value;
11472
+ context[`${stepId}.${key}`] = value;
11017
11473
  }
11018
11474
  }
11019
11475
  async function runWorkflow(config, workflow, runState, options) {
@@ -11028,27 +11484,23 @@ async function runWorkflow(config, workflow, runState, options) {
11028
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);
11029
11485
  return runState;
11030
11486
  }
11031
- const agent = workflow.agents.find((item) => item.id === step.agent);
11032
- if (!agent) {
11033
- await pauseRun(config, runState, `Unknown agent "${step.agent}" for step "${step.id}".`, runState.last_harness?.name ?? "claude", runState.last_harness?.session_id ?? null);
11034
- return runState;
11035
- }
11036
11487
  const stepStartedAt = new Date().toISOString();
11037
- ui.stepStart(stepNumber, step.id, agent.id);
11038
11488
  try {
11039
- assertRequiredInputs(step.input_required, runState.context);
11040
- 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);
11041
11493
  const injectedHint = isFirstIteration && typeof options.overrides?.hint === "string" ? options.overrides.hint.trim() : "";
11042
- const renderedInput = injectedHint ? `${resolvedInput}
11494
+ const prompt = injectedHint ? `${resolvedPrompt}
11043
11495
 
11044
- Note: ${injectedHint}` : resolvedInput;
11045
- const agentPrompt = await loadAgentPrompt(runState.workflow_path, agent.prompt);
11046
- const prompt = composePrompt(agentPrompt, renderedInput);
11047
- const harness = options.overrides?.harness ?? agent.harness;
11496
+ Note: ${injectedHint}` : resolvedPrompt;
11497
+ const harness = step.harness;
11048
11498
  const adapter = getHarnessAdapter(harness);
11049
- const effectiveModel = options.overrides?.model ?? agent.model;
11499
+ const effectiveModel = step.model;
11050
11500
  const adapterOptions = typeof effectiveModel === "string" ? { allowAll: options.allowAll, model: effectiveModel } : { allowAll: options.allowAll };
11051
- 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;
11052
11504
  const command = isFirstIteration && selectedSessionId ? adapter.buildResumeCommand(selectedSessionId, prompt, {
11053
11505
  ...adapterOptions
11054
11506
  }) : adapter.buildRunCommand(prompt, {
@@ -11064,7 +11516,14 @@ Note: ${injectedHint}` : resolvedInput;
11064
11516
  runState.last_harness.session_id = result.sessionId;
11065
11517
  }
11066
11518
  if (result.exitCode !== 0) {
11067
- 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);
11068
11527
  return runState;
11069
11528
  }
11070
11529
  if (result.combinedOutput.includes(HUMAN_SENTINEL)) {
@@ -11074,15 +11533,15 @@ Note: ${injectedHint}` : resolvedInput;
11074
11533
  let stepOutput;
11075
11534
  try {
11076
11535
  stepOutput = parseRmrOutput(result.combinedOutput);
11077
- validateRequiredOutputKeys(stepOutput, step.outputs.required);
11536
+ validateRequiredOutputKeys(stepOutput, step.requires.outputs);
11078
11537
  } catch (error) {
11079
11538
  const message = error instanceof Error ? error.message : "Failed to parse step output.";
11080
- 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);
11081
11540
  return runState;
11082
11541
  }
11083
11542
  ui.stepOutputs(stepOutput.values);
11084
11543
  applyOutputToContext(runState.context, step.id, stepOutput.values);
11085
- const nextState = stepOutput.next_state ?? step.default_next;
11544
+ const nextState = stepOutput.next_state ?? step.next_step;
11086
11545
  if (!isValidTarget(workflow, nextState)) {
11087
11546
  await pauseRun(config, runState, `Invalid next_state "${nextState}" at step "${step.id}".`, harness, runState.last_harness.session_id);
11088
11547
  return runState;
@@ -11094,7 +11553,6 @@ Note: ${injectedHint}` : resolvedInput;
11094
11553
  const stepExecution = {
11095
11554
  step_number: stepNumber,
11096
11555
  step_id: step.id,
11097
- agent_id: agent.id,
11098
11556
  session_id: runState.last_harness?.session_id ?? null,
11099
11557
  started_at: stepStartedAt,
11100
11558
  completed_at: new Date().toISOString()
@@ -11114,25 +11572,13 @@ Note: ${injectedHint}` : resolvedInput;
11114
11572
  isFirstIteration = false;
11115
11573
  } catch (error) {
11116
11574
  const reason = error instanceof Error ? error.message : "Unknown execution error.";
11117
- 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);
11118
11576
  return runState;
11119
11577
  }
11120
11578
  }
11121
11579
  return runState;
11122
11580
  }
11123
11581
 
11124
- // src/lib/types.ts
11125
- var HARNESSES = ["claude", "opencode", "codex", "copilot"];
11126
- function parseHarnessOverride(value) {
11127
- if (!value) {
11128
- return;
11129
- }
11130
- if (!HARNESSES.includes(value)) {
11131
- throw new UserInputError(`Invalid harness override "${value}". Expected one of: ${HARNESSES.join(", ")}.`);
11132
- }
11133
- return value;
11134
- }
11135
-
11136
11582
  // src/lib/version.ts
11137
11583
  import { readFileSync } from "node:fs";
11138
11584
  import { dirname as dirname2, resolve as resolve5 } from "node:path";
@@ -11308,81 +11754,85 @@ async function loadWorkflowDefinition(workflowPath) {
11308
11754
  const id = ensureString(parsed.id, "id");
11309
11755
  const name = ensureString(parsed.name, "name");
11310
11756
  const version = typeof parsed.version === "string" ? parsed.version : undefined;
11311
- if (!Array.isArray(parsed.agents) || parsed.agents.length === 0) {
11312
- 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;
11313
11764
  }
11765
+ const topLevelModel = typeof parsed.model === "string" && parsed.model.trim() !== "" ? parsed.model.trim() : undefined;
11314
11766
  if (!Array.isArray(parsed.steps) || parsed.steps.length === 0) {
11315
11767
  throw new ValidationError("Workflow must define a non-empty steps array.");
11316
11768
  }
11317
- const agents = parsed.agents.map((rawAgent, index) => {
11318
- if (!rawAgent || typeof rawAgent !== "object") {
11319
- throw new ValidationError(`Invalid agents[${index}] definition.`);
11320
- }
11321
- const agent = rawAgent;
11322
- const agentId = ensureString(agent.id, `agents[${index}].id`);
11323
- const harness = ensureString(agent.harness, `agents[${index}].harness`);
11324
- if (!SUPPORTED_HARNESSES.has(harness)) {
11325
- 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.`);
11326
11772
  }
11327
- const prompt = ensureString(agent.prompt, `agents[${index}].prompt`);
11328
- 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`) : [];
11329
11796
  const normalized = {
11330
- id: agentId,
11797
+ id: stepId,
11331
11798
  harness,
11332
- prompt
11799
+ next_step: ensureString(step.next_step, `steps[${index}].next_step`),
11800
+ requires: {
11801
+ inputs: requiresInputs,
11802
+ outputs: requiresOutputs
11803
+ }
11333
11804
  };
11334
- if (typeof model === "string" && model.trim() !== "") {
11335
- return {
11336
- ...normalized,
11337
- model
11338
- };
11805
+ if (hasPromptFile) {
11806
+ normalized.prompt_file = step.prompt_file.trim();
11339
11807
  }
11340
- return normalized;
11341
- });
11342
- const steps = parsed.steps.map((rawStep, index) => {
11343
- if (!rawStep || typeof rawStep !== "object") {
11344
- throw new ValidationError(`Invalid steps[${index}] definition.`);
11808
+ if (hasPrompt) {
11809
+ normalized.prompt = step.prompt;
11345
11810
  }
11346
- const step = rawStep;
11347
- const outputs = step.outputs;
11348
- if (!outputs || typeof outputs !== "object") {
11349
- throw new ValidationError(`Invalid step field "steps[${index}].outputs": expected object.`);
11811
+ if (effectiveModel) {
11812
+ normalized.model = effectiveModel;
11350
11813
  }
11351
- return {
11352
- id: ensureString(step.id, `steps[${index}].id`),
11353
- agent: ensureString(step.agent, `steps[${index}].agent`),
11354
- default_next: ensureString(step.default_next, `steps[${index}].default_next`),
11355
- input_required: ensureStringArray(step.input_required, `steps[${index}].input_required`),
11356
- outputs: {
11357
- required: ensureStringArray(outputs.required, `steps[${index}].outputs.required`)
11358
- },
11359
- input: ensureString(step.input, `steps[${index}].input`)
11360
- };
11814
+ return normalized;
11361
11815
  });
11362
- validateUniqueness(agents.map((agent) => agent.id), "agent");
11363
11816
  validateUniqueness(steps.map((step) => step.id), "step");
11364
- const knownAgents = new Set(agents.map((agent) => agent.id));
11365
11817
  const knownSteps = new Set(steps.map((step) => step.id));
11366
11818
  for (const step of steps) {
11367
- if (!knownAgents.has(step.agent)) {
11368
- throw new ValidationError(`Unknown agent "${step.agent}" referenced by step "${step.id}".`);
11369
- }
11370
11819
  const validTransitionTargets = new Set([...knownSteps, "done", "human_intervention"]);
11371
- if (!validTransitionTargets.has(step.default_next)) {
11372
- 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}".`);
11373
11822
  }
11374
11823
  }
11375
11824
  return {
11376
11825
  id,
11377
11826
  name,
11378
11827
  ...version && { version },
11379
- agents,
11828
+ ...topLevelHarness && { harness: topLevelHarness },
11829
+ ...topLevelModel && { model: topLevelModel },
11380
11830
  steps
11381
11831
  };
11382
11832
  }
11383
11833
 
11384
11834
  // src/commands/continue.ts
11385
- class ContinueCommand extends Command {
11835
+ class ContinueCommand extends BaseCommand {
11386
11836
  static paths = [["continue"]];
11387
11837
  static usage = Command.Usage({
11388
11838
  category: "Workflow",
@@ -11396,8 +11846,8 @@ class ContinueCommand extends Command {
11396
11846
  '$0 continue 20260316-153210Z --hint "Plan mode only: read and propose changes, do not edit files."'
11397
11847
  ],
11398
11848
  [
11399
- "Force harness/session override",
11400
- "$0 continue 20260316-153210Z --harness claude --session-id abc123"
11849
+ "Force session override",
11850
+ "$0 continue 20260316-153210Z --session-id abc123"
11401
11851
  ]
11402
11852
  ]
11403
11853
  });
@@ -11408,10 +11858,6 @@ class ContinueCommand extends Command {
11408
11858
  required: false,
11409
11859
  description: "Override current step id before resuming."
11410
11860
  });
11411
- harness = exports_options.String("--harness", {
11412
- required: false,
11413
- description: "Override harness for the resumed step."
11414
- });
11415
11861
  sessionId = exports_options.String("--session-id", {
11416
11862
  required: false,
11417
11863
  description: "Force harness session id for resume attempt."
@@ -11420,35 +11866,36 @@ class ContinueCommand extends Command {
11420
11866
  required: false,
11421
11867
  description: "Inject a one-time hint into the resumed harness prompt."
11422
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
+ });
11423
11875
  async execute() {
11424
11876
  const showUpdateNotice = startUpdateCheck();
11425
11877
  const config = await loadConfig();
11426
11878
  const runState = await loadRunState(config, this.runId);
11427
11879
  const workflow = await loadWorkflowDefinition(runState.workflow_path);
11428
- const harnessOverride = parseHarnessOverride(this.harness);
11429
11880
  runState.status = "running";
11430
11881
  if (this.step) {
11431
11882
  runState.current_step = this.step;
11432
11883
  }
11884
+ const effectiveAllowAll = this.noAllowAll ? false : this.allowAll;
11433
11885
  ui.workflowHeader({
11434
- title: "rmr continue",
11886
+ title: `${binaryName} continue`,
11435
11887
  workflow: runState.workflow_path,
11436
11888
  workflowId: workflow.id,
11437
11889
  task: runState.context["task"] ?? "(continuing)",
11438
11890
  runId: this.runId,
11439
- currentStep: runState.current_step,
11440
11891
  runFile: "",
11441
- allowAll: true,
11442
- harness: this.harness,
11892
+ allowAll: effectiveAllowAll,
11443
11893
  varsCount: 0
11444
11894
  });
11445
11895
  const overrides = {};
11446
11896
  if (this.step) {
11447
11897
  overrides.stepId = this.step;
11448
11898
  }
11449
- if (harnessOverride) {
11450
- overrides.harness = harnessOverride;
11451
- }
11452
11899
  if (this.sessionId) {
11453
11900
  overrides.sessionId = this.sessionId;
11454
11901
  }
@@ -11456,7 +11903,7 @@ class ContinueCommand extends Command {
11456
11903
  overrides.hint = this.hint;
11457
11904
  }
11458
11905
  await runWorkflow(config, workflow, runState, {
11459
- allowAll: true,
11906
+ allowAll: effectiveAllowAll,
11460
11907
  overrides
11461
11908
  });
11462
11909
  showUpdateNotice();
@@ -11480,7 +11927,7 @@ function getExamplesWorkflowsDir() {
11480
11927
  return fromSrc;
11481
11928
  }
11482
11929
 
11483
- class InstallCommand extends Command {
11930
+ class InstallCommand extends BaseCommand {
11484
11931
  static paths = [["install"]];
11485
11932
  static usage = Command.Usage({
11486
11933
  category: "Setup",
@@ -11506,76 +11953,191 @@ class InstallCommand extends Command {
11506
11953
  }
11507
11954
  if (existsSync(destinationDir)) {
11508
11955
  ui.info(`Workflow already installed at .rmr/workflows/${this.workflowName}/`);
11509
- 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"`);
11510
11957
  return 0;
11511
11958
  }
11512
11959
  await cp(sourceDir, destinationDir, { recursive: true, force: false, errorOnExist: true });
11513
11960
  ui.success(`installed .rmr/workflows/${this.workflowName}/`);
11514
- 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
+ }
11515
12077
  return 0;
11516
12078
  }
11517
12079
  }
11518
12080
 
11519
12081
  // src/commands/root.ts
11520
- import { basename } from "node:path";
12082
+ import { basename as basename2 } from "node:path";
11521
12083
  function detectShell() {
11522
12084
  const raw = process.env.SHELL;
11523
12085
  if (!raw) {
11524
12086
  return null;
11525
12087
  }
11526
- const shell = basename(raw);
12088
+ const shell = basename2(raw);
11527
12089
  if (shell === "bash" || shell === "zsh" || shell === "fish") {
11528
12090
  return shell;
11529
12091
  }
11530
12092
  return null;
11531
12093
  }
11532
12094
 
11533
- class RootCommand extends Command {
12095
+ class RootCommand extends BaseCommand {
11534
12096
  static paths = [Command.Default];
11535
12097
  async execute() {
11536
12098
  const shell = detectShell();
11537
- 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
11538
12100
 
11539
12101
  `);
11540
12102
  process.stdout.write(`Setup
11541
12103
  `);
11542
- 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/
11543
12105
 
11544
12106
  `);
11545
12107
  process.stdout.write(`Workflow
11546
12108
  `);
11547
- 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)
11548
12110
  `);
11549
- 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
11550
12112
 
11551
12113
  `);
11552
12114
  process.stdout.write(`Shell Completion (optional)
11553
12115
  `);
11554
12116
  if (shell === "fish") {
11555
- process.stdout.write(` rmr completion fish > ~/.config/fish/completions/rmr.fish
12117
+ process.stdout.write(` ${binaryName} completion fish > ~/.config/fish/completions/${binaryName}.fish
11556
12118
 
11557
12119
  `);
11558
12120
  } else if (shell) {
11559
12121
  const rcFile = shell === "zsh" ? "~/.zshrc" : "~/.bashrc";
11560
- process.stdout.write(` echo 'eval "$(rmr completion ${shell})"' >> ${rcFile}
12122
+ process.stdout.write(` echo 'eval "$(${binaryName} completion ${shell})"' >> ${rcFile}
11561
12123
  `);
11562
12124
  process.stdout.write(` source ${rcFile}
11563
12125
 
11564
12126
  `);
11565
12127
  } else {
11566
- process.stdout.write(` echo 'eval "$(rmr completion zsh)"' >> ~/.zshrc && source ~/.zshrc
12128
+ process.stdout.write(` echo 'eval "$(${binaryName} completion zsh)"' >> ~/.zshrc && source ~/.zshrc
11567
12129
  `);
11568
- process.stdout.write(` echo 'eval "$(rmr completion bash)"' >> ~/.bashrc && source ~/.bashrc
12130
+ process.stdout.write(` echo 'eval "$(${binaryName} completion bash)"' >> ~/.bashrc && source ~/.bashrc
11569
12131
  `);
11570
- process.stdout.write(` rmr completion fish > ~/.config/fish/completions/rmr.fish
12132
+ process.stdout.write(` ${binaryName} completion fish > ~/.config/fish/completions/${binaryName}.fish
11571
12133
 
11572
12134
  `);
11573
12135
  }
11574
12136
  process.stdout.write(`More
11575
12137
  `);
11576
- process.stdout.write(` rmr --help Show full help with all options
12138
+ process.stdout.write(` ${binaryName} --help Show full help with all options
11577
12139
  `);
11578
- 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
11579
12141
  `);
11580
12142
  return 0;
11581
12143
  }
@@ -11617,8 +12179,8 @@ var logger = {
11617
12179
  };
11618
12180
 
11619
12181
  // src/commands/run.ts
11620
- import { readFile as readFile4 } from "node:fs/promises";
11621
- 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";
11622
12184
  function parseVar(input) {
11623
12185
  const index = input.indexOf("=");
11624
12186
  if (index < 1 || index === input.length - 1) {
@@ -11629,8 +12191,57 @@ function parseVar(input) {
11629
12191
  value: input.slice(index + 1).trim()
11630
12192
  };
11631
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
+ }
11632
12243
 
11633
- class RunCommand extends Command {
12244
+ class RunCommand extends BaseCommand {
11634
12245
  static paths = [["run"]];
11635
12246
  static usage = Command.Usage({
11636
12247
  category: "Workflow",
@@ -11642,14 +12253,6 @@ class RunCommand extends Command {
11642
12253
  '$0 run .rmr/workflows/feature-dev/workflow.yaml --task "Implement auth middleware"'
11643
12254
  ],
11644
12255
  ["Run with task file", "$0 run .rmr/workflows/feature-dev/workflow.yaml --task-file task.md"],
11645
- [
11646
- "Override harness",
11647
- '$0 run .rmr/workflows/feature-dev/workflow.yaml --task "Fix bug" --harness opencode'
11648
- ],
11649
- [
11650
- "Override model",
11651
- '$0 run .rmr/workflows/feature-dev/workflow.yaml --task "Fix bug" --model openai/gpt-5.3-codex-high'
11652
- ],
11653
12256
  [
11654
12257
  "Run with extra variables",
11655
12258
  '$0 run .rmr/workflows/feature-dev/workflow.yaml --task "Ship feature" --var issue_id=123 --var env=staging'
@@ -11671,14 +12274,6 @@ class RunCommand extends Command {
11671
12274
  required: false,
11672
12275
  description: "Path to file containing task description."
11673
12276
  });
11674
- harness = exports_options.String("--harness", {
11675
- required: false,
11676
- description: "Override harness for all workflow steps."
11677
- });
11678
- model = exports_options.String("--model", {
11679
- required: false,
11680
- description: "Override model for all workflow steps (e.g., openai/gpt-5.3-codex-high)."
11681
- });
11682
12277
  vars = exports_options.Array("--var", [], {
11683
12278
  description: "Inject initial variables as key=value (repeatable)."
11684
12279
  });
@@ -11694,7 +12289,7 @@ class RunCommand extends Command {
11694
12289
  }
11695
12290
  if (this.taskFile) {
11696
12291
  try {
11697
- const content = await readFile4(resolve8(this.taskFile), "utf-8");
12292
+ const content = await readFile4(resolve9(this.taskFile), "utf-8");
11698
12293
  const task = content.trim();
11699
12294
  return { task, displayTask: `(file: ${this.taskFile})` };
11700
12295
  } catch (error) {
@@ -11705,18 +12300,34 @@ class RunCommand extends Command {
11705
12300
  if (this.taskFlag) {
11706
12301
  return { task: this.taskFlag, displayTask: this.taskFlag };
11707
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
+ }
11708
12312
  throw new UserInputError("No task provided. Use --task/-t or --task-file/-f.");
11709
12313
  }
11710
12314
  async execute() {
11711
12315
  const showUpdateNotice = startUpdateCheck();
11712
12316
  const config = await loadConfig();
11713
- const { task, displayTask } = await this.resolveTask();
11714
- const harnessOverride = parseHarnessOverride(this.harness);
11715
12317
  const parsedVars = this.vars.map(parseVar);
11716
12318
  const effectiveAllowAll = this.noAllowAll ? false : this.allowAll;
11717
12319
  const varsObject = Object.fromEntries(parsedVars.map((entry) => [entry.key, entry.value]));
11718
- const workflowPath = resolve8(this.workflowPath);
11719
- 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();
11720
12331
  const runId = generateRunId();
11721
12332
  const runState = createInitialRunState({
11722
12333
  runId,
@@ -11727,28 +12338,17 @@ class RunCommand extends Command {
11727
12338
  });
11728
12339
  const runPath = await saveRunState(config, runState);
11729
12340
  ui.workflowHeader({
11730
- title: "rmr config",
12341
+ title: `${binaryName} config`,
11731
12342
  workflow: workflowPath,
11732
12343
  workflowId: workflow.id,
11733
12344
  task: displayTask,
11734
12345
  runId: runState.run_id,
11735
- currentStep: runState.current_step,
11736
12346
  runFile: runPath,
11737
12347
  allowAll: effectiveAllowAll,
11738
- harness: this.harness,
11739
- model: this.model,
11740
12348
  varsCount: parsedVars.length
11741
12349
  });
11742
- const overrides = {};
11743
- if (harnessOverride) {
11744
- overrides.harness = harnessOverride;
11745
- }
11746
- if (this.model) {
11747
- overrides.model = this.model;
11748
- }
11749
12350
  await runWorkflow(config, workflow, runState, {
11750
- allowAll: effectiveAllowAll,
11751
- ...Object.keys(overrides).length > 0 && { overrides }
12351
+ allowAll: effectiveAllowAll
11752
12352
  });
11753
12353
  showUpdateNotice();
11754
12354
  return 0;
@@ -11758,7 +12358,7 @@ class RunCommand extends Command {
11758
12358
  // src/index.ts
11759
12359
  var [, , ...args] = process.argv;
11760
12360
  var cli = new Cli({
11761
- binaryName: "rmr",
12361
+ binaryName,
11762
12362
  binaryVersion: getVersion(),
11763
12363
  enableColors: false
11764
12364
  });
@@ -11766,13 +12366,14 @@ cli.register(exports_builtins.HelpCommand);
11766
12366
  cli.register(exports_builtins.VersionCommand);
11767
12367
  cli.register(RootCommand);
11768
12368
  cli.register(InstallCommand);
12369
+ cli.register(ListCommand);
11769
12370
  cli.register(RunCommand);
11770
12371
  cli.register(ContinueCommand);
11771
12372
  cli.register(CompleteCommand);
11772
12373
  cli.register(CompletionCommand);
11773
12374
  try {
11774
12375
  const exitCode = await cli.run(args);
11775
- process.exitCode = exitCode;
12376
+ process.exitCode = Math.max(process.exitCode ?? 0, exitCode);
11776
12377
  } catch (error) {
11777
12378
  if (error instanceof RmrError) {
11778
12379
  logger.error(`${error.code}: ${error.message}`);