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