@klaudworks/rmr 0.4.5 → 1.0.1

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,1626 @@ 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}`;
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_);
10049
10048
  }
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}`;
10049
+ if (level === "ansi256") {
10050
+ return ansi_styles_default[type].ansi256(ansi_styles_default.rgbToAnsi256(...arguments_));
10064
10051
  }
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>"`;
10052
+ return ansi_styles_default[type].ansi(ansi_styles_default.rgbToAnsi(...arguments_));
10053
+ }
10054
+ if (model === "hex") {
10055
+ return getModelAnsi("rgb", level, type, ...ansi_styles_default.hexToRgb(...arguments_));
10056
+ }
10057
+ return ansi_styles_default[type][model](...arguments_);
10058
+ };
10059
+ var usedModels = ["rgb", "hex", "ansi256"];
10060
+ for (const model of usedModels) {
10061
+ styles2[model] = {
10062
+ get() {
10063
+ const { level } = this;
10064
+ return function(...arguments_) {
10065
+ const styler = createStyler(getModelAnsi(model, levelMapping[level], "color", ...arguments_), ansi_styles_default.color.close, this[STYLER]);
10066
+ return createBuilder(this, styler, this[IS_EMPTY]);
10067
+ };
10080
10068
  }
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) };
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;
10092
10087
  },
10093
- createStreamParser: createPassthroughParser,
10094
- resumeTemplate(sessionId) {
10095
- return `copilot --resume ${sessionId}`;
10088
+ set(level) {
10089
+ this[GENERATOR].level = level;
10096
10090
  }
10097
10091
  }
10098
- };
10099
- function getHarnessAdapter(name) {
10100
- const adapter = adapters[name];
10101
- if (!adapter) {
10102
- throw new ValidationError(`Unknown harness "${name}".`);
10103
- }
10104
- return adapter;
10105
- }
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;
10106
10145
 
10107
- // src/lib/process-runner.ts
10108
- import { spawn } from "node:child_process";
10146
+ // src/lib/ui.ts
10147
+ import * as readline from "node:readline";
10109
10148
 
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]
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;
10808
+ }
10809
+
10810
+ // src/commands/completion.ts
10811
+ function parseShell(value) {
10812
+ if (value === "bash" || value === "zsh" || value === "fish") {
10813
+ return value;
10403
10814
  }
10404
- return min;
10815
+ throw new UserInputError(`Unsupported shell "${value}". Use bash, zsh, or fish.`);
10405
10816
  }
10406
- function createSupportsColor(stream, options = {}) {
10407
- const level = _supportsColor(stream, {
10408
- streamIsTTY: stream && stream.isTTY,
10409
- ...options
10817
+ function bashScript() {
10818
+ return [
10819
+ "_rex_complete() {",
10820
+ " local cur prev",
10821
+ " COMPREPLY=()",
10822
+ ' cur="${COMP_WORDS[COMP_CWORD]}"',
10823
+ ' prev="${COMP_WORDS[COMP_CWORD-1]}"',
10824
+ "",
10825
+ " if [[ ${COMP_CWORD} -eq 1 ]]; then",
10826
+ ' COMPREPLY=( $(compgen -W "install run continue complete completion --help --version" -- "${cur}") )',
10827
+ " return 0",
10828
+ " fi",
10829
+ "",
10830
+ ' if [[ "${prev}" == "continue" ]]; then',
10831
+ ` COMPREPLY=( $(${binaryName} complete run-id "\${cur}") )`,
10832
+ " return 0",
10833
+ " fi",
10834
+ "",
10835
+ ' if [[ "${prev}" == "run" ]]; then',
10836
+ ` COMPREPLY=( $(${binaryName} complete workflow "\${cur}") )`,
10837
+ " return 0",
10838
+ " fi",
10839
+ "",
10840
+ ' if [[ "${prev}" == "install" ]]; then',
10841
+ ' COMPREPLY=( $(compgen -W "feature-dev" -- "${cur}") )',
10842
+ " return 0",
10843
+ " fi",
10844
+ "}",
10845
+ `complete -F _rex_complete ${binaryName}`
10846
+ ].join(`
10847
+ `);
10848
+ }
10849
+ function zshScript() {
10850
+ return [
10851
+ `#compdef ${binaryName}`,
10852
+ "_rex_complete() {",
10853
+ " local -a subcommands",
10854
+ " subcommands=(install run continue complete completion)",
10855
+ "",
10856
+ " if (( CURRENT == 2 )); then",
10857
+ " _describe 'command' subcommands",
10858
+ " return",
10859
+ " fi",
10860
+ "",
10861
+ " if [[ ${words[2]} == continue && $CURRENT -eq 3 ]]; then",
10862
+ ` compadd -- $(${binaryName} complete run-id "\${words[CURRENT]}")`,
10863
+ " return",
10864
+ " fi",
10865
+ "",
10866
+ " if [[ ${words[2]} == run && $CURRENT -eq 3 ]]; then",
10867
+ ` compadd -- $(${binaryName} complete workflow "\${words[CURRENT]}")`,
10868
+ " return",
10869
+ " fi",
10870
+ "",
10871
+ " if [[ ${words[2]} == install && $CURRENT -eq 3 ]]; then",
10872
+ " compadd -- feature-dev",
10873
+ " return",
10874
+ " fi",
10875
+ "}",
10876
+ `compdef _rex_complete ${binaryName}`
10877
+ ].join(`
10878
+ `);
10879
+ }
10880
+ function fishScript() {
10881
+ return [
10882
+ "function __rex_complete_run_id",
10883
+ ` ${binaryName} complete run-id (commandline -ct)`,
10884
+ "end",
10885
+ "",
10886
+ "function __rex_complete_workflow",
10887
+ ` ${binaryName} complete workflow (commandline -ct)`,
10888
+ "end",
10889
+ "",
10890
+ `complete -c ${binaryName} -f`,
10891
+ `complete -c ${binaryName} -n '__fish_use_subcommand' -a 'install run continue complete completion'`,
10892
+ `complete -c ${binaryName} -n '__fish_seen_subcommand_from continue' -a '(__rex_complete_run_id)'`,
10893
+ `complete -c ${binaryName} -n '__fish_seen_subcommand_from run' -a '(__rex_complete_workflow)'`,
10894
+ `complete -c ${binaryName} -n '__fish_seen_subcommand_from install' -a 'feature-dev'`
10895
+ ].join(`
10896
+ `);
10897
+ }
10898
+
10899
+ class CompletionCommand extends BaseCommand {
10900
+ static paths = [["completion"]];
10901
+ static usage = Command.Usage({
10902
+ category: "Workflow",
10903
+ description: "Print optional shell completion setup script.",
10904
+ details: "Generates completion script text for your shell. Source the output in your shell profile to enable command and dynamic argument completion.",
10905
+ examples: [
10906
+ ["Show Bash completion script", "$0 completion bash"],
10907
+ ["Show Zsh completion script", "$0 completion zsh"],
10908
+ ["Show Fish completion script", "$0 completion fish"]
10909
+ ]
10410
10910
  });
10411
- return translateLevel(level);
10911
+ shell = exports_options.String({
10912
+ name: "shell"
10913
+ });
10914
+ async execute() {
10915
+ const shell = parseShell(this.shell);
10916
+ const script = shell === "bash" ? bashScript() : shell === "zsh" ? zshScript() : fishScript();
10917
+ process.stdout.write(`${script}
10918
+ `);
10919
+ return 0;
10920
+ }
10412
10921
  }
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
10922
 
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;
10424
- }
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;
10923
+ // src/lib/run-state.ts
10924
+ import { appendFile, 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");
10435
10928
  }
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;
10929
+ function generateRunId(now = new Date) {
10930
+ const yyyy = now.getFullYear();
10931
+ const mm = pad(now.getMonth() + 1);
10932
+ const dd = pad(now.getDate());
10933
+ const hh = pad(now.getHours());
10934
+ const min = pad(now.getMinutes());
10935
+ const sec = pad(now.getSeconds());
10936
+ return `${yyyy}${mm}${dd}-${hh}${min}${sec}`;
10450
10937
  }
10451
-
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");
10467
- }
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);
10938
+ function runFilePath(config, runId) {
10939
+ return resolve3(config.runsDir, `${runId}.json`);
10479
10940
  }
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
- };
10941
+ function runLogPath(config, runId) {
10942
+ return resolve3(config.runsDir, `${runId}.log`);
10489
10943
  }
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
- };
10944
+ async function appendToRunLog(config, runId, content) {
10945
+ const path = runLogPath(config, runId);
10946
+ await appendFile(path, content, "utf8");
10947
+ return path;
10533
10948
  }
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;
10949
+ function createInitialRunState(options) {
10950
+ const firstStep = options.workflow.steps[0];
10951
+ if (!firstStep) {
10952
+ throw new StorageError("Cannot create run state without at least one workflow step.");
10555
10953
  }
10556
10954
  return {
10557
- open,
10558
- close,
10559
- openAll,
10560
- closeAll,
10561
- parent
10955
+ run_id: options.runId,
10956
+ workflow_path: options.workflowPath,
10957
+ status: "running",
10958
+ current_step: firstStep.id,
10959
+ context: {
10960
+ task: options.task,
10961
+ ...options.vars
10962
+ },
10963
+ last_harness: {
10964
+ name: firstStep.harness,
10965
+ binary: firstStep.harness,
10966
+ session_id: null
10967
+ },
10968
+ step_history: [],
10969
+ updated_at: new Date().toISOString()
10562
10970
  };
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;
10971
+ }
10972
+ async function saveRunState(config, state) {
10973
+ const path = runFilePath(config, state.run_id);
10974
+ const payload = JSON.stringify({
10975
+ ...state,
10976
+ updated_at: new Date().toISOString()
10977
+ }, null, 2);
10978
+ await writeFile(path, `${payload}
10979
+ `, "utf8");
10980
+ return path;
10981
+ }
10982
+ async function loadRunState(config, runId) {
10983
+ const path = runFilePath(config, runId);
10984
+ try {
10985
+ const raw = await readFile(path, "utf8");
10986
+ const parsed = JSON.parse(raw);
10987
+ if (!parsed || typeof parsed !== "object" || parsed.run_id !== runId) {
10988
+ throw new StorageError(`Run state file is invalid for run id "${runId}".`);
10585
10989
  }
10990
+ if (!Array.isArray(parsed.step_history)) {
10991
+ parsed.step_history = [];
10992
+ }
10993
+ return parsed;
10994
+ } catch (error) {
10995
+ if (error instanceof StorageError) {
10996
+ throw error;
10997
+ }
10998
+ throw new StorageError(`Failed to load run state for "${runId}".`);
10586
10999
  }
10587
- const lfIndex = string.indexOf(`
10588
- `);
10589
- if (lfIndex !== -1) {
10590
- string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex);
10591
- }
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;
10598
-
10599
- // src/lib/ui.ts
10600
- var isTTY = process.stdout.isTTY === true;
10601
- function getTerminalWidth() {
10602
- return process.stdout.columns ?? 80;
10603
11000
  }
10604
- function getBoxWidth() {
10605
- const termWidth = getTerminalWidth();
10606
- return Math.max(40, termWidth - 2);
11001
+
11002
+ // src/lib/prompt-composer.ts
11003
+ import { readFile as readFile2 } from "node:fs/promises";
11004
+ import { dirname, resolve as resolve4 } from "node:path";
11005
+ function stripFrontmatter(content) {
11006
+ const match = content.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/);
11007
+ return match ? content.slice(match[0].length) : content;
10607
11008
  }
10608
- function truncate(text, maxLength) {
10609
- if (text.length <= maxLength) {
10610
- return text;
11009
+ async function loadPromptFile(workflowPath, promptFileName) {
11010
+ const promptPath = resolve4(dirname(workflowPath), promptFileName);
11011
+ try {
11012
+ const raw = await readFile2(promptPath, "utf8");
11013
+ return stripFrontmatter(raw);
11014
+ } catch {
11015
+ throw new ConfigError(`Prompt file not found: ${promptPath}`);
10611
11016
  }
10612
- return text.slice(0, maxLength - 3) + "...";
10613
11017
  }
10614
- function wrapText(text, maxWidth) {
10615
- if (text.length <= maxWidth) {
10616
- return [text];
11018
+ function composePrompt(promptFile, promptInline) {
11019
+ const parts = [];
11020
+ if (promptFile) {
11021
+ parts.push(promptFile.trimEnd());
10617
11022
  }
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;
10624
- }
10625
- lines.push(remaining.slice(0, breakAt));
10626
- remaining = remaining.slice(breakAt).trimStart();
11023
+ if (promptInline) {
11024
+ parts.push(promptInline.trimEnd());
10627
11025
  }
10628
- if (remaining) {
10629
- lines.push(remaining);
11026
+ return parts.join(`
11027
+
11028
+ `);
11029
+ }
11030
+
11031
+ // src/lib/harness-adapters.ts
11032
+ function createPassthroughParser() {
11033
+ return (line) => ({ text: line + `
11034
+ ` });
11035
+ }
11036
+ function withModelArgs(model, args) {
11037
+ if (!model) {
11038
+ return args;
10630
11039
  }
10631
- return lines;
11040
+ return [...args, "--model", model];
10632
11041
  }
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
- `);
11042
+ function opencodePermissionEnv() {
11043
+ return {
11044
+ OPENCODE_PERMISSION: JSON.stringify({
11045
+ "*": "allow",
11046
+ external_directory: "allow",
11047
+ doom_loop: "allow"
11048
+ })
11049
+ };
11050
+ }
11051
+ function claudeStreamFlags() {
11052
+ return ["--output-format", "stream-json", "--verbose", "--include-partial-messages"];
11053
+ }
11054
+ function createOpenCodeStreamParser() {
11055
+ return (line) => {
11056
+ if (!line.trim()) {
11057
+ return null;
10676
11058
  }
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
- `);
11059
+ let obj;
11060
+ try {
11061
+ obj = JSON.parse(line);
11062
+ } catch {
11063
+ return null;
10719
11064
  }
10720
- },
10721
- stepOutputs(values) {
10722
- const entries = Object.entries(values);
10723
- if (entries.length === 0) {
10724
- return;
11065
+ const type = obj.type;
11066
+ const sessionId = typeof obj.sessionID === "string" ? obj.sessionID : undefined;
11067
+ if (type === "text") {
11068
+ const part = obj.part;
11069
+ const text = typeof part?.text === "string" ? part.text : "";
11070
+ return { text, sessionId };
11071
+ }
11072
+ if (type === "tool_use") {
11073
+ const part = obj.part;
11074
+ const toolName = typeof part?.tool === "string" ? part.tool : undefined;
11075
+ const state = part?.state;
11076
+ const input = state?.input;
11077
+ const toolInput = input ? JSON.stringify(input) : undefined;
11078
+ return { text: "", sessionId, toolName, toolInput };
11079
+ }
11080
+ if (sessionId) {
11081
+ return { text: "", sessionId };
10725
11082
  }
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
- }
11083
+ return null;
11084
+ };
11085
+ }
11086
+ function createCodexStreamParser() {
11087
+ return (line) => {
11088
+ if (!line.trim()) {
11089
+ return null;
11090
+ }
11091
+ let obj;
11092
+ try {
11093
+ obj = JSON.parse(line);
11094
+ } catch {
11095
+ return null;
11096
+ }
11097
+ const type = obj.type;
11098
+ if (type === "thread.started") {
11099
+ const sessionId = typeof obj.thread_id === "string" ? obj.thread_id : undefined;
11100
+ return sessionId ? { text: "", sessionId } : null;
11101
+ }
11102
+ if (type === "item.completed") {
11103
+ const item = obj.item;
11104
+ if (!item) {
11105
+ return null;
11106
+ }
11107
+ if (item.type === "agent_message") {
11108
+ const text = typeof item.text === "string" ? item.text : "";
11109
+ return { text };
11110
+ }
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
+ if (type === "item.started") {
11118
+ const item = obj.item;
11119
+ if (item?.type === "command_execution") {
11120
+ const command = typeof item.command === "string" ? item.command : "shell";
11121
+ return { text: "", toolName: "shell", toolInput: command };
11122
+ }
11123
+ return null;
11124
+ }
11125
+ return null;
11126
+ };
11127
+ }
11128
+ function createClaudeStreamParser() {
11129
+ let currentToolName = null;
11130
+ let currentToolInput = "";
11131
+ let currentBlockIndex = null;
11132
+ return (line) => {
11133
+ if (!line.trim()) {
11134
+ return null;
11135
+ }
11136
+ let obj;
11137
+ try {
11138
+ obj = JSON.parse(line);
11139
+ } catch {
11140
+ return null;
11141
+ }
11142
+ const type = obj.type;
11143
+ const sessionId = typeof obj.session_id === "string" ? obj.session_id : undefined;
11144
+ if (type === "stream_event") {
11145
+ const event = obj.event;
11146
+ const index = typeof event?.index === "number" ? event.index : null;
11147
+ if (event?.type === "content_block_start") {
11148
+ const block = event.content_block;
11149
+ if (block?.type === "tool_use" && typeof block.name === "string") {
11150
+ currentToolName = block.name;
11151
+ currentToolInput = "";
11152
+ currentBlockIndex = index;
11153
+ return sessionId ? { text: "", sessionId } : null;
10755
11154
  }
10756
11155
  }
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
- `);
11156
+ if (event?.type === "content_block_delta") {
11157
+ const delta = event.delta;
11158
+ if (delta?.type === "text_delta" && typeof delta.text === "string") {
11159
+ return { text: delta.text, sessionId };
11160
+ }
11161
+ if (delta?.type === "input_json_delta" && typeof delta.partial_json === "string") {
11162
+ currentToolInput += delta.partial_json;
11163
+ return sessionId ? { text: "", sessionId } : null;
11164
+ }
11165
+ return sessionId ? { text: "", sessionId } : null;
10764
11166
  }
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
- `);
11167
+ if (event?.type === "content_block_stop") {
11168
+ if (currentToolName && index === currentBlockIndex) {
11169
+ const result = {
11170
+ text: "",
11171
+ sessionId,
11172
+ toolName: currentToolName,
11173
+ toolInput: currentToolInput || undefined
11174
+ };
11175
+ currentToolName = null;
11176
+ currentToolInput = "";
11177
+ currentBlockIndex = null;
11178
+ return result;
10772
11179
  }
10773
11180
  }
11181
+ return sessionId ? { text: "", sessionId } : null;
11182
+ }
11183
+ if (type === "result") {
11184
+ return sessionId ? { text: "", sessionId } : null;
11185
+ }
11186
+ if (type === "system") {
11187
+ return sessionId ? { text: "", sessionId } : null;
11188
+ }
11189
+ return null;
11190
+ };
11191
+ }
11192
+ var adapters = {
11193
+ claude: {
11194
+ name: "claude",
11195
+ buildRunCommand(prompt, options) {
11196
+ const allowArgs = options.allowAll ? ["--dangerously-skip-permissions"] : [];
11197
+ const base = ["-p", prompt, ...claudeStreamFlags()];
11198
+ return { binary: "claude", args: withModelArgs(options.model, [...allowArgs, ...base]) };
11199
+ },
11200
+ buildResumeCommand(sessionId, prompt, options) {
11201
+ const allowArgs = options.allowAll ? ["--dangerously-skip-permissions"] : [];
11202
+ const base = [...allowArgs, "--resume", sessionId, "-p", prompt, ...claudeStreamFlags()];
11203
+ return { binary: "claude", args: withModelArgs(options.model, base) };
11204
+ },
11205
+ createStreamParser: createClaudeStreamParser,
11206
+ resumeTemplate(sessionId) {
11207
+ return `claude --resume ${sessionId}`;
10774
11208
  }
10775
11209
  },
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
- `);
11210
+ opencode: {
11211
+ name: "opencode",
11212
+ buildRunCommand(prompt, options) {
11213
+ const args = ["run", "--format", "json", prompt];
11214
+ return {
11215
+ binary: "opencode",
11216
+ args: withModelArgs(options.model, args),
11217
+ ...options.allowAll ? { env: opencodePermissionEnv() } : {}
11218
+ };
11219
+ },
11220
+ buildResumeCommand(sessionId, prompt, options) {
11221
+ const args = ["run", "--format", "json", "--session", sessionId, prompt];
11222
+ return {
11223
+ binary: "opencode",
11224
+ args: withModelArgs(options.model, args),
11225
+ ...options.allowAll ? { env: opencodePermissionEnv() } : {}
11226
+ };
11227
+ },
11228
+ createStreamParser: createOpenCodeStreamParser,
11229
+ resumeTemplate(sessionId) {
11230
+ return `opencode --session ${sessionId}`;
11231
+ }
10800
11232
  },
10801
- dim(text) {
10802
- process.stdout.write(isTTY ? source_default.gray(text) : text);
11233
+ codex: {
11234
+ name: "codex",
11235
+ buildRunCommand(prompt, options) {
11236
+ const auto = options.allowAll ? ["--dangerously-bypass-approvals-and-sandbox"] : [];
11237
+ const args = ["exec", "--json", ...auto, prompt];
11238
+ return { binary: "codex", args: withModelArgs(options.model, args) };
11239
+ },
11240
+ buildResumeCommand(sessionId, prompt, options) {
11241
+ const auto = options.allowAll ? ["--dangerously-bypass-approvals-and-sandbox"] : [];
11242
+ const args = ["exec", "resume", "--json", ...auto, sessionId, prompt];
11243
+ return { binary: "codex", args: withModelArgs(options.model, args) };
11244
+ },
11245
+ createStreamParser: createCodexStreamParser,
11246
+ resumeTemplate(sessionId) {
11247
+ return `codex resume ${sessionId}`;
11248
+ }
10803
11249
  },
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
- `);
11250
+ copilot: {
11251
+ name: "copilot",
11252
+ buildRunCommand(prompt, options) {
11253
+ const auto = options.allowAll ? ["--allow-all", "--no-ask-user"] : [];
11254
+ const args = [...auto, "-p", prompt];
11255
+ return { binary: "copilot", args: withModelArgs(options.model, args) };
11256
+ },
11257
+ buildResumeCommand(_sessionId, prompt, options) {
11258
+ const args = ["-p", prompt];
11259
+ return { binary: "copilot", args: withModelArgs(options.model, args) };
11260
+ },
11261
+ createStreamParser: createPassthroughParser,
11262
+ resumeTemplate(sessionId) {
11263
+ return `copilot --resume ${sessionId}`;
11264
+ }
10824
11265
  }
10825
11266
  };
11267
+ function getHarnessAdapter(name) {
11268
+ const adapter = adapters[name];
11269
+ if (!adapter) {
11270
+ throw new ValidationError(`Unknown harness "${name}".`);
11271
+ }
11272
+ return adapter;
11273
+ }
10826
11274
 
10827
11275
  // src/lib/process-runner.ts
11276
+ import { spawn } from "node:child_process";
10828
11277
  function formatToolInput(toolInput) {
10829
11278
  try {
10830
11279
  const parsed = JSON.parse(toolInput);
@@ -10845,7 +11294,8 @@ function formatToolInput(toolInput) {
10845
11294
  async function runHarnessCommand(command, parseStreamLine) {
10846
11295
  return new Promise((resolve5, reject) => {
10847
11296
  const child = spawn(command.binary, command.args, {
10848
- stdio: ["ignore", "pipe", "pipe"]
11297
+ stdio: ["ignore", "pipe", "pipe"],
11298
+ ...command.env ? { env: { ...process.env, ...command.env } } : {}
10849
11299
  });
10850
11300
  child.on("error", (err) => {
10851
11301
  if (err.code === "ENOENT") {
@@ -10997,6 +11447,20 @@ function outputSnippet(output) {
10997
11447
  const compact = trimmed.replace(/\s+/g, " ");
10998
11448
  return compact.length > 220 ? `${compact.slice(0, 220)}...` : compact;
10999
11449
  }
11450
+ function harnessExitReason(params) {
11451
+ const stderrSnippet = outputSnippet(params.stderr);
11452
+ const outputPreview = outputSnippet(params.combinedOutput);
11453
+ const details = [`harness=${params.harness}`];
11454
+ if (params.model) {
11455
+ details.push(`model=${params.model}`);
11456
+ }
11457
+ if (stderrSnippet !== "(no output)") {
11458
+ details.push(`stderr=${stderrSnippet}`);
11459
+ } else if (outputPreview !== "(no output)") {
11460
+ details.push(`output=${outputPreview}`);
11461
+ }
11462
+ return `Harness exited with code ${params.exitCode} at step "${params.stepId}" (${details.join("; ")}).`;
11463
+ }
11000
11464
  async function pauseRun(config, runState, reason, harnessName, sessionId) {
11001
11465
  runState.status = "paused_human";
11002
11466
  await saveRunState(config, runState);
@@ -11008,14 +11472,27 @@ async function pauseRun(config, runState, reason, harnessName, sessionId) {
11008
11472
  resumeCommand: adapter.resumeTemplate(resolvedSession)
11009
11473
  });
11010
11474
  }
11011
- function applyOutputToContext(context, agentId, values) {
11475
+ function applyOutputToContext(context, stepId, values) {
11012
11476
  for (const [key, value] of Object.entries(values)) {
11013
11477
  if (key === "status" || key === "next_state") {
11014
11478
  continue;
11015
11479
  }
11016
- context[`${agentId}.${key}`] = value;
11480
+ context[`${stepId}.${key}`] = value;
11017
11481
  }
11018
11482
  }
11483
+ function formatStepLogEntry(params) {
11484
+ const separator = `${"=".repeat(80)}
11485
+ `;
11486
+ const output = params.combinedOutput.length > 0 ? params.combinedOutput.endsWith(`
11487
+ `) ? params.combinedOutput : `${params.combinedOutput}
11488
+ ` : `
11489
+ `;
11490
+ return `${separator}STEP: ${params.stepId} (step ${params.stepNumber}) | Started: ${params.startedAt}
11491
+ ` + `${separator}` + `${output}
11492
+ ` + `${separator}STEP: ${params.stepId} (step ${params.stepNumber}) | Completed: ${params.completedAt} | Status: ${params.status}
11493
+ ` + `${separator}
11494
+ `;
11495
+ }
11019
11496
  async function runWorkflow(config, workflow, runState, options) {
11020
11497
  if (options.overrides?.stepId) {
11021
11498
  runState.current_step = options.overrides.stepId;
@@ -11028,27 +11505,23 @@ async function runWorkflow(config, workflow, runState, options) {
11028
11505
  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
11506
  return runState;
11030
11507
  }
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
11508
  const stepStartedAt = new Date().toISOString();
11037
- ui.stepStart(stepNumber, step.id, agent.id);
11038
11509
  try {
11039
- assertRequiredInputs(step.input_required, runState.context);
11040
- const resolvedInput = resolveTemplate(step.input, runState.context);
11510
+ assertRequiredInputs(step.requires.inputs, runState.context);
11511
+ const fileContent = step.prompt_file ? await loadPromptFile(runState.workflow_path, step.prompt_file) : undefined;
11512
+ const rawPrompt = composePrompt(fileContent, step.prompt);
11513
+ const resolvedPrompt = resolveTemplate(rawPrompt, runState.context);
11041
11514
  const injectedHint = isFirstIteration && typeof options.overrides?.hint === "string" ? options.overrides.hint.trim() : "";
11042
- const renderedInput = injectedHint ? `${resolvedInput}
11515
+ const prompt = injectedHint ? `${resolvedPrompt}
11043
11516
 
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;
11517
+ Note: ${injectedHint}` : resolvedPrompt;
11518
+ const harness = step.harness;
11048
11519
  const adapter = getHarnessAdapter(harness);
11049
- const effectiveModel = options.overrides?.model ?? agent.model;
11520
+ const effectiveModel = step.model;
11050
11521
  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;
11522
+ ui.stepStart(stepNumber, step.id, harness, effectiveModel);
11523
+ const lastSessionMatchesHarness = runState.last_harness?.name === harness ? runState.last_harness.session_id : null;
11524
+ const selectedSessionId = isFirstIteration && options.overrides?.sessionId ? options.overrides.sessionId : lastSessionMatchesHarness;
11052
11525
  const command = isFirstIteration && selectedSessionId ? adapter.buildResumeCommand(selectedSessionId, prompt, {
11053
11526
  ...adapterOptions
11054
11527
  }) : adapter.buildRunCommand(prompt, {
@@ -11064,40 +11537,100 @@ Note: ${injectedHint}` : resolvedInput;
11064
11537
  runState.last_harness.session_id = result.sessionId;
11065
11538
  }
11066
11539
  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);
11540
+ const completedAt = new Date().toISOString();
11541
+ await appendToRunLog(config, runState.run_id, formatStepLogEntry({
11542
+ stepId: step.id,
11543
+ stepNumber,
11544
+ startedAt: stepStartedAt,
11545
+ completedAt,
11546
+ status: "paused",
11547
+ combinedOutput: result.combinedOutput
11548
+ }));
11549
+ await pauseRun(config, runState, harnessExitReason({
11550
+ stepId: step.id,
11551
+ harness,
11552
+ exitCode: result.exitCode,
11553
+ stderr: result.stderr,
11554
+ combinedOutput: result.combinedOutput,
11555
+ ...effectiveModel ? { model: effectiveModel } : {}
11556
+ }), harness, runState.last_harness.session_id);
11068
11557
  return runState;
11069
11558
  }
11070
11559
  if (result.combinedOutput.includes(HUMAN_SENTINEL)) {
11560
+ const completedAt = new Date().toISOString();
11561
+ await appendToRunLog(config, runState.run_id, formatStepLogEntry({
11562
+ stepId: step.id,
11563
+ stepNumber,
11564
+ startedAt: stepStartedAt,
11565
+ completedAt,
11566
+ status: "paused",
11567
+ combinedOutput: result.combinedOutput
11568
+ }));
11071
11569
  await pauseRun(config, runState, `HUMAN_INTERVENTION_REQUIRED at step "${step.id}".`, harness, runState.last_harness.session_id);
11072
11570
  return runState;
11073
11571
  }
11074
11572
  let stepOutput;
11075
11573
  try {
11076
11574
  stepOutput = parseRmrOutput(result.combinedOutput);
11077
- validateRequiredOutputKeys(stepOutput, step.outputs.required);
11575
+ validateRequiredOutputKeys(stepOutput, step.requires.outputs);
11078
11576
  } catch (error) {
11079
11577
  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);
11578
+ const completedAt = new Date().toISOString();
11579
+ await appendToRunLog(config, runState.run_id, formatStepLogEntry({
11580
+ stepId: step.id,
11581
+ stepNumber,
11582
+ startedAt: stepStartedAt,
11583
+ completedAt,
11584
+ status: "paused",
11585
+ combinedOutput: result.combinedOutput
11586
+ }));
11587
+ await pauseRun(config, runState, message, harness, runState.last_harness.session_id);
11081
11588
  return runState;
11082
11589
  }
11083
11590
  ui.stepOutputs(stepOutput.values);
11084
11591
  applyOutputToContext(runState.context, step.id, stepOutput.values);
11085
- const nextState = stepOutput.next_state ?? step.default_next;
11592
+ const nextState = stepOutput.next_state ?? step.next_step;
11086
11593
  if (!isValidTarget(workflow, nextState)) {
11594
+ const completedAt = new Date().toISOString();
11595
+ await appendToRunLog(config, runState.run_id, formatStepLogEntry({
11596
+ stepId: step.id,
11597
+ stepNumber,
11598
+ startedAt: stepStartedAt,
11599
+ completedAt,
11600
+ status: "paused",
11601
+ combinedOutput: result.combinedOutput
11602
+ }));
11087
11603
  await pauseRun(config, runState, `Invalid next_state "${nextState}" at step "${step.id}".`, harness, runState.last_harness.session_id);
11088
11604
  return runState;
11089
11605
  }
11090
11606
  if (nextState === "human_intervention") {
11607
+ const completedAt = new Date().toISOString();
11608
+ await appendToRunLog(config, runState.run_id, formatStepLogEntry({
11609
+ stepId: step.id,
11610
+ stepNumber,
11611
+ startedAt: stepStartedAt,
11612
+ completedAt,
11613
+ status: "paused",
11614
+ combinedOutput: result.combinedOutput
11615
+ }));
11091
11616
  await pauseRun(config, runState, `Step "${step.id}" requested human intervention.`, harness, runState.last_harness.session_id);
11092
11617
  return runState;
11093
11618
  }
11619
+ const stepCompletedAt = new Date().toISOString();
11620
+ await appendToRunLog(config, runState.run_id, formatStepLogEntry({
11621
+ stepId: step.id,
11622
+ stepNumber,
11623
+ startedAt: stepStartedAt,
11624
+ completedAt: stepCompletedAt,
11625
+ status: "success",
11626
+ combinedOutput: result.combinedOutput
11627
+ }));
11094
11628
  const stepExecution = {
11095
11629
  step_number: stepNumber,
11096
11630
  step_id: step.id,
11097
- agent_id: agent.id,
11098
11631
  session_id: runState.last_harness?.session_id ?? null,
11099
11632
  started_at: stepStartedAt,
11100
- completed_at: new Date().toISOString()
11633
+ completed_at: stepCompletedAt
11101
11634
  };
11102
11635
  runState.step_history.push(stepExecution);
11103
11636
  stepNumber++;
@@ -11114,25 +11647,13 @@ Note: ${injectedHint}` : resolvedInput;
11114
11647
  isFirstIteration = false;
11115
11648
  } catch (error) {
11116
11649
  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);
11650
+ await pauseRun(config, runState, `${reason} (step "${step.id}")`, step.harness, runState.last_harness?.session_id ?? null);
11118
11651
  return runState;
11119
11652
  }
11120
11653
  }
11121
11654
  return runState;
11122
11655
  }
11123
11656
 
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
11657
  // src/lib/version.ts
11137
11658
  import { readFileSync } from "node:fs";
11138
11659
  import { dirname as dirname2, resolve as resolve5 } from "node:path";
@@ -11308,96 +11829,100 @@ async function loadWorkflowDefinition(workflowPath) {
11308
11829
  const id = ensureString(parsed.id, "id");
11309
11830
  const name = ensureString(parsed.name, "name");
11310
11831
  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.");
11832
+ let topLevelHarness;
11833
+ if (typeof parsed.harness === "string" && parsed.harness.trim() !== "") {
11834
+ const h = parsed.harness.trim();
11835
+ if (!SUPPORTED_HARNESSES.has(h)) {
11836
+ throw new ValidationError(`Unsupported top-level harness "${h}".`);
11837
+ }
11838
+ topLevelHarness = h;
11313
11839
  }
11840
+ const topLevelModel = typeof parsed.model === "string" && parsed.model.trim() !== "" ? parsed.model.trim() : undefined;
11314
11841
  if (!Array.isArray(parsed.steps) || parsed.steps.length === 0) {
11315
11842
  throw new ValidationError("Workflow must define a non-empty steps array.");
11316
11843
  }
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}".`);
11844
+ const steps = parsed.steps.map((rawStep, index) => {
11845
+ if (!rawStep || typeof rawStep !== "object") {
11846
+ throw new ValidationError(`Invalid steps[${index}] definition.`);
11326
11847
  }
11327
- const prompt = ensureString(agent.prompt, `agents[${index}].prompt`);
11328
- const model = agent.model;
11848
+ const step = rawStep;
11849
+ const stepId = ensureString(step.id, `steps[${index}].id`);
11850
+ let harness;
11851
+ if (typeof step.harness === "string" && step.harness.trim() !== "") {
11852
+ harness = step.harness.trim();
11853
+ if (!SUPPORTED_HARNESSES.has(harness)) {
11854
+ throw new ValidationError(`Unsupported harness "${harness}" for step "${stepId}".`);
11855
+ }
11856
+ } else if (topLevelHarness) {
11857
+ harness = topLevelHarness;
11858
+ } else {
11859
+ throw new ValidationError(`Step "${stepId}" has no harness and no top-level harness default is defined.`);
11860
+ }
11861
+ const stepModel = step.model;
11862
+ const effectiveModel = typeof stepModel === "string" && stepModel.trim() !== "" ? stepModel : topLevelModel;
11863
+ const hasPromptFile = typeof step.prompt_file === "string" && step.prompt_file.trim() !== "";
11864
+ const hasPrompt = typeof step.prompt === "string" && step.prompt.trim() !== "";
11865
+ if (!hasPromptFile && !hasPrompt) {
11866
+ throw new ValidationError(`Step "${stepId}" must define at least one of "prompt_file" or "prompt".`);
11867
+ }
11868
+ const requires = step.requires;
11869
+ const requiresInputs = requires && Array.isArray(requires.inputs) ? ensureStringArray(requires.inputs, `steps[${index}].requires.inputs`) : [];
11870
+ const requiresOutputs = requires && Array.isArray(requires.outputs) ? ensureStringArray(requires.outputs, `steps[${index}].requires.outputs`) : [];
11329
11871
  const normalized = {
11330
- id: agentId,
11872
+ id: stepId,
11331
11873
  harness,
11332
- prompt
11874
+ next_step: ensureString(step.next_step, `steps[${index}].next_step`),
11875
+ requires: {
11876
+ inputs: requiresInputs,
11877
+ outputs: requiresOutputs
11878
+ }
11333
11879
  };
11334
- if (typeof model === "string" && model.trim() !== "") {
11335
- return {
11336
- ...normalized,
11337
- model
11338
- };
11880
+ if (hasPromptFile) {
11881
+ normalized.prompt_file = step.prompt_file.trim();
11339
11882
  }
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.`);
11883
+ if (hasPrompt) {
11884
+ normalized.prompt = step.prompt;
11345
11885
  }
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.`);
11886
+ if (effectiveModel) {
11887
+ normalized.model = effectiveModel;
11350
11888
  }
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
- };
11889
+ return normalized;
11361
11890
  });
11362
- validateUniqueness(agents.map((agent) => agent.id), "agent");
11363
11891
  validateUniqueness(steps.map((step) => step.id), "step");
11364
- const knownAgents = new Set(agents.map((agent) => agent.id));
11365
11892
  const knownSteps = new Set(steps.map((step) => step.id));
11366
11893
  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
11894
  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}".`);
11895
+ if (!validTransitionTargets.has(step.next_step)) {
11896
+ throw new ValidationError(`Invalid next_step "${step.next_step}" in step "${step.id}".`);
11373
11897
  }
11374
11898
  }
11375
11899
  return {
11376
11900
  id,
11377
11901
  name,
11378
11902
  ...version && { version },
11379
- agents,
11903
+ ...topLevelHarness && { harness: topLevelHarness },
11904
+ ...topLevelModel && { model: topLevelModel },
11380
11905
  steps
11381
11906
  };
11382
11907
  }
11383
11908
 
11384
11909
  // src/commands/continue.ts
11385
- class ContinueCommand extends Command {
11910
+ class ContinueCommand extends BaseCommand {
11386
11911
  static paths = [["continue"]];
11387
11912
  static usage = Command.Usage({
11388
11913
  category: "Workflow",
11389
11914
  description: "Resume a previously created run by run id.",
11390
11915
  details: "Loads `.rmr/runs/<run-id>.json` and continues orchestration from the stored step unless overridden. If a harness session id exists (or is provided), rmr attempts harness resume first.",
11391
11916
  examples: [
11392
- ["Resume a paused run", "$0 continue 20260316-153210Z"],
11393
- ["Resume from a specific step", "$0 continue 20260316-153210Z --step verify"],
11917
+ ["Resume a paused run", "$0 continue 20260316-153210"],
11918
+ ["Resume from a specific step", "$0 continue 20260316-153210 --step verify"],
11394
11919
  [
11395
11920
  "Resume with a hint",
11396
- '$0 continue 20260316-153210Z --hint "Plan mode only: read and propose changes, do not edit files."'
11921
+ '$0 continue 20260316-153210 --hint "Plan mode only: read and propose changes, do not edit files."'
11397
11922
  ],
11398
11923
  [
11399
- "Force harness/session override",
11400
- "$0 continue 20260316-153210Z --harness claude --session-id abc123"
11924
+ "Force session override",
11925
+ "$0 continue 20260316-153210 --session-id abc123"
11401
11926
  ]
11402
11927
  ]
11403
11928
  });
@@ -11408,10 +11933,6 @@ class ContinueCommand extends Command {
11408
11933
  required: false,
11409
11934
  description: "Override current step id before resuming."
11410
11935
  });
11411
- harness = exports_options.String("--harness", {
11412
- required: false,
11413
- description: "Override harness for the resumed step."
11414
- });
11415
11936
  sessionId = exports_options.String("--session-id", {
11416
11937
  required: false,
11417
11938
  description: "Force harness session id for resume attempt."
@@ -11420,35 +11941,36 @@ class ContinueCommand extends Command {
11420
11941
  required: false,
11421
11942
  description: "Inject a one-time hint into the resumed harness prompt."
11422
11943
  });
11944
+ allowAll = exports_options.Boolean("--allow-all", true, {
11945
+ description: "Enable harness auto-approval flags when supported (default: true)."
11946
+ });
11947
+ noAllowAll = exports_options.Boolean("--no-allow-all", false, {
11948
+ description: "Disable harness auto-approval flags."
11949
+ });
11423
11950
  async execute() {
11424
11951
  const showUpdateNotice = startUpdateCheck();
11425
11952
  const config = await loadConfig();
11426
11953
  const runState = await loadRunState(config, this.runId);
11427
11954
  const workflow = await loadWorkflowDefinition(runState.workflow_path);
11428
- const harnessOverride = parseHarnessOverride(this.harness);
11429
11955
  runState.status = "running";
11430
11956
  if (this.step) {
11431
11957
  runState.current_step = this.step;
11432
11958
  }
11959
+ const effectiveAllowAll = this.noAllowAll ? false : this.allowAll;
11433
11960
  ui.workflowHeader({
11434
- title: "rmr continue",
11961
+ title: `${binaryName} continue`,
11435
11962
  workflow: runState.workflow_path,
11436
11963
  workflowId: workflow.id,
11437
11964
  task: runState.context["task"] ?? "(continuing)",
11438
11965
  runId: this.runId,
11439
- currentStep: runState.current_step,
11440
11966
  runFile: "",
11441
- allowAll: true,
11442
- harness: this.harness,
11967
+ allowAll: effectiveAllowAll,
11443
11968
  varsCount: 0
11444
11969
  });
11445
11970
  const overrides = {};
11446
11971
  if (this.step) {
11447
11972
  overrides.stepId = this.step;
11448
11973
  }
11449
- if (harnessOverride) {
11450
- overrides.harness = harnessOverride;
11451
- }
11452
11974
  if (this.sessionId) {
11453
11975
  overrides.sessionId = this.sessionId;
11454
11976
  }
@@ -11456,7 +11978,7 @@ class ContinueCommand extends Command {
11456
11978
  overrides.hint = this.hint;
11457
11979
  }
11458
11980
  await runWorkflow(config, workflow, runState, {
11459
- allowAll: true,
11981
+ allowAll: effectiveAllowAll,
11460
11982
  overrides
11461
11983
  });
11462
11984
  showUpdateNotice();
@@ -11480,7 +12002,7 @@ function getExamplesWorkflowsDir() {
11480
12002
  return fromSrc;
11481
12003
  }
11482
12004
 
11483
- class InstallCommand extends Command {
12005
+ class InstallCommand extends BaseCommand {
11484
12006
  static paths = [["install"]];
11485
12007
  static usage = Command.Usage({
11486
12008
  category: "Setup",
@@ -11506,76 +12028,191 @@ class InstallCommand extends Command {
11506
12028
  }
11507
12029
  if (existsSync(destinationDir)) {
11508
12030
  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"`);
12031
+ ui.info(`Run it with: ${binaryName} run .rmr/workflows/${this.workflowName}/workflow.yaml --task "Describe your task"`);
11510
12032
  return 0;
11511
12033
  }
11512
12034
  await cp(sourceDir, destinationDir, { recursive: true, force: false, errorOnExist: true });
11513
12035
  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"`);
12036
+ ui.info(`Run it with: ${binaryName} run .rmr/workflows/${this.workflowName}/workflow.yaml --task "Describe your task"`);
12037
+ return 0;
12038
+ }
12039
+ }
12040
+
12041
+ // src/commands/list.ts
12042
+ import { readdir as readdir3 } from "node:fs/promises";
12043
+ import { existsSync as existsSync2 } from "node:fs";
12044
+ import { dirname as dirname4, resolve as resolve8 } from "node:path";
12045
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
12046
+ function getExamplesWorkflowsDir2() {
12047
+ const thisDir = dirname4(fileURLToPath3(import.meta.url));
12048
+ const fromDist = resolve8(thisDir, "..", "examples", "workflows");
12049
+ const fromSrc = resolve8(thisDir, "..", "..", "examples", "workflows");
12050
+ if (existsSync2(fromDist))
12051
+ return fromDist;
12052
+ if (existsSync2(fromSrc))
12053
+ return fromSrc;
12054
+ return fromSrc;
12055
+ }
12056
+ async function getInstalledWorkflows(workflowsDir) {
12057
+ if (!existsSync2(workflowsDir)) {
12058
+ return [];
12059
+ }
12060
+ const entries = await readdir3(workflowsDir, { withFileTypes: true });
12061
+ const workflows = [];
12062
+ for (const entry of entries) {
12063
+ if (!entry.isDirectory())
12064
+ continue;
12065
+ const workflowPath = resolve8(workflowsDir, entry.name, "workflow.yaml");
12066
+ if (!existsSync2(workflowPath))
12067
+ continue;
12068
+ try {
12069
+ const workflow = await loadWorkflowDefinition(workflowPath);
12070
+ workflows.push({
12071
+ id: workflow.id,
12072
+ name: workflow.name,
12073
+ path: `.rmr/workflows/${entry.name}/workflow.yaml`
12074
+ });
12075
+ } catch {
12076
+ workflows.push({
12077
+ id: entry.name,
12078
+ name: "(invalid workflow.yaml)",
12079
+ path: `.rmr/workflows/${entry.name}/workflow.yaml`
12080
+ });
12081
+ }
12082
+ }
12083
+ return workflows.sort((a, b) => a.id.localeCompare(b.id));
12084
+ }
12085
+ async function getBundledWorkflows(examplesDir) {
12086
+ if (!existsSync2(examplesDir)) {
12087
+ return [];
12088
+ }
12089
+ const entries = await readdir3(examplesDir, { withFileTypes: true });
12090
+ const workflows = [];
12091
+ for (const entry of entries) {
12092
+ if (!entry.isDirectory())
12093
+ continue;
12094
+ const workflowPath = resolve8(examplesDir, entry.name, "workflow.yaml");
12095
+ if (!existsSync2(workflowPath))
12096
+ continue;
12097
+ try {
12098
+ const workflow = await loadWorkflowDefinition(workflowPath);
12099
+ workflows.push({
12100
+ id: workflow.id,
12101
+ name: workflow.name,
12102
+ path: entry.name
12103
+ });
12104
+ } catch {
12105
+ workflows.push({
12106
+ id: entry.name,
12107
+ name: "(invalid workflow.yaml)",
12108
+ path: entry.name
12109
+ });
12110
+ }
12111
+ }
12112
+ return workflows.sort((a, b) => a.id.localeCompare(b.id));
12113
+ }
12114
+
12115
+ class ListCommand extends BaseCommand {
12116
+ static paths = [["list"]];
12117
+ static usage = Command.Usage({
12118
+ category: "Setup",
12119
+ description: "List installed and available workflows.",
12120
+ details: "Shows workflows installed in `.rmr/workflows/` and bundled workflows available for installation.",
12121
+ examples: [["List all workflows", "$0 list"]]
12122
+ });
12123
+ async execute() {
12124
+ const config = await loadConfig();
12125
+ const examplesDir = getExamplesWorkflowsDir2();
12126
+ const installed = await getInstalledWorkflows(config.workflowsDir);
12127
+ const bundled = await getBundledWorkflows(examplesDir);
12128
+ const installedIds = new Set(installed.map((w) => w.id));
12129
+ if (installed.length > 0) {
12130
+ ui.info("Installed workflows:");
12131
+ for (const workflow of installed) {
12132
+ ui.info(` ${workflow.id.padEnd(16)} ${workflow.name.padEnd(24)} ${workflow.path}`);
12133
+ }
12134
+ ui.info("");
12135
+ }
12136
+ const available = bundled.filter((w) => !installedIds.has(w.id));
12137
+ if (available.length > 0) {
12138
+ ui.info("Available to install:");
12139
+ for (const workflow of available) {
12140
+ ui.info(` ${workflow.id.padEnd(16)} ${binaryName} install ${workflow.path}`);
12141
+ }
12142
+ ui.info("");
12143
+ }
12144
+ if (installed.length === 0 && bundled.length > 0) {
12145
+ ui.info("No workflows installed yet. Install one with:");
12146
+ ui.info(` ${binaryName} install ${bundled[0]?.path}`);
12147
+ ui.info("");
12148
+ }
12149
+ if (installed.length === 0 && bundled.length === 0) {
12150
+ ui.info("No workflows found.");
12151
+ }
11515
12152
  return 0;
11516
12153
  }
11517
12154
  }
11518
12155
 
11519
12156
  // src/commands/root.ts
11520
- import { basename } from "node:path";
12157
+ import { basename as basename2 } from "node:path";
11521
12158
  function detectShell() {
11522
12159
  const raw = process.env.SHELL;
11523
12160
  if (!raw) {
11524
12161
  return null;
11525
12162
  }
11526
- const shell = basename(raw);
12163
+ const shell = basename2(raw);
11527
12164
  if (shell === "bash" || shell === "zsh" || shell === "fish") {
11528
12165
  return shell;
11529
12166
  }
11530
12167
  return null;
11531
12168
  }
11532
12169
 
11533
- class RootCommand extends Command {
12170
+ class RootCommand extends BaseCommand {
11534
12171
  static paths = [Command.Default];
11535
12172
  async execute() {
11536
12173
  const shell = detectShell();
11537
- process.stdout.write(`rmr ${getVersion()} - multi-step coding workflows for AI agents
12174
+ process.stdout.write(`${binaryName} ${getVersion()} - multi-step coding workflows for AI agents
11538
12175
 
11539
12176
  `);
11540
12177
  process.stdout.write(`Setup
11541
12178
  `);
11542
- process.stdout.write(` rmr install <name> Install bundled workflow into .rmr/workflows/
12179
+ process.stdout.write(` ${binaryName} install <name> Install bundled workflow into .rmr/workflows/
11543
12180
 
11544
12181
  `);
11545
12182
  process.stdout.write(`Workflow
11546
12183
  `);
11547
- process.stdout.write(` rmr run <workflow-path> Start a new workflow run (requires --task/-t or --task-file/-f)
12184
+ process.stdout.write(` ${binaryName} run <workflow-path> Start a new workflow run (requires --task/-t or --task-file/-f)
11548
12185
  `);
11549
- process.stdout.write(` rmr continue <run-id> Resume a paused or interrupted run
12186
+ process.stdout.write(` ${binaryName} continue <run-id> Resume a paused or interrupted run
11550
12187
 
11551
12188
  `);
11552
12189
  process.stdout.write(`Shell Completion (optional)
11553
12190
  `);
11554
12191
  if (shell === "fish") {
11555
- process.stdout.write(` rmr completion fish > ~/.config/fish/completions/rmr.fish
12192
+ process.stdout.write(` ${binaryName} completion fish > ~/.config/fish/completions/${binaryName}.fish
11556
12193
 
11557
12194
  `);
11558
12195
  } else if (shell) {
11559
12196
  const rcFile = shell === "zsh" ? "~/.zshrc" : "~/.bashrc";
11560
- process.stdout.write(` echo 'eval "$(rmr completion ${shell})"' >> ${rcFile}
12197
+ process.stdout.write(` echo 'eval "$(${binaryName} completion ${shell})"' >> ${rcFile}
11561
12198
  `);
11562
12199
  process.stdout.write(` source ${rcFile}
11563
12200
 
11564
12201
  `);
11565
12202
  } else {
11566
- process.stdout.write(` echo 'eval "$(rmr completion zsh)"' >> ~/.zshrc && source ~/.zshrc
12203
+ process.stdout.write(` echo 'eval "$(${binaryName} completion zsh)"' >> ~/.zshrc && source ~/.zshrc
11567
12204
  `);
11568
- process.stdout.write(` echo 'eval "$(rmr completion bash)"' >> ~/.bashrc && source ~/.bashrc
12205
+ process.stdout.write(` echo 'eval "$(${binaryName} completion bash)"' >> ~/.bashrc && source ~/.bashrc
11569
12206
  `);
11570
- process.stdout.write(` rmr completion fish > ~/.config/fish/completions/rmr.fish
12207
+ process.stdout.write(` ${binaryName} completion fish > ~/.config/fish/completions/${binaryName}.fish
11571
12208
 
11572
12209
  `);
11573
12210
  }
11574
12211
  process.stdout.write(`More
11575
12212
  `);
11576
- process.stdout.write(` rmr --help Show full help with all options
12213
+ process.stdout.write(` ${binaryName} --help Show full help with all options
11577
12214
  `);
11578
- process.stdout.write(` rmr <command> --help Show help for a specific command
12215
+ process.stdout.write(` ${binaryName} <command> --help Show help for a specific command
11579
12216
  `);
11580
12217
  return 0;
11581
12218
  }
@@ -11617,8 +12254,8 @@ var logger = {
11617
12254
  };
11618
12255
 
11619
12256
  // src/commands/run.ts
11620
- import { readFile as readFile4 } from "node:fs/promises";
11621
- import { resolve as resolve8 } from "node:path";
12257
+ import { readFile as readFile4, stat } from "node:fs/promises";
12258
+ import { resolve as resolve9 } from "node:path";
11622
12259
  function parseVar(input) {
11623
12260
  const index = input.indexOf("=");
11624
12261
  if (index < 1 || index === input.length - 1) {
@@ -11629,8 +12266,57 @@ function parseVar(input) {
11629
12266
  value: input.slice(index + 1).trim()
11630
12267
  };
11631
12268
  }
12269
+ function getErrorMessage(error) {
12270
+ if (error instanceof Error) {
12271
+ return error.message;
12272
+ }
12273
+ if (typeof error === "string") {
12274
+ return error;
12275
+ }
12276
+ try {
12277
+ return JSON.stringify(error);
12278
+ } catch {
12279
+ return "Unknown error";
12280
+ }
12281
+ }
12282
+ function isErrnoLike(error) {
12283
+ return typeof error === "object" && error !== null;
12284
+ }
12285
+ async function resolveWorkflowPath(inputPath) {
12286
+ const absolutePath = resolve9(inputPath);
12287
+ try {
12288
+ const pathStat = await stat(absolutePath);
12289
+ if (!pathStat.isDirectory()) {
12290
+ return absolutePath;
12291
+ }
12292
+ const yamlPath = resolve9(absolutePath, "workflow.yaml");
12293
+ try {
12294
+ const yamlStat = await stat(yamlPath);
12295
+ if (yamlStat.isFile()) {
12296
+ return yamlPath;
12297
+ }
12298
+ } catch {}
12299
+ const ymlPath = resolve9(absolutePath, "workflow.yml");
12300
+ try {
12301
+ const ymlStat = await stat(ymlPath);
12302
+ if (ymlStat.isFile()) {
12303
+ return ymlPath;
12304
+ }
12305
+ } catch {}
12306
+ throw new UserInputError(`Workflow directory does not contain workflow.yaml or workflow.yml: ${absolutePath}`);
12307
+ } catch (error) {
12308
+ if (isErrnoLike(error) && error.code === "ENOENT") {
12309
+ const hint = absolutePath.includes(`${resolve9(".rmr")}/workflow/`) ? " Did you mean .rmr/workflows/?" : "";
12310
+ throw new UserInputError(`Workflow does not exist: ${absolutePath}${hint}`);
12311
+ }
12312
+ if (error instanceof UserInputError) {
12313
+ throw error;
12314
+ }
12315
+ throw new UserInputError(`Failed to access workflow path "${absolutePath}": ${getErrorMessage(error)}`);
12316
+ }
12317
+ }
11632
12318
 
11633
- class RunCommand extends Command {
12319
+ class RunCommand extends BaseCommand {
11634
12320
  static paths = [["run"]];
11635
12321
  static usage = Command.Usage({
11636
12322
  category: "Workflow",
@@ -11642,14 +12328,6 @@ class RunCommand extends Command {
11642
12328
  '$0 run .rmr/workflows/feature-dev/workflow.yaml --task "Implement auth middleware"'
11643
12329
  ],
11644
12330
  ["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
12331
  [
11654
12332
  "Run with extra variables",
11655
12333
  '$0 run .rmr/workflows/feature-dev/workflow.yaml --task "Ship feature" --var issue_id=123 --var env=staging'
@@ -11671,14 +12349,6 @@ class RunCommand extends Command {
11671
12349
  required: false,
11672
12350
  description: "Path to file containing task description."
11673
12351
  });
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
12352
  vars = exports_options.Array("--var", [], {
11683
12353
  description: "Inject initial variables as key=value (repeatable)."
11684
12354
  });
@@ -11694,7 +12364,7 @@ class RunCommand extends Command {
11694
12364
  }
11695
12365
  if (this.taskFile) {
11696
12366
  try {
11697
- const content = await readFile4(resolve8(this.taskFile), "utf-8");
12367
+ const content = await readFile4(resolve9(this.taskFile), "utf-8");
11698
12368
  const task = content.trim();
11699
12369
  return { task, displayTask: `(file: ${this.taskFile})` };
11700
12370
  } catch (error) {
@@ -11705,18 +12375,34 @@ class RunCommand extends Command {
11705
12375
  if (this.taskFlag) {
11706
12376
  return { task: this.taskFlag, displayTask: this.taskFlag };
11707
12377
  }
12378
+ if (process.stdin.isTTY) {
12379
+ ui.warning("No task provided. Enter your task below. Press Ctrl+D to submit.");
12380
+ const task = await ui.multilinePrompt("Task: ");
12381
+ const trimmedTask = task.trim();
12382
+ if (!trimmedTask) {
12383
+ throw new UserInputError("Task cannot be empty.");
12384
+ }
12385
+ return { task: trimmedTask, displayTask: trimmedTask };
12386
+ }
11708
12387
  throw new UserInputError("No task provided. Use --task/-t or --task-file/-f.");
11709
12388
  }
11710
12389
  async execute() {
11711
12390
  const showUpdateNotice = startUpdateCheck();
11712
12391
  const config = await loadConfig();
11713
- const { task, displayTask } = await this.resolveTask();
11714
- const harnessOverride = parseHarnessOverride(this.harness);
11715
12392
  const parsedVars = this.vars.map(parseVar);
11716
12393
  const effectiveAllowAll = this.noAllowAll ? false : this.allowAll;
11717
12394
  const varsObject = Object.fromEntries(parsedVars.map((entry) => [entry.key, entry.value]));
11718
- const workflowPath = resolve8(this.workflowPath);
11719
- const workflow = await loadWorkflowDefinition(workflowPath);
12395
+ const workflowPath = await resolveWorkflowPath(this.workflowPath);
12396
+ let workflow;
12397
+ try {
12398
+ workflow = await loadWorkflowDefinition(workflowPath);
12399
+ } catch (error) {
12400
+ if (isErrnoLike(error) && error.code === "ENOENT") {
12401
+ throw new UserInputError(`Workflow does not exist: ${workflowPath}`);
12402
+ }
12403
+ throw new UserInputError(`Failed to load workflow "${workflowPath}": ${getErrorMessage(error)}`);
12404
+ }
12405
+ const { task, displayTask } = await this.resolveTask();
11720
12406
  const runId = generateRunId();
11721
12407
  const runState = createInitialRunState({
11722
12408
  runId,
@@ -11727,28 +12413,17 @@ class RunCommand extends Command {
11727
12413
  });
11728
12414
  const runPath = await saveRunState(config, runState);
11729
12415
  ui.workflowHeader({
11730
- title: "rmr config",
12416
+ title: `${binaryName} config`,
11731
12417
  workflow: workflowPath,
11732
12418
  workflowId: workflow.id,
11733
12419
  task: displayTask,
11734
12420
  runId: runState.run_id,
11735
- currentStep: runState.current_step,
11736
12421
  runFile: runPath,
11737
12422
  allowAll: effectiveAllowAll,
11738
- harness: this.harness,
11739
- model: this.model,
11740
12423
  varsCount: parsedVars.length
11741
12424
  });
11742
- const overrides = {};
11743
- if (harnessOverride) {
11744
- overrides.harness = harnessOverride;
11745
- }
11746
- if (this.model) {
11747
- overrides.model = this.model;
11748
- }
11749
12425
  await runWorkflow(config, workflow, runState, {
11750
- allowAll: effectiveAllowAll,
11751
- ...Object.keys(overrides).length > 0 && { overrides }
12426
+ allowAll: effectiveAllowAll
11752
12427
  });
11753
12428
  showUpdateNotice();
11754
12429
  return 0;
@@ -11758,7 +12433,7 @@ class RunCommand extends Command {
11758
12433
  // src/index.ts
11759
12434
  var [, , ...args] = process.argv;
11760
12435
  var cli = new Cli({
11761
- binaryName: "rmr",
12436
+ binaryName,
11762
12437
  binaryVersion: getVersion(),
11763
12438
  enableColors: false
11764
12439
  });
@@ -11766,13 +12441,14 @@ cli.register(exports_builtins.HelpCommand);
11766
12441
  cli.register(exports_builtins.VersionCommand);
11767
12442
  cli.register(RootCommand);
11768
12443
  cli.register(InstallCommand);
12444
+ cli.register(ListCommand);
11769
12445
  cli.register(RunCommand);
11770
12446
  cli.register(ContinueCommand);
11771
12447
  cli.register(CompleteCommand);
11772
12448
  cli.register(CompletionCommand);
11773
12449
  try {
11774
12450
  const exitCode = await cli.run(args);
11775
- process.exitCode = exitCode;
12451
+ process.exitCode = Math.max(process.exitCode ?? 0, exitCode);
11776
12452
  } catch (error) {
11777
12453
  if (error instanceof RmrError) {
11778
12454
  logger.error(`${error.code}: ${error.message}`);