@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/LICENSE +21 -0
- package/README.md +147 -18
- package/dist/index.js +1905 -1229
- package/examples/workflows/feature-dev/planner-agent.md +4 -0
- package/examples/workflows/feature-dev/review-agent.md +8 -0
- package/examples/workflows/feature-dev/tackle-agent.md +6 -0
- package/examples/workflows/feature-dev/workflow.yaml +14 -52
- package/npm-shrinkwrap.json +2 -2
- package/package.json +2 -1
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
|
-
//
|
|
9678
|
-
|
|
9679
|
-
|
|
9680
|
-
|
|
9681
|
-
|
|
9682
|
-
|
|
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
|
-
|
|
9685
|
-
|
|
9686
|
-
|
|
9687
|
-
|
|
9688
|
-
|
|
9689
|
-
|
|
9690
|
-
|
|
9691
|
-
const
|
|
9692
|
-
|
|
9693
|
-
|
|
9694
|
-
|
|
9695
|
-
|
|
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
|
-
|
|
9698
|
-
|
|
9699
|
-
|
|
9700
|
-
|
|
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
|
-
|
|
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
|
-
|
|
9730
|
-
|
|
9731
|
-
|
|
9732
|
-
|
|
9733
|
-
|
|
9734
|
-
|
|
9735
|
-
|
|
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
|
-
|
|
9739
|
-
|
|
9828
|
+
});
|
|
9829
|
+
return styles;
|
|
9740
9830
|
}
|
|
9831
|
+
var ansiStyles = assembleStyles();
|
|
9832
|
+
var ansi_styles_default = ansiStyles;
|
|
9741
9833
|
|
|
9742
|
-
//
|
|
9743
|
-
|
|
9744
|
-
|
|
9745
|
-
|
|
9746
|
-
|
|
9747
|
-
|
|
9748
|
-
|
|
9749
|
-
|
|
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
|
-
|
|
9813
|
-
|
|
9814
|
-
|
|
9815
|
-
|
|
9816
|
-
|
|
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
|
-
|
|
9832
|
-
|
|
9833
|
-
|
|
9834
|
-
|
|
9835
|
-
|
|
9836
|
-
|
|
9837
|
-
|
|
9838
|
-
|
|
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
|
-
|
|
9856
|
-
|
|
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
|
-
|
|
9880
|
-
|
|
9881
|
-
|
|
9882
|
-
|
|
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
|
|
9897
|
-
const
|
|
9898
|
-
if (
|
|
9899
|
-
|
|
9873
|
+
function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
|
|
9874
|
+
const noFlagForceColor = envForceColor();
|
|
9875
|
+
if (noFlagForceColor !== undefined) {
|
|
9876
|
+
flagForceColor = noFlagForceColor;
|
|
9900
9877
|
}
|
|
9901
|
-
|
|
9902
|
-
|
|
9903
|
-
|
|
9904
|
-
|
|
9905
|
-
|
|
9906
|
-
|
|
9907
|
-
|
|
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
|
-
|
|
9925
|
-
|
|
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
|
-
|
|
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
|
-
|
|
9965
|
-
|
|
9966
|
-
|
|
9967
|
-
|
|
9968
|
-
|
|
9969
|
-
|
|
9970
|
-
|
|
9971
|
-
|
|
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
|
-
|
|
9978
|
-
|
|
9979
|
-
|
|
9980
|
-
|
|
9981
|
-
return
|
|
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
|
-
|
|
9984
|
-
|
|
9985
|
-
|
|
9986
|
-
|
|
9987
|
-
|
|
9988
|
-
|
|
9989
|
-
|
|
9990
|
-
|
|
9991
|
-
|
|
9992
|
-
|
|
9993
|
-
|
|
9994
|
-
|
|
9995
|
-
|
|
9996
|
-
|
|
9997
|
-
|
|
9998
|
-
|
|
9999
|
-
|
|
10000
|
-
|
|
10001
|
-
|
|
10002
|
-
|
|
10003
|
-
|
|
10004
|
-
|
|
10005
|
-
|
|
10006
|
-
|
|
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
|
-
|
|
10009
|
-
|
|
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
|
-
|
|
10028
|
-
|
|
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
|
-
|
|
10034
|
-
|
|
10035
|
-
|
|
10036
|
-
|
|
10037
|
-
|
|
10038
|
-
|
|
10039
|
-
|
|
10040
|
-
|
|
10041
|
-
|
|
10042
|
-
|
|
10043
|
-
|
|
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
|
-
|
|
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
|
-
|
|
10067
|
-
|
|
10068
|
-
|
|
10069
|
-
|
|
10070
|
-
|
|
10071
|
-
|
|
10072
|
-
|
|
10073
|
-
|
|
10074
|
-
|
|
10075
|
-
|
|
10076
|
-
|
|
10077
|
-
|
|
10078
|
-
|
|
10079
|
-
|
|
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
|
-
|
|
10083
|
-
|
|
10084
|
-
|
|
10085
|
-
const
|
|
10086
|
-
|
|
10087
|
-
|
|
10088
|
-
|
|
10089
|
-
|
|
10090
|
-
|
|
10091
|
-
|
|
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
|
-
|
|
10094
|
-
|
|
10095
|
-
return `copilot --resume ${sessionId}`;
|
|
10088
|
+
set(level) {
|
|
10089
|
+
this[GENERATOR].level = level;
|
|
10096
10090
|
}
|
|
10097
10091
|
}
|
|
10098
|
-
};
|
|
10099
|
-
|
|
10100
|
-
|
|
10101
|
-
|
|
10102
|
-
|
|
10103
|
-
|
|
10104
|
-
|
|
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/
|
|
10108
|
-
import
|
|
10146
|
+
// src/lib/ui.ts
|
|
10147
|
+
import * as readline from "node:readline";
|
|
10109
10148
|
|
|
10110
|
-
//
|
|
10111
|
-
|
|
10112
|
-
|
|
10113
|
-
|
|
10114
|
-
|
|
10115
|
-
|
|
10116
|
-
|
|
10117
|
-
|
|
10118
|
-
|
|
10119
|
-
|
|
10120
|
-
|
|
10121
|
-
|
|
10122
|
-
|
|
10123
|
-
|
|
10124
|
-
|
|
10125
|
-
|
|
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
|
-
|
|
10128
|
-
|
|
10129
|
-
|
|
10130
|
-
|
|
10131
|
-
|
|
10132
|
-
|
|
10133
|
-
|
|
10134
|
-
|
|
10135
|
-
|
|
10136
|
-
|
|
10137
|
-
|
|
10138
|
-
|
|
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
|
-
|
|
10148
|
-
|
|
10149
|
-
|
|
10150
|
-
|
|
10151
|
-
|
|
10152
|
-
|
|
10153
|
-
|
|
10154
|
-
|
|
10155
|
-
|
|
10156
|
-
|
|
10157
|
-
|
|
10158
|
-
|
|
10159
|
-
|
|
10160
|
-
|
|
10161
|
-
|
|
10162
|
-
|
|
10163
|
-
|
|
10164
|
-
|
|
10165
|
-
|
|
10166
|
-
|
|
10167
|
-
};
|
|
10168
|
-
|
|
10169
|
-
|
|
10170
|
-
|
|
10171
|
-
|
|
10172
|
-
|
|
10173
|
-
|
|
10174
|
-
|
|
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
|
-
|
|
10181
|
-
|
|
10182
|
-
|
|
10183
|
-
|
|
10184
|
-
|
|
10185
|
-
|
|
10186
|
-
|
|
10187
|
-
|
|
10188
|
-
|
|
10189
|
-
|
|
10190
|
-
|
|
10191
|
-
|
|
10192
|
-
|
|
10193
|
-
|
|
10194
|
-
|
|
10195
|
-
|
|
10196
|
-
|
|
10197
|
-
|
|
10198
|
-
|
|
10199
|
-
|
|
10200
|
-
|
|
10201
|
-
|
|
10202
|
-
|
|
10203
|
-
|
|
10204
|
-
|
|
10205
|
-
|
|
10206
|
-
|
|
10207
|
-
|
|
10208
|
-
|
|
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
|
-
|
|
10213
|
-
|
|
10214
|
-
|
|
10215
|
-
|
|
10216
|
-
|
|
10217
|
-
|
|
10218
|
-
|
|
10219
|
-
|
|
10220
|
-
|
|
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
|
-
|
|
10223
|
-
|
|
10224
|
-
|
|
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
|
-
|
|
10227
|
-
|
|
10228
|
-
|
|
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 (
|
|
10245
|
-
|
|
10585
|
+
if (value === "\x01") {
|
|
10586
|
+
const { line } = getCursorPosition();
|
|
10587
|
+
cursor = getLineStart(line);
|
|
10588
|
+
renderBuffer();
|
|
10589
|
+
return;
|
|
10246
10590
|
}
|
|
10247
|
-
|
|
10248
|
-
|
|
10249
|
-
|
|
10250
|
-
|
|
10251
|
-
|
|
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
|
-
|
|
10262
|
-
|
|
10263
|
-
|
|
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
|
-
|
|
10266
|
-
|
|
10267
|
-
|
|
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
|
-
|
|
10270
|
-
|
|
10271
|
-
|
|
10272
|
-
|
|
10273
|
-
|
|
10274
|
-
|
|
10275
|
-
|
|
10276
|
-
|
|
10277
|
-
|
|
10278
|
-
|
|
10279
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
10288
|
-
import
|
|
10289
|
-
import
|
|
10290
|
-
|
|
10291
|
-
|
|
10292
|
-
|
|
10293
|
-
|
|
10294
|
-
|
|
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
|
-
|
|
10298
|
-
|
|
10299
|
-
|
|
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
|
|
10305
|
-
|
|
10306
|
-
|
|
10307
|
-
|
|
10308
|
-
|
|
10309
|
-
|
|
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
|
-
|
|
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
|
-
|
|
10316
|
-
|
|
10317
|
-
|
|
10779
|
+
|
|
10780
|
+
// src/commands/complete.ts
|
|
10781
|
+
function parseTarget(value) {
|
|
10782
|
+
if (value === "run-id" || value === "workflow") {
|
|
10783
|
+
return value;
|
|
10318
10784
|
}
|
|
10319
|
-
|
|
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
|
-
|
|
10327
|
-
|
|
10328
|
-
|
|
10329
|
-
|
|
10330
|
-
|
|
10331
|
-
|
|
10332
|
-
|
|
10333
|
-
|
|
10334
|
-
|
|
10335
|
-
|
|
10336
|
-
|
|
10337
|
-
|
|
10338
|
-
|
|
10339
|
-
|
|
10340
|
-
|
|
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
|
-
|
|
10350
|
-
|
|
10351
|
-
|
|
10352
|
-
|
|
10353
|
-
if (
|
|
10354
|
-
|
|
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
|
-
|
|
10815
|
+
throw new UserInputError(`Unsupported shell "${value}". Use bash, zsh, or fish.`);
|
|
10405
10816
|
}
|
|
10406
|
-
function
|
|
10407
|
-
|
|
10408
|
-
|
|
10409
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
10420
|
-
|
|
10421
|
-
|
|
10422
|
-
|
|
10423
|
-
|
|
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
|
|
10437
|
-
|
|
10438
|
-
|
|
10439
|
-
|
|
10440
|
-
|
|
10441
|
-
|
|
10442
|
-
|
|
10443
|
-
|
|
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
|
-
|
|
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
|
-
|
|
10481
|
-
|
|
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
|
-
|
|
10491
|
-
|
|
10492
|
-
|
|
10493
|
-
|
|
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
|
-
|
|
10535
|
-
|
|
10536
|
-
|
|
10537
|
-
|
|
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
|
-
|
|
10558
|
-
|
|
10559
|
-
|
|
10560
|
-
|
|
10561
|
-
|
|
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
|
-
|
|
10565
|
-
const
|
|
10566
|
-
|
|
10567
|
-
|
|
10568
|
-
|
|
10569
|
-
|
|
10570
|
-
|
|
10571
|
-
|
|
10572
|
-
|
|
10573
|
-
|
|
10574
|
-
|
|
10575
|
-
|
|
10576
|
-
|
|
10577
|
-
|
|
10578
|
-
|
|
10579
|
-
|
|
10580
|
-
|
|
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
|
-
|
|
10605
|
-
|
|
10606
|
-
|
|
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
|
|
10609
|
-
|
|
10610
|
-
|
|
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
|
|
10615
|
-
|
|
10616
|
-
|
|
11018
|
+
function composePrompt(promptFile, promptInline) {
|
|
11019
|
+
const parts = [];
|
|
11020
|
+
if (promptFile) {
|
|
11021
|
+
parts.push(promptFile.trimEnd());
|
|
10617
11022
|
}
|
|
10618
|
-
|
|
10619
|
-
|
|
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
|
-
|
|
10629
|
-
|
|
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
|
|
11040
|
+
return [...args, "--model", model];
|
|
10632
11041
|
}
|
|
10633
|
-
|
|
10634
|
-
|
|
10635
|
-
|
|
10636
|
-
|
|
10637
|
-
|
|
10638
|
-
|
|
10639
|
-
|
|
10640
|
-
|
|
10641
|
-
|
|
10642
|
-
|
|
10643
|
-
|
|
10644
|
-
|
|
10645
|
-
|
|
10646
|
-
|
|
10647
|
-
|
|
10648
|
-
|
|
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
|
-
|
|
10678
|
-
|
|
10679
|
-
|
|
10680
|
-
|
|
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
|
-
|
|
10722
|
-
|
|
10723
|
-
|
|
10724
|
-
|
|
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
|
-
|
|
10727
|
-
|
|
10728
|
-
|
|
10729
|
-
|
|
10730
|
-
|
|
10731
|
-
|
|
10732
|
-
|
|
10733
|
-
|
|
10734
|
-
|
|
10735
|
-
|
|
10736
|
-
|
|
10737
|
-
|
|
10738
|
-
|
|
10739
|
-
|
|
10740
|
-
|
|
10741
|
-
|
|
10742
|
-
|
|
10743
|
-
|
|
10744
|
-
|
|
10745
|
-
|
|
10746
|
-
|
|
10747
|
-
|
|
10748
|
-
|
|
10749
|
-
|
|
10750
|
-
|
|
10751
|
-
|
|
10752
|
-
|
|
10753
|
-
|
|
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
|
-
|
|
10758
|
-
|
|
10759
|
-
|
|
10760
|
-
|
|
10761
|
-
|
|
10762
|
-
|
|
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
|
-
|
|
10766
|
-
if (
|
|
10767
|
-
|
|
10768
|
-
|
|
10769
|
-
|
|
10770
|
-
|
|
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
|
-
|
|
10777
|
-
|
|
10778
|
-
|
|
10779
|
-
|
|
10780
|
-
|
|
10781
|
-
|
|
10782
|
-
|
|
10783
|
-
|
|
10784
|
-
|
|
10785
|
-
|
|
10786
|
-
|
|
10787
|
-
|
|
10788
|
-
|
|
10789
|
-
|
|
10790
|
-
|
|
10791
|
-
|
|
10792
|
-
|
|
10793
|
-
|
|
10794
|
-
|
|
10795
|
-
|
|
10796
|
-
|
|
10797
|
-
|
|
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
|
-
|
|
10802
|
-
|
|
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
|
-
|
|
10805
|
-
|
|
10806
|
-
|
|
10807
|
-
|
|
10808
|
-
|
|
10809
|
-
|
|
10810
|
-
|
|
10811
|
-
|
|
10812
|
-
|
|
10813
|
-
|
|
10814
|
-
|
|
10815
|
-
|
|
10816
|
-
|
|
10817
|
-
|
|
10818
|
-
|
|
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,
|
|
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[`${
|
|
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.
|
|
11040
|
-
const
|
|
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
|
|
11515
|
+
const prompt = injectedHint ? `${resolvedPrompt}
|
|
11043
11516
|
|
|
11044
|
-
Note: ${injectedHint}` :
|
|
11045
|
-
const
|
|
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 =
|
|
11520
|
+
const effectiveModel = step.model;
|
|
11050
11521
|
const adapterOptions = typeof effectiveModel === "string" ? { allowAll: options.allowAll, model: effectiveModel } : { allowAll: options.allowAll };
|
|
11051
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
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:
|
|
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}")`,
|
|
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
|
-
|
|
11312
|
-
|
|
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
|
|
11318
|
-
if (!
|
|
11319
|
-
throw new ValidationError(`Invalid
|
|
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
|
|
11328
|
-
const
|
|
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:
|
|
11872
|
+
id: stepId,
|
|
11331
11873
|
harness,
|
|
11332
|
-
|
|
11874
|
+
next_step: ensureString(step.next_step, `steps[${index}].next_step`),
|
|
11875
|
+
requires: {
|
|
11876
|
+
inputs: requiresInputs,
|
|
11877
|
+
outputs: requiresOutputs
|
|
11878
|
+
}
|
|
11333
11879
|
};
|
|
11334
|
-
if (
|
|
11335
|
-
|
|
11336
|
-
...normalized,
|
|
11337
|
-
model
|
|
11338
|
-
};
|
|
11880
|
+
if (hasPromptFile) {
|
|
11881
|
+
normalized.prompt_file = step.prompt_file.trim();
|
|
11339
11882
|
}
|
|
11340
|
-
|
|
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
|
-
|
|
11347
|
-
|
|
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.
|
|
11372
|
-
throw new ValidationError(`Invalid
|
|
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
|
-
|
|
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
|
|
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-
|
|
11393
|
-
["Resume from a specific step", "$0 continue 20260316-
|
|
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-
|
|
11921
|
+
'$0 continue 20260316-153210 --hint "Plan mode only: read and propose changes, do not edit files."'
|
|
11397
11922
|
],
|
|
11398
11923
|
[
|
|
11399
|
-
"Force
|
|
11400
|
-
"$0 continue 20260316-
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
|
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:
|
|
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 =
|
|
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
|
|
12170
|
+
class RootCommand extends BaseCommand {
|
|
11534
12171
|
static paths = [Command.Default];
|
|
11535
12172
|
async execute() {
|
|
11536
12173
|
const shell = detectShell();
|
|
11537
|
-
process.stdout.write(
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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 "$(
|
|
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 "$(
|
|
12203
|
+
process.stdout.write(` echo 'eval "$(${binaryName} completion zsh)"' >> ~/.zshrc && source ~/.zshrc
|
|
11567
12204
|
`);
|
|
11568
|
-
process.stdout.write(` echo 'eval "$(
|
|
12205
|
+
process.stdout.write(` echo 'eval "$(${binaryName} completion bash)"' >> ~/.bashrc && source ~/.bashrc
|
|
11569
12206
|
`);
|
|
11570
|
-
process.stdout.write(`
|
|
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(`
|
|
12213
|
+
process.stdout.write(` ${binaryName} --help Show full help with all options
|
|
11577
12214
|
`);
|
|
11578
|
-
process.stdout.write(`
|
|
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
|
|
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
|
|
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(
|
|
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 =
|
|
11719
|
-
|
|
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:
|
|
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
|
|
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}`);
|