@mutagent/cli 0.1.280 → 0.1.281
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/cli.js +724 -128
- package/dist/bin/cli.js.map +11 -8
- package/package.json +1 -1
package/dist/bin/cli.js
CHANGED
|
@@ -883,8 +883,8 @@ var init_sdk_client = __esm(() => {
|
|
|
883
883
|
});
|
|
884
884
|
|
|
885
885
|
// src/bin/cli.ts
|
|
886
|
-
import { Command as
|
|
887
|
-
import
|
|
886
|
+
import { Command as Command15 } from "commander";
|
|
887
|
+
import chalk28 from "chalk";
|
|
888
888
|
import { readFileSync as readFileSync12 } from "fs";
|
|
889
889
|
import { join as join14, dirname as dirname4 } from "path";
|
|
890
890
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
@@ -6121,11 +6121,11 @@ If mutagent-cli is not installed: mutagent install helix`), isJson);
|
|
|
6121
6121
|
}
|
|
6122
6122
|
|
|
6123
6123
|
// src/commands/helix/index.ts
|
|
6124
|
-
import { Command as
|
|
6125
|
-
import
|
|
6124
|
+
import { Command as Command14 } from "commander";
|
|
6125
|
+
import chalk27 from "chalk";
|
|
6126
6126
|
|
|
6127
6127
|
// src/commands/helix/spawn.ts
|
|
6128
|
-
import
|
|
6128
|
+
import chalk21 from "chalk";
|
|
6129
6129
|
init_errors();
|
|
6130
6130
|
|
|
6131
6131
|
// src/lib/sandbox-api.ts
|
|
@@ -6408,6 +6408,20 @@ async function execSandbox(id, body) {
|
|
|
6408
6408
|
sandboxId: id
|
|
6409
6409
|
});
|
|
6410
6410
|
}
|
|
6411
|
+
async function postInput(id, body) {
|
|
6412
|
+
return requestJson(`/api/sandbox/${encodeURIComponent(id)}/input`, {
|
|
6413
|
+
method: "POST",
|
|
6414
|
+
body: JSON.stringify(body),
|
|
6415
|
+
sandboxId: id
|
|
6416
|
+
});
|
|
6417
|
+
}
|
|
6418
|
+
async function startSession(id, body) {
|
|
6419
|
+
return requestJson(`/api/sandbox/${encodeURIComponent(id)}/session`, {
|
|
6420
|
+
method: "POST",
|
|
6421
|
+
body: JSON.stringify(body),
|
|
6422
|
+
sandboxId: id
|
|
6423
|
+
});
|
|
6424
|
+
}
|
|
6411
6425
|
async function oneShotRun(body) {
|
|
6412
6426
|
return requestJson("/api/sandbox/run", {
|
|
6413
6427
|
method: "POST",
|
|
@@ -6543,7 +6557,7 @@ function canAnimate(isJson) {
|
|
|
6543
6557
|
}
|
|
6544
6558
|
|
|
6545
6559
|
// src/commands/helix/session.ts
|
|
6546
|
-
import
|
|
6560
|
+
import chalk20 from "chalk";
|
|
6547
6561
|
|
|
6548
6562
|
// src/lib/sse.ts
|
|
6549
6563
|
var DEFAULT_EVENT_NAME = "message";
|
|
@@ -6642,6 +6656,13 @@ async function runStreamSession(options) {
|
|
|
6642
6656
|
});
|
|
6643
6657
|
const detached = () => outcome("detached");
|
|
6644
6658
|
const isAborted = () => signal.aborted;
|
|
6659
|
+
const noteSeq = (seq) => {
|
|
6660
|
+
if (seq > lastSeq + 1 && lastSeq > 0) {
|
|
6661
|
+
gaps += 1;
|
|
6662
|
+
sink.notice(gapNotice(lastSeq, seq));
|
|
6663
|
+
}
|
|
6664
|
+
lastSeq = Math.max(lastSeq, seq);
|
|
6665
|
+
};
|
|
6645
6666
|
for (;; ) {
|
|
6646
6667
|
if (isAborted())
|
|
6647
6668
|
return detached();
|
|
@@ -6666,38 +6687,44 @@ async function runStreamSession(options) {
|
|
|
6666
6687
|
for await (const event of readSseEvents(stream)) {
|
|
6667
6688
|
attempt = 0;
|
|
6668
6689
|
if (event.event === "stdout" || event.event === "stderr") {
|
|
6669
|
-
const
|
|
6670
|
-
if (!
|
|
6690
|
+
const payload2 = parseOutputPayload(event.data);
|
|
6691
|
+
if (!payload2)
|
|
6671
6692
|
continue;
|
|
6672
|
-
|
|
6673
|
-
gaps += 1;
|
|
6674
|
-
sink.notice(gapNotice(lastSeq, payload.seq));
|
|
6675
|
-
}
|
|
6676
|
-
lastSeq = Math.max(lastSeq, payload.seq);
|
|
6693
|
+
noteSeq(payload2.seq);
|
|
6677
6694
|
if (event.event === "stdout")
|
|
6678
|
-
sink.stdout(
|
|
6695
|
+
sink.stdout(payload2.text, payload2.seq);
|
|
6679
6696
|
else
|
|
6680
|
-
sink.stderr(
|
|
6697
|
+
sink.stderr(payload2.text, payload2.seq);
|
|
6681
6698
|
continue;
|
|
6682
6699
|
}
|
|
6683
6700
|
if (event.event === "sandbox") {
|
|
6684
|
-
const
|
|
6685
|
-
if (
|
|
6686
|
-
sandbox =
|
|
6687
|
-
options.onSandbox?.(
|
|
6701
|
+
const payload2 = parseSandboxRunPayload(event.data);
|
|
6702
|
+
if (payload2) {
|
|
6703
|
+
sandbox = payload2;
|
|
6704
|
+
options.onSandbox?.(payload2);
|
|
6688
6705
|
}
|
|
6689
6706
|
continue;
|
|
6690
6707
|
}
|
|
6691
6708
|
if (event.event === "result") {
|
|
6692
|
-
const
|
|
6693
|
-
if (
|
|
6694
|
-
result =
|
|
6709
|
+
const payload2 = parseSandboxResultPayload(event.data);
|
|
6710
|
+
if (payload2)
|
|
6711
|
+
result = payload2;
|
|
6712
|
+
continue;
|
|
6713
|
+
}
|
|
6714
|
+
if (event.event === "rpc") {
|
|
6715
|
+
const payload2 = parseOutputPayload(event.data);
|
|
6716
|
+
if (!payload2) {
|
|
6717
|
+
sink.unrecognised(event.data, 0);
|
|
6718
|
+
continue;
|
|
6719
|
+
}
|
|
6720
|
+
noteSeq(payload2.seq);
|
|
6721
|
+
sink.rpc(payload2.text, payload2.seq);
|
|
6695
6722
|
continue;
|
|
6696
6723
|
}
|
|
6697
6724
|
if (event.event === "status") {
|
|
6698
|
-
const
|
|
6699
|
-
if (
|
|
6700
|
-
exitCode =
|
|
6725
|
+
const payload2 = parseStatusPayload(event.data);
|
|
6726
|
+
if (payload2?.state === "exited") {
|
|
6727
|
+
exitCode = payload2.exitCode;
|
|
6701
6728
|
exited = true;
|
|
6702
6729
|
break;
|
|
6703
6730
|
}
|
|
@@ -6713,6 +6740,15 @@ async function runStreamSession(options) {
|
|
|
6713
6740
|
}
|
|
6714
6741
|
continue;
|
|
6715
6742
|
}
|
|
6743
|
+
if (event.event === "ping")
|
|
6744
|
+
continue;
|
|
6745
|
+
const payload = parseOutputPayload(event.data);
|
|
6746
|
+
if (payload) {
|
|
6747
|
+
noteSeq(payload.seq);
|
|
6748
|
+
sink.unrecognised(payload.text, payload.seq);
|
|
6749
|
+
} else {
|
|
6750
|
+
sink.unrecognised(event.data, 0);
|
|
6751
|
+
}
|
|
6716
6752
|
}
|
|
6717
6753
|
} catch (error) {
|
|
6718
6754
|
if (isAborted())
|
|
@@ -6775,8 +6811,226 @@ function delay(ms, signal) {
|
|
|
6775
6811
|
|
|
6776
6812
|
// src/commands/helix/session.ts
|
|
6777
6813
|
init_errors();
|
|
6814
|
+
|
|
6815
|
+
// src/commands/helix/frames.ts
|
|
6816
|
+
import chalk19 from "chalk";
|
|
6817
|
+
var SILENT = new Set([
|
|
6818
|
+
"message_start",
|
|
6819
|
+
"message_update",
|
|
6820
|
+
"message_delta",
|
|
6821
|
+
"turn_end",
|
|
6822
|
+
"agent_start",
|
|
6823
|
+
"agent_end"
|
|
6824
|
+
]);
|
|
6825
|
+
var IDENTIFYING_ARG = {
|
|
6826
|
+
read: ["path", "file_path", "filePath"],
|
|
6827
|
+
write: ["path", "file_path", "filePath"],
|
|
6828
|
+
edit: ["path", "file_path", "filePath"],
|
|
6829
|
+
ls: ["path", "dir"],
|
|
6830
|
+
glob: ["pattern", "path"],
|
|
6831
|
+
grep: ["pattern", "query"],
|
|
6832
|
+
search: ["pattern", "query"],
|
|
6833
|
+
bash: ["command", "cmd", "script"],
|
|
6834
|
+
shell: ["command", "cmd"],
|
|
6835
|
+
exec: ["command", "cmd"],
|
|
6836
|
+
fetch: ["url"],
|
|
6837
|
+
webfetch: ["url"],
|
|
6838
|
+
task: ["description", "prompt"],
|
|
6839
|
+
agent: ["description", "prompt"]
|
|
6840
|
+
};
|
|
6841
|
+
var GENERIC_ARG = ["path", "file_path", "command", "pattern", "url", "query", "name", "id"];
|
|
6842
|
+
var TOOL_COL = 9;
|
|
6843
|
+
var DURATION_COL = 44;
|
|
6844
|
+
var MAX_ARG = 56;
|
|
6845
|
+
var MAX_TEXT = 400;
|
|
6846
|
+
|
|
6847
|
+
class FrameFeed {
|
|
6848
|
+
#plain;
|
|
6849
|
+
#pending = new Map;
|
|
6850
|
+
#turn = 0;
|
|
6851
|
+
#now;
|
|
6852
|
+
constructor(options = {}) {
|
|
6853
|
+
this.#plain = options.plain ?? isPlainOutput();
|
|
6854
|
+
this.#now = options.now ?? (() => Date.now());
|
|
6855
|
+
}
|
|
6856
|
+
render(frameText, seq) {
|
|
6857
|
+
const frame = asRecord(frameText);
|
|
6858
|
+
if (!frame)
|
|
6859
|
+
return this.renderUnrecognised(frameText, seq);
|
|
6860
|
+
const type = typeof frame.type === "string" ? frame.type : "";
|
|
6861
|
+
switch (type) {
|
|
6862
|
+
case "tool_execution_start":
|
|
6863
|
+
this.#startCall(frame);
|
|
6864
|
+
return null;
|
|
6865
|
+
case "tool_execution_end":
|
|
6866
|
+
return this.#endCall(frame);
|
|
6867
|
+
case "turn_start":
|
|
6868
|
+
this.#turn += 1;
|
|
6869
|
+
return this.#rule(`turn ${String(this.#turn)}`);
|
|
6870
|
+
case "message_end":
|
|
6871
|
+
return this.#assistantText(frame);
|
|
6872
|
+
case "agent_settled":
|
|
6873
|
+
return this.#line(this.#sym("●", "*"), this.#dim("agent settled — waiting for input"));
|
|
6874
|
+
case "extension_ui_request":
|
|
6875
|
+
return this.#hitl(frame);
|
|
6876
|
+
case "response":
|
|
6877
|
+
return this.#response(frame);
|
|
6878
|
+
default:
|
|
6879
|
+
if (type === "" || SILENT.has(type))
|
|
6880
|
+
return null;
|
|
6881
|
+
return this.#line(this.#sym("·", "."), this.#dim(type));
|
|
6882
|
+
}
|
|
6883
|
+
}
|
|
6884
|
+
renderUnrecognised(raw, seq) {
|
|
6885
|
+
const where = seq > 0 ? ` (seq ${String(seq)})` : "";
|
|
6886
|
+
const body = `unrecognised${where}: ${oneLine(raw, MAX_ARG * 2)}`;
|
|
6887
|
+
return this.#line(this.#sym("·", "."), this.#dim(body));
|
|
6888
|
+
}
|
|
6889
|
+
#startCall(frame) {
|
|
6890
|
+
const id = typeof frame.toolCallId === "string" ? frame.toolCallId : "";
|
|
6891
|
+
if (id === "")
|
|
6892
|
+
return;
|
|
6893
|
+
this.#pending.set(id, {
|
|
6894
|
+
toolName: typeof frame.toolName === "string" ? frame.toolName : "tool",
|
|
6895
|
+
arg: identifyingArg(frame.toolName, frame.args),
|
|
6896
|
+
startedAt: this.#now()
|
|
6897
|
+
});
|
|
6898
|
+
}
|
|
6899
|
+
#endCall(frame) {
|
|
6900
|
+
const id = typeof frame.toolCallId === "string" ? frame.toolCallId : "";
|
|
6901
|
+
const started = id === "" ? undefined : this.#pending.get(id);
|
|
6902
|
+
if (id !== "")
|
|
6903
|
+
this.#pending.delete(id);
|
|
6904
|
+
const toolName = started?.toolName ?? (typeof frame.toolName === "string" ? frame.toolName : "tool");
|
|
6905
|
+
const arg = started?.arg ?? identifyingArg(frame.toolName, frame.args);
|
|
6906
|
+
const failed = isFailure(frame.result);
|
|
6907
|
+
const elapsed = started === undefined ? "" : formatDuration(this.#now() - started.startedAt);
|
|
6908
|
+
const name = this.#plain ? toolName : failed ? chalk19.red(toolName) : chalk19.cyan(toolName);
|
|
6909
|
+
const visible = arg === "" ? toolName.padEnd(TOOL_COL) : `${toolName.padEnd(TOOL_COL)} ${arg}`;
|
|
6910
|
+
const head = arg === "" ? pad(name, toolName, TOOL_COL) : `${pad(name, toolName, TOOL_COL)} ${arg}`;
|
|
6911
|
+
const gap = " ".repeat(Math.max(2, DURATION_COL - visible.length));
|
|
6912
|
+
const tail = elapsed === "" ? "" : gap + this.#dim(elapsed);
|
|
6913
|
+
return this.#line(failed ? this.#sym("✗", "x") : this.#sym("▸", ">"), head + tail);
|
|
6914
|
+
}
|
|
6915
|
+
#assistantText(frame) {
|
|
6916
|
+
const text = extractText(frame);
|
|
6917
|
+
if (text === "")
|
|
6918
|
+
return null;
|
|
6919
|
+
return this.#line(this.#sym("❯", "|"), oneLine(text, MAX_TEXT));
|
|
6920
|
+
}
|
|
6921
|
+
#hitl(frame) {
|
|
6922
|
+
const method = typeof frame.method === "string" ? frame.method : "request";
|
|
6923
|
+
const key = typeof frame.statusKey === "string" ? ` ${frame.statusKey}` : "";
|
|
6924
|
+
const body = `hitl ${method}${key}`;
|
|
6925
|
+
return this.#line(this.#sym("◆", "?"), this.#plain ? body : chalk19.yellow(body));
|
|
6926
|
+
}
|
|
6927
|
+
#response(frame) {
|
|
6928
|
+
const command = typeof frame.command === "string" ? frame.command : "command";
|
|
6929
|
+
const id = typeof frame.id === "string" ? ` ${frame.id}` : "";
|
|
6930
|
+
const ok = frame.success !== false;
|
|
6931
|
+
const body = `${command}${id} ${ok ? "accepted" : "rejected"}`;
|
|
6932
|
+
if (this.#plain)
|
|
6933
|
+
return this.#line(ok ? "+" : "x", body);
|
|
6934
|
+
return this.#line(ok ? "✓" : "✗", ok ? chalk19.green(body) : chalk19.red(body));
|
|
6935
|
+
}
|
|
6936
|
+
#line(symbol, body) {
|
|
6937
|
+
return ` ${symbol} ${body}`;
|
|
6938
|
+
}
|
|
6939
|
+
#sym(styled, plain) {
|
|
6940
|
+
return this.#plain ? plain : styled;
|
|
6941
|
+
}
|
|
6942
|
+
#dim(text) {
|
|
6943
|
+
return this.#plain ? text : chalk19.dim(text);
|
|
6944
|
+
}
|
|
6945
|
+
#rule(label) {
|
|
6946
|
+
const dash = this.#plain ? "-" : "─";
|
|
6947
|
+
const body = `${dash.repeat(2)} ${label} ${dash.repeat(Math.max(2, 40 - label.length))}`;
|
|
6948
|
+
return ` ${this.#dim(body)}`;
|
|
6949
|
+
}
|
|
6950
|
+
}
|
|
6951
|
+
function asRecord(text) {
|
|
6952
|
+
try {
|
|
6953
|
+
const parsed = JSON.parse(text);
|
|
6954
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
6955
|
+
return parsed;
|
|
6956
|
+
}
|
|
6957
|
+
} catch {}
|
|
6958
|
+
return null;
|
|
6959
|
+
}
|
|
6960
|
+
function pad(styled, visible, width) {
|
|
6961
|
+
const missing = Math.max(0, width - visible.length);
|
|
6962
|
+
return styled + " ".repeat(missing);
|
|
6963
|
+
}
|
|
6964
|
+
function identifyingArg(toolName, args) {
|
|
6965
|
+
if (!args || typeof args !== "object" || Array.isArray(args))
|
|
6966
|
+
return "";
|
|
6967
|
+
const record = args;
|
|
6968
|
+
const name = typeof toolName === "string" ? toolName.toLowerCase() : "";
|
|
6969
|
+
for (const key of IDENTIFYING_ARG[name] ?? GENERIC_ARG) {
|
|
6970
|
+
const value = record[key];
|
|
6971
|
+
if (typeof value === "string" && value.trim() !== "")
|
|
6972
|
+
return oneLine(value, MAX_ARG);
|
|
6973
|
+
if (typeof value === "number")
|
|
6974
|
+
return String(value);
|
|
6975
|
+
}
|
|
6976
|
+
for (const value of Object.values(record)) {
|
|
6977
|
+
if (typeof value === "string" && value.trim() !== "")
|
|
6978
|
+
return oneLine(value, MAX_ARG);
|
|
6979
|
+
}
|
|
6980
|
+
return "";
|
|
6981
|
+
}
|
|
6982
|
+
function isFailure(result) {
|
|
6983
|
+
if (!result || typeof result !== "object" || Array.isArray(result))
|
|
6984
|
+
return false;
|
|
6985
|
+
const record = result;
|
|
6986
|
+
if (record.isError === true || record.error != null)
|
|
6987
|
+
return true;
|
|
6988
|
+
return record.success === false;
|
|
6989
|
+
}
|
|
6990
|
+
function extractText(frame) {
|
|
6991
|
+
const candidates = [
|
|
6992
|
+
frame.text,
|
|
6993
|
+
frame.content,
|
|
6994
|
+
frame.message?.content,
|
|
6995
|
+
frame.message?.text,
|
|
6996
|
+
frame.message
|
|
6997
|
+
];
|
|
6998
|
+
for (const candidate of candidates) {
|
|
6999
|
+
if (typeof candidate === "string" && candidate.trim() !== "")
|
|
7000
|
+
return candidate.trim();
|
|
7001
|
+
if (Array.isArray(candidate)) {
|
|
7002
|
+
const joined = candidate.map((part) => {
|
|
7003
|
+
if (typeof part === "string")
|
|
7004
|
+
return part;
|
|
7005
|
+
if (part && typeof part === "object" && typeof part.text === "string") {
|
|
7006
|
+
return part.text;
|
|
7007
|
+
}
|
|
7008
|
+
return "";
|
|
7009
|
+
}).join("").trim();
|
|
7010
|
+
if (joined !== "")
|
|
7011
|
+
return joined;
|
|
7012
|
+
}
|
|
7013
|
+
}
|
|
7014
|
+
return "";
|
|
7015
|
+
}
|
|
7016
|
+
function oneLine(text, max) {
|
|
7017
|
+
const flat = text.replace(/\s+/g, " ").trim();
|
|
7018
|
+
return flat.length <= max ? flat : `${flat.slice(0, max - 1)}…`;
|
|
7019
|
+
}
|
|
7020
|
+
function formatDuration(ms) {
|
|
7021
|
+
const safe = Math.max(0, Math.round(ms));
|
|
7022
|
+
if (safe < 1000)
|
|
7023
|
+
return `${String(safe)}ms`;
|
|
7024
|
+
if (safe < 60000)
|
|
7025
|
+
return `${(safe / 1000).toFixed(1)}s`;
|
|
7026
|
+
const minutes = Math.floor(safe / 60000);
|
|
7027
|
+
const seconds = Math.round(safe % 60000 / 1000);
|
|
7028
|
+
return `${String(minutes)}m ${String(seconds).padStart(2, "0")}s`;
|
|
7029
|
+
}
|
|
7030
|
+
|
|
7031
|
+
// src/commands/helix/session.ts
|
|
6778
7032
|
function dim(text) {
|
|
6779
|
-
return isPlainOutput() ? text :
|
|
7033
|
+
return isPlainOutput() ? text : chalk20.dim(text);
|
|
6780
7034
|
}
|
|
6781
7035
|
function reattachHint(id, lastSeq) {
|
|
6782
7036
|
const since = lastSeq > 0 ? ` --since ${String(lastSeq)}` : "";
|
|
@@ -6791,12 +7045,23 @@ function createSessionSink(isJson) {
|
|
|
6791
7045
|
stderr: (text, seq) => {
|
|
6792
7046
|
writeEvent("stderr", { seq, text });
|
|
6793
7047
|
},
|
|
7048
|
+
rpc: (frame, seq) => {
|
|
7049
|
+
const parsed = parseFrame(frame);
|
|
7050
|
+
if (parsed === null)
|
|
7051
|
+
writeEvent("unrecognised", { seq, raw: frame });
|
|
7052
|
+
else
|
|
7053
|
+
writeEvent("rpc", { seq, frame: parsed });
|
|
7054
|
+
},
|
|
7055
|
+
unrecognised: (raw, seq) => {
|
|
7056
|
+
writeEvent("unrecognised", { seq, raw });
|
|
7057
|
+
},
|
|
6794
7058
|
notice: (message) => {
|
|
6795
7059
|
process.stderr.write(`${JSON.stringify({ type: "notice", message })}
|
|
6796
7060
|
`);
|
|
6797
7061
|
}
|
|
6798
7062
|
};
|
|
6799
7063
|
}
|
|
7064
|
+
const feed = new FrameFeed;
|
|
6800
7065
|
return {
|
|
6801
7066
|
stdout: (text) => {
|
|
6802
7067
|
process.stdout.write(text);
|
|
@@ -6804,12 +7069,33 @@ function createSessionSink(isJson) {
|
|
|
6804
7069
|
stderr: (text) => {
|
|
6805
7070
|
process.stderr.write(text);
|
|
6806
7071
|
},
|
|
7072
|
+
rpc: (frame, seq) => {
|
|
7073
|
+
const line = feed.render(frame, seq);
|
|
7074
|
+
if (line !== null)
|
|
7075
|
+
process.stderr.write(`${line}
|
|
7076
|
+
`);
|
|
7077
|
+
},
|
|
7078
|
+
unrecognised: (raw, seq) => {
|
|
7079
|
+
const line = feed.renderUnrecognised(raw, seq);
|
|
7080
|
+
if (line !== null)
|
|
7081
|
+
process.stderr.write(`${line}
|
|
7082
|
+
`);
|
|
7083
|
+
},
|
|
6807
7084
|
notice: (message) => {
|
|
6808
7085
|
process.stderr.write(`${dim(`[mutagent] ${message}`)}
|
|
6809
7086
|
`);
|
|
6810
7087
|
}
|
|
6811
7088
|
};
|
|
6812
7089
|
}
|
|
7090
|
+
function parseFrame(text) {
|
|
7091
|
+
try {
|
|
7092
|
+
const parsed = JSON.parse(text);
|
|
7093
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
7094
|
+
return parsed;
|
|
7095
|
+
}
|
|
7096
|
+
} catch {}
|
|
7097
|
+
return null;
|
|
7098
|
+
}
|
|
6813
7099
|
function writeEvent(type, fields) {
|
|
6814
7100
|
process.stdout.write(`${JSON.stringify({ type, ...fields })}
|
|
6815
7101
|
`);
|
|
@@ -6883,13 +7169,17 @@ function parseSince(raw) {
|
|
|
6883
7169
|
|
|
6884
7170
|
// src/commands/helix/spawn.ts
|
|
6885
7171
|
var ARCHES = ["x86_64", "arm64"];
|
|
7172
|
+
var EGRESS_POLICIES = ["deny", "allowlist", "allow"];
|
|
7173
|
+
var EGRESS_KINDS = ["port", "cidr", "domain"];
|
|
6886
7174
|
function registerSpawnCommand(parent) {
|
|
6887
|
-
parent.command("spawn").description("Spawn a Helix session in a cloud sandbox and attach to it").requiredOption("--image <ref>", "Image reference (repository:tag or @digest)").option("--arch <arch>", `Image architecture: ${ARCHES.join(" | ")} (default: this host's)`).option("--name <name>", "Definition name (default: derived from the image reference)").option("--provider <name>", "Provider registry key (default: the backend default)").option("--env <name>", "Environment (secrets + variables) to load at spawn").option("--detach", "Spawn without attaching — print the ID and return").addHelpText("after", `
|
|
7175
|
+
parent.command("spawn").description("Spawn a Helix session in a cloud sandbox and attach to it").requiredOption("--image <ref>", "Image reference (repository:tag or @digest)").option("--arch <arch>", `Image architecture: ${ARCHES.join(" | ")} (default: this host's)`).option("--name <name>", "Definition name (default: derived from the image reference)").option("--provider <name>", "Provider registry key (default: the backend default)").option("--env <name>", "Environment (secrets + variables) to load at spawn").option("--egress <policy>", `Outbound network policy: ${EGRESS_POLICIES.join(" | ")} (default: deny)`).option("--egress-allow <rule>", "Allowance for --egress allowlist, as kind:value (repeatable)", collectRule, []).option("--detach", "Spawn without attaching — print the ID and return").addHelpText("after", `
|
|
6888
7176
|
Examples:
|
|
6889
|
-
${
|
|
6890
|
-
${
|
|
6891
|
-
${
|
|
6892
|
-
${
|
|
7177
|
+
${chalk21.dim("$")} mutagent helix spawn --image alpine:3.20
|
|
7178
|
+
${chalk21.dim("$")} mutagent helix spawn --image ghcr.io/example/helix:latest --arch x86_64
|
|
7179
|
+
${chalk21.dim("$")} mutagent helix spawn --image alpine:3.20 --detach --json
|
|
7180
|
+
${chalk21.dim("$")} mutagent helix spawn --image alpine:3.20 --provider docker --env staging
|
|
7181
|
+
${chalk21.dim("$")} mutagent helix spawn --image <helix-image> --egress allow ${chalk21.dim("Required for a Helix session")}
|
|
7182
|
+
${chalk21.dim("$")} mutagent helix spawn --image <ref> --egress allowlist --egress-allow domain:api.anthropic.com
|
|
6893
7183
|
|
|
6894
7184
|
--image is required. A sandbox is spawned from a DEFINITION, and the definition
|
|
6895
7185
|
names its own image — there is no default the CLI could invent for you. To run
|
|
@@ -6905,11 +7195,37 @@ the image was built elsewhere.
|
|
|
6905
7195
|
DIFFERENT axis from --provider, which selects the backend. Secrets never travel
|
|
6906
7196
|
on the command line.
|
|
6907
7197
|
|
|
7198
|
+
NETWORK EGRESS IS DENIED BY DEFAULT, AND THAT BREAKS HELIX SESSIONS:
|
|
7199
|
+
A sandbox runs code we did not write, so the safe default is the default:
|
|
7200
|
+
nothing reaches out. That is right for a plain 'exec' box, which needs no
|
|
7201
|
+
network at all.
|
|
7202
|
+
|
|
7203
|
+
It is WRONG for a Helix session, for two independent reasons — either alone
|
|
7204
|
+
is enough to sink it:
|
|
7205
|
+
1. Under deny the container runs with --network none. Docker accepts -P and
|
|
7206
|
+
publishes nothing, so the in-box server on :8080 is unreachable and
|
|
7207
|
+
'mutagent helix session start' fails with "Helix is not reachable".
|
|
7208
|
+
2. An agent turn has to reach a model provider regardless.
|
|
7209
|
+
|
|
7210
|
+
So: ${chalk21.bold("--egress allow")} before 'helix session start'. Without it the failure
|
|
7211
|
+
presents as an unreachable box, which reads like a broken image rather than a
|
|
7212
|
+
policy you chose.
|
|
7213
|
+
|
|
7214
|
+
${chalk21.bold("--egress allowlist")} narrows that to named allowances, each given explicitly
|
|
7215
|
+
as kind:value — 'domain:api.anthropic.com', 'port:443', 'cidr:10.0.0.0/8'. The
|
|
7216
|
+
kind is never inferred from the value. An allowlist with no rules denies
|
|
7217
|
+
everything, so it is refused rather than silently walling the box off; it also
|
|
7218
|
+
needs a provider with egress control, which not every backend has.
|
|
7219
|
+
|
|
6908
7220
|
Without --detach this spawns and immediately attaches, so the common path is a
|
|
6909
7221
|
single command. Ctrl-C then DETACHES — the sandbox keeps running and the
|
|
6910
7222
|
re-attach command is printed. Use 'mutagent helix rm <id> --force' to destroy it.
|
|
6911
7223
|
|
|
6912
7224
|
AI Agent Directive:
|
|
7225
|
+
Pass --egress allow whenever the box is for a Helix session — 'mutagent helix
|
|
7226
|
+
session start' CANNOT reach the in-box server under the default deny policy,
|
|
7227
|
+
and the error it raises names the box, not the policy. A box you only 'exec'
|
|
7228
|
+
in needs no egress.
|
|
6913
7229
|
Prefer --detach --json for automation: it returns one JSON object with the
|
|
6914
7230
|
sandbox id, so you can decide what to run before committing to a live stream.
|
|
6915
7231
|
Without --detach, --json output is NDJSON and the final line is the summary.
|
|
@@ -6968,8 +7284,49 @@ function buildDefinition(options) {
|
|
|
6968
7284
|
definition.provider = options.provider;
|
|
6969
7285
|
if (options.env)
|
|
6970
7286
|
definition.environment = options.env;
|
|
7287
|
+
const network = resolveNetwork(options.egress, options.egressAllow ?? []);
|
|
7288
|
+
if (network)
|
|
7289
|
+
definition.network = network;
|
|
6971
7290
|
return definition;
|
|
6972
7291
|
}
|
|
7292
|
+
function resolveNetwork(requested, rules) {
|
|
7293
|
+
if (requested === undefined) {
|
|
7294
|
+
if (rules.length > 0) {
|
|
7295
|
+
throw new MutagentError("INVALID_ARGUMENTS", "--egress-allow only means something with --egress allowlist.", "Add --egress allowlist, or drop the allowances and use --egress allow.");
|
|
7296
|
+
}
|
|
7297
|
+
return;
|
|
7298
|
+
}
|
|
7299
|
+
const policy = requested.trim();
|
|
7300
|
+
if (!EGRESS_POLICIES.includes(policy)) {
|
|
7301
|
+
throw new MutagentError("INVALID_ARGUMENTS", `--egress must be one of ${EGRESS_POLICIES.join(", ")}, got "${requested}".`, "Use --egress allow to drive a Helix session; the default, deny, leaves the box with no network at all.");
|
|
7302
|
+
}
|
|
7303
|
+
if (policy !== "allowlist") {
|
|
7304
|
+
if (rules.length > 0) {
|
|
7305
|
+
throw new MutagentError("INVALID_ARGUMENTS", `--egress-allow is only read under "allowlist", so under "${policy}" the allowances would be silently discarded.`, `Use --egress allowlist to apply them, or drop them to keep --egress ${policy}.`);
|
|
7306
|
+
}
|
|
7307
|
+
return { egress: policy };
|
|
7308
|
+
}
|
|
7309
|
+
if (rules.length === 0) {
|
|
7310
|
+
throw new MutagentError("INVALID_ARGUMENTS", "An allowlist with no allowances denies everything — the same wall as --egress deny, under a name that says otherwise.", `Name what may be reached: --egress-allow domain:api.anthropic.com
|
|
7311
|
+
Or open it fully: --egress allow`);
|
|
7312
|
+
}
|
|
7313
|
+
return { egress: "allowlist", allow: rules.map(parseRule) };
|
|
7314
|
+
}
|
|
7315
|
+
function collectRule(value, previous) {
|
|
7316
|
+
return [...previous, value];
|
|
7317
|
+
}
|
|
7318
|
+
function parseRule(raw) {
|
|
7319
|
+
const separator = raw.indexOf(":");
|
|
7320
|
+
const kind = separator === -1 ? "" : raw.slice(0, separator).trim();
|
|
7321
|
+
const value = separator === -1 ? "" : raw.slice(separator + 1).trim();
|
|
7322
|
+
if (!EGRESS_KINDS.includes(kind) || value === "") {
|
|
7323
|
+
throw new MutagentError("INVALID_ARGUMENTS", `--egress-allow must be <kind>:<value>, got "${raw}".`, `kind is one of ${EGRESS_KINDS.join(", ")}. Examples:
|
|
7324
|
+
--egress-allow domain:api.anthropic.com
|
|
7325
|
+
--egress-allow port:443
|
|
7326
|
+
--egress-allow cidr:10.0.0.0/8`);
|
|
7327
|
+
}
|
|
7328
|
+
return { kind, value };
|
|
7329
|
+
}
|
|
6973
7330
|
function resolveArch(requested) {
|
|
6974
7331
|
if (requested !== undefined) {
|
|
6975
7332
|
const value = requested.trim();
|
|
@@ -6995,10 +7352,10 @@ async function createSandbox(definition, isJson) {
|
|
|
6995
7352
|
const spinner = ora2(`Spawning ${definition.image} (${definition.arch})...`).start();
|
|
6996
7353
|
try {
|
|
6997
7354
|
const sandbox = await spawnSandbox(definition);
|
|
6998
|
-
spinner.succeed(
|
|
7355
|
+
spinner.succeed(chalk21.green(`Sandbox ${sandbox.id} spawned`));
|
|
6999
7356
|
return sandbox;
|
|
7000
7357
|
} catch (error) {
|
|
7001
|
-
spinner.fail(
|
|
7358
|
+
spinner.fail(chalk21.red("Spawn failed"));
|
|
7002
7359
|
throw error;
|
|
7003
7360
|
}
|
|
7004
7361
|
}
|
|
@@ -7033,15 +7390,15 @@ function reportDetached(sandbox, id, output, isJson) {
|
|
|
7033
7390
|
}
|
|
7034
7391
|
|
|
7035
7392
|
// src/commands/helix/attach.ts
|
|
7036
|
-
import
|
|
7393
|
+
import chalk22 from "chalk";
|
|
7037
7394
|
init_errors();
|
|
7038
7395
|
function registerAttachCommand(parent) {
|
|
7039
7396
|
parent.command("attach").description("Attach to a running sandbox and stream its output").argument("<id>", "Sandbox ID").option("--since <seq>", "Resume after this sequence number instead of from the start").addHelpText("after", `
|
|
7040
7397
|
Examples:
|
|
7041
|
-
${
|
|
7042
|
-
${
|
|
7043
|
-
${
|
|
7044
|
-
${
|
|
7398
|
+
${chalk22.dim("$")} mutagent helix attach sbx_123
|
|
7399
|
+
${chalk22.dim("$")} mutagent helix attach sbx_123 --since 412
|
|
7400
|
+
${chalk22.dim("$")} mutagent helix attach sbx_123 --json | tail -1
|
|
7401
|
+
${chalk22.dim("$")} mutagent helix attach sbx_123 > session.log
|
|
7045
7402
|
|
|
7046
7403
|
Ctrl-C detaches. The sandbox keeps running and the exact re-attach command,
|
|
7047
7404
|
with the resume point filled in, is printed to stderr. Only 'mutagent helix rm <id> --force'
|
|
@@ -7090,16 +7447,244 @@ AI Agent Directive:
|
|
|
7090
7447
|
});
|
|
7091
7448
|
}
|
|
7092
7449
|
|
|
7450
|
+
// src/commands/helix/send.ts
|
|
7451
|
+
import chalk23 from "chalk";
|
|
7452
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
7453
|
+
init_errors();
|
|
7454
|
+
var COMMAND_TYPES = ["prompt", "steer", "follow_up", "abort"];
|
|
7455
|
+
var NEEDS_MESSAGE = new Set(["prompt", "steer", "follow_up"]);
|
|
7456
|
+
function registerSendCommand(parent) {
|
|
7457
|
+
parent.command("send").description("Send a command line into a running session").argument("<id>", "Sandbox ID").argument("[message...]", "What to say to the agent").requiredOption("--session <id>", "Session ID inside the sandbox").option("--type <type>", `Command verb: ${COMMAND_TYPES.join(" | ")} (default: prompt)`).option("--line <json>", "Send this exact JSON line instead of building one").option("--message-id <id>", "Correlation id to use instead of a generated one").addHelpText("after", `
|
|
7458
|
+
Examples:
|
|
7459
|
+
${chalk23.dim("$")} mutagent helix send sbx_123 --session s1 "also check the refresh path"
|
|
7460
|
+
${chalk23.dim("$")} mutagent helix send sbx_123 --session s1 --type steer "stop and re-read the spec"
|
|
7461
|
+
${chalk23.dim("$")} mutagent helix send sbx_123 --session s1 --type abort
|
|
7462
|
+
${chalk23.dim("$")} mutagent helix send sbx_123 --session s1 --line '{"type":"prompt","id":"p7","message":"go"}'
|
|
7463
|
+
${chalk23.dim("$")} mutagent helix send sbx_123 --session s1 "run the tests" --json
|
|
7464
|
+
|
|
7465
|
+
TWO FORMS:
|
|
7466
|
+
${chalk23.bold("The friendly form")} — a --type and a message. This command builds the JSON:
|
|
7467
|
+
{"type":"prompt","id":"<generated>","message":"<your message>"}. Use it for
|
|
7468
|
+
everything the four verbs cover.
|
|
7469
|
+
${chalk23.bold("--line")} — one raw JSON object, passed through byte for byte. It exists for
|
|
7470
|
+
a command shape this CLI does not know about yet. It is validated as a single
|
|
7471
|
+
JSON object before anything is sent, but its CONTENTS are the harness's
|
|
7472
|
+
business, not this CLI's. Do not combine it with --type or a message.
|
|
7473
|
+
|
|
7474
|
+
THE VERBS:
|
|
7475
|
+
prompt A new instruction to a settled agent.
|
|
7476
|
+
steer Redirect the turn already running.
|
|
7477
|
+
follow_up Continue from what the agent just said.
|
|
7478
|
+
abort Stop the current turn. Takes no message.
|
|
7479
|
+
|
|
7480
|
+
Sending is not watching. A 200 means the line was written to the session; the
|
|
7481
|
+
agent's reply arrives on the event stream as a 'response' frame carrying the id
|
|
7482
|
+
printed here. Attach in another terminal to see it:
|
|
7483
|
+
${chalk23.dim("$")} mutagent helix attach sbx_123
|
|
7484
|
+
|
|
7485
|
+
A session that is no longer running is refused rather than silently accepted —
|
|
7486
|
+
'mutagent helix ls' shows what is still up.
|
|
7487
|
+
|
|
7488
|
+
AI Agent Directive:
|
|
7489
|
+
--json returns ONE object: { success, sandboxId, sessionId, type, id, line,
|
|
7490
|
+
accepted, _links }. 'accepted' means DELIVERED, not answered — the answer is a
|
|
7491
|
+
'response' frame with the same id on 'mutagent helix attach <id> --json'.
|
|
7492
|
+
Attach BEFORE sending if you need the reply; this call does not wait for one.
|
|
7493
|
+
Use the friendly form (--type + message). Reserve --line for a command shape
|
|
7494
|
+
the four verbs cannot express.
|
|
7495
|
+
`).action(async (id, messageParts, options) => {
|
|
7496
|
+
const isJson = getJsonFlag(parent);
|
|
7497
|
+
const output = new OutputFormatter(isJson ? "json" : "table");
|
|
7498
|
+
try {
|
|
7499
|
+
const sessionId = options.session?.trim() ?? "";
|
|
7500
|
+
if (sessionId === "") {
|
|
7501
|
+
throw new MutagentError("MISSING_ARGUMENTS", "--session is required — a sandbox can hold more than one session.", `Run: mutagent helix send ${id} --session <session-id> "<message>"`);
|
|
7502
|
+
}
|
|
7503
|
+
const built = buildLine(messageParts.join(" ").trim(), options);
|
|
7504
|
+
const result = await postInput(id, { sessionId, line: built.line });
|
|
7505
|
+
if (isJson) {
|
|
7506
|
+
output.output({
|
|
7507
|
+
success: true,
|
|
7508
|
+
sandboxId: id,
|
|
7509
|
+
sessionId,
|
|
7510
|
+
type: built.type,
|
|
7511
|
+
id: built.id,
|
|
7512
|
+
line: built.line,
|
|
7513
|
+
accepted: result.accepted,
|
|
7514
|
+
_links: sandboxLinks(id),
|
|
7515
|
+
_directive: {
|
|
7516
|
+
instruction: "The line was delivered, not answered. The reply is a `response` frame with this id on the event stream.",
|
|
7517
|
+
next: [`mutagent helix attach ${id} --json`]
|
|
7518
|
+
}
|
|
7519
|
+
});
|
|
7520
|
+
return;
|
|
7521
|
+
}
|
|
7522
|
+
output.success(`Sent ${built.type} to session ${sessionId}`);
|
|
7523
|
+
console.error(chalk23.dim(` ${built.line}`));
|
|
7524
|
+
console.error(chalk23.dim(` The reply arrives on the stream, not here: mutagent helix attach ${id}`));
|
|
7525
|
+
} catch (error) {
|
|
7526
|
+
handleError(error, isJson);
|
|
7527
|
+
}
|
|
7528
|
+
});
|
|
7529
|
+
}
|
|
7530
|
+
function buildLine(message, options) {
|
|
7531
|
+
const raw = options.line;
|
|
7532
|
+
if (raw !== undefined) {
|
|
7533
|
+
if (message !== "" || options.type !== undefined) {
|
|
7534
|
+
throw new MutagentError("INVALID_ARGUMENTS", "--line is the whole command already — it cannot be combined with --type or a message.", `Send the raw line on its own, or drop --line and use the friendly form:
|
|
7535
|
+
--type prompt "<message>"`);
|
|
7536
|
+
}
|
|
7537
|
+
return { line: requireOneJsonObject(raw), type: readType(raw), id: null };
|
|
7538
|
+
}
|
|
7539
|
+
const type = options.type ?? "prompt";
|
|
7540
|
+
if (!COMMAND_TYPES.includes(type)) {
|
|
7541
|
+
throw new MutagentError("INVALID_ARGUMENTS", `Unknown command type "${options.type ?? ""}".`, `Use one of: ${COMMAND_TYPES.join(", ")}. For anything else, pass the raw JSON with --line.`);
|
|
7542
|
+
}
|
|
7543
|
+
if (message === "" && NEEDS_MESSAGE.has(type)) {
|
|
7544
|
+
throw new MutagentError("MISSING_ARGUMENTS", `A "${type}" carries a message — there is nothing to send without one.`, `Run: mutagent helix send <id> --session <session-id> --type ${type} "<message>"`);
|
|
7545
|
+
}
|
|
7546
|
+
if (message !== "" && !NEEDS_MESSAGE.has(type)) {
|
|
7547
|
+
throw new MutagentError("INVALID_ARGUMENTS", `A "${type}" takes no message, so the text would be silently discarded.`, `Drop the message, or send it separately as a prompt.`);
|
|
7548
|
+
}
|
|
7549
|
+
const id = options.messageId?.trim() ?? "";
|
|
7550
|
+
const correlationId = id === "" ? `cli_${randomUUID3().slice(0, 8)}` : id;
|
|
7551
|
+
const body = { type, id: correlationId };
|
|
7552
|
+
if (message !== "")
|
|
7553
|
+
body.message = message;
|
|
7554
|
+
return { line: JSON.stringify(body), type, id: correlationId };
|
|
7555
|
+
}
|
|
7556
|
+
function requireOneJsonObject(raw) {
|
|
7557
|
+
const trimmed = raw.trim();
|
|
7558
|
+
if (trimmed.includes(`
|
|
7559
|
+
`)) {
|
|
7560
|
+
throw new MutagentError("INVALID_ARGUMENTS", "--line must be ONE line — a newline inside it would be read as two commands.", "Send one command per call.");
|
|
7561
|
+
}
|
|
7562
|
+
let parsed;
|
|
7563
|
+
try {
|
|
7564
|
+
parsed = JSON.parse(trimmed);
|
|
7565
|
+
} catch {
|
|
7566
|
+
throw new MutagentError("INVALID_ARGUMENTS", "--line is not valid JSON.", `The harness reads one JSON object per line. Example:
|
|
7567
|
+
--line '{"type":"prompt","id":"p1","message":"go"}'`);
|
|
7568
|
+
}
|
|
7569
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
7570
|
+
throw new MutagentError("INVALID_ARGUMENTS", "--line must be a JSON object, not an array or a bare value.", `Example:
|
|
7571
|
+
--line '{"type":"prompt","id":"p1","message":"go"}'`);
|
|
7572
|
+
}
|
|
7573
|
+
return trimmed;
|
|
7574
|
+
}
|
|
7575
|
+
function readType(raw) {
|
|
7576
|
+
try {
|
|
7577
|
+
const parsed = JSON.parse(raw);
|
|
7578
|
+
if (parsed && typeof parsed === "object" && typeof parsed.type === "string") {
|
|
7579
|
+
return parsed.type;
|
|
7580
|
+
}
|
|
7581
|
+
} catch {}
|
|
7582
|
+
return "raw";
|
|
7583
|
+
}
|
|
7584
|
+
|
|
7585
|
+
// src/commands/helix/session-start.ts
|
|
7586
|
+
import { Command as Command13 } from "commander";
|
|
7587
|
+
import chalk24 from "chalk";
|
|
7588
|
+
init_errors();
|
|
7589
|
+
function registerSessionCommand(parent) {
|
|
7590
|
+
const session = new Command13("session").description("Start and inspect Helix sessions inside a sandbox").addHelpText("after", `
|
|
7591
|
+
Examples:
|
|
7592
|
+
${chalk24.dim("$")} mutagent helix session start sbx_123 --rpc
|
|
7593
|
+
${chalk24.dim("$")} mutagent helix session start sbx_123
|
|
7594
|
+
${chalk24.dim("$")} mutagent helix session start sbx_123 --rpc --json
|
|
7595
|
+
|
|
7596
|
+
A sandbox and a session are not the same thing:
|
|
7597
|
+
${chalk24.bold("spawn")} gives you a BOX — a machine with nothing running in it.
|
|
7598
|
+
${chalk24.bold("session start")} gives you an AGENT inside that box.
|
|
7599
|
+
${chalk24.bold("attach")} WATCHES that agent.
|
|
7600
|
+
${chalk24.bold("send")} TYPES INTO it.
|
|
7601
|
+
A box with no session is a normal state — that is what 'exec' runs against.
|
|
7602
|
+
|
|
7603
|
+
AI Agent Directive:
|
|
7604
|
+
Run 'mutagent helix session start <sandbox-id> --rpc --json' before 'send':
|
|
7605
|
+
a session id is required and there is no default one to guess.
|
|
7606
|
+
`);
|
|
7607
|
+
registerStart(session, parent);
|
|
7608
|
+
parent.addCommand(session);
|
|
7609
|
+
}
|
|
7610
|
+
function registerStart(session, root) {
|
|
7611
|
+
session.command("start").description("Start a Helix session inside an existing sandbox").argument("<id>", "Sandbox ID").option("--rpc", "Long-lived session you can send into (default: one-shot)").addHelpText("after", `
|
|
7612
|
+
Examples:
|
|
7613
|
+
${chalk24.dim("$")} mutagent helix session start sbx_123 --rpc
|
|
7614
|
+
${chalk24.dim("$")} mutagent helix session start sbx_123
|
|
7615
|
+
${chalk24.dim("$")} mutagent helix session start sbx_123 --rpc --json
|
|
7616
|
+
|
|
7617
|
+
TWO MODES, AND ONLY ONE OF THEM LISTENS:
|
|
7618
|
+
${chalk24.bold("--rpc")} A long-lived agent reading commands on its stdin. This is
|
|
7619
|
+
the mode 'mutagent helix send' writes into; without it there
|
|
7620
|
+
is nothing to write to and every send is refused.
|
|
7621
|
+
${chalk24.dim("(default)")} One-shot. The session takes its turn and ends. Watchable
|
|
7622
|
+
with 'attach', not steerable.
|
|
7623
|
+
|
|
7624
|
+
The box must already exist — 'mutagent helix spawn --image <ref>' makes one, and
|
|
7625
|
+
'mutagent helix ls' shows which are running. One sandbox can host several
|
|
7626
|
+
sessions, so starting one never replaces another.
|
|
7627
|
+
|
|
7628
|
+
The session id printed here is the handle for everything after it. Nothing else
|
|
7629
|
+
can produce it, so a session started and then forgotten can only be found again
|
|
7630
|
+
by attaching to the box and reading the stream.
|
|
7631
|
+
|
|
7632
|
+
AI Agent Directive:
|
|
7633
|
+
--json returns ONE object: { success, sandboxId, sessionId, status, mode,
|
|
7634
|
+
_links }. Capture 'sessionId' — 'mutagent helix send' requires it and there is
|
|
7635
|
+
no default. Use --rpc whenever you intend to send into the session; a one-shot
|
|
7636
|
+
session refuses input with a 409.
|
|
7637
|
+
`).action(async (id, options) => {
|
|
7638
|
+
const isJson = getJsonFlag(root);
|
|
7639
|
+
const output = new OutputFormatter(isJson ? "json" : "table");
|
|
7640
|
+
try {
|
|
7641
|
+
const mode = options.rpc === true ? "rpc" : "oneshot";
|
|
7642
|
+
const result = await startSession(id, { mode });
|
|
7643
|
+
const sessionId = requireSessionId(result, id);
|
|
7644
|
+
if (isJson) {
|
|
7645
|
+
output.output({
|
|
7646
|
+
success: true,
|
|
7647
|
+
sandboxId: id,
|
|
7648
|
+
sessionId,
|
|
7649
|
+
status: result.status,
|
|
7650
|
+
mode,
|
|
7651
|
+
_links: sandboxLinks(id),
|
|
7652
|
+
_directive: {
|
|
7653
|
+
instruction: mode === "rpc" ? "The session is live and reading its stdin. Attach to watch it; send to steer it." : "The session takes one turn and ends. Attach to watch it; it does not accept input.",
|
|
7654
|
+
next: mode === "rpc" ? [
|
|
7655
|
+
`mutagent helix attach ${id} --json`,
|
|
7656
|
+
`mutagent helix send ${id} --session ${sessionId} "<message>" --json`
|
|
7657
|
+
] : [`mutagent helix attach ${id} --json`]
|
|
7658
|
+
}
|
|
7659
|
+
});
|
|
7660
|
+
return;
|
|
7661
|
+
}
|
|
7662
|
+
output.success(`Session ${sessionId} started in ${id} (${mode})`);
|
|
7663
|
+
console.error(chalk24.dim(` Watch it: mutagent helix attach ${id}`));
|
|
7664
|
+
if (mode === "rpc") {
|
|
7665
|
+
console.error(chalk24.dim(` Talk to it: mutagent helix send ${id} --session ${sessionId} "<message>"`));
|
|
7666
|
+
}
|
|
7667
|
+
} catch (error) {
|
|
7668
|
+
handleError(error, isJson);
|
|
7669
|
+
}
|
|
7670
|
+
});
|
|
7671
|
+
}
|
|
7672
|
+
function requireSessionId(result, sandboxId) {
|
|
7673
|
+
if (typeof result.sessionId === "string" && result.sessionId !== "")
|
|
7674
|
+
return result.sessionId;
|
|
7675
|
+
throw new MutagentError("INVALID_RESPONSE", "The server started a session but did not return a session id.", `The session may be running — watch it with: mutagent helix attach ${sandboxId}`);
|
|
7676
|
+
}
|
|
7677
|
+
|
|
7093
7678
|
// src/commands/helix/run.ts
|
|
7094
|
-
import
|
|
7679
|
+
import chalk25 from "chalk";
|
|
7095
7680
|
init_errors();
|
|
7096
7681
|
function registerRunCommand(parent) {
|
|
7097
7682
|
parent.command("run").description("Run one Helix agent turn in a fresh sandbox").argument("<task...>", "WHAT to do this turn").requiredOption("--prompt <definition>", "WHO the agent is — the agent definition").option("--preset <name>", "Preset to run on (default: the backend default)").option("--keep", "Keep the sandbox afterwards instead of tearing it down").option("--timeout-ms <n>", "Per-command timeout in milliseconds").option("--no-stream", "Wait for the whole turn instead of watching it happen").addHelpText("after", `
|
|
7098
7683
|
Examples:
|
|
7099
|
-
${
|
|
7100
|
-
${
|
|
7101
|
-
${
|
|
7102
|
-
${
|
|
7684
|
+
${chalk25.dim("$")} mutagent helix run "summarise the failing tests" --prompt "You are a test triage agent."
|
|
7685
|
+
${chalk25.dim("$")} mutagent helix run fix the flaky login spec --prompt "$(cat agent.md)"
|
|
7686
|
+
${chalk25.dim("$")} mutagent helix run "list open PRs" --prompt "You are a release assistant." --json
|
|
7687
|
+
${chalk25.dim("$")} mutagent helix run "explore the repo" --prompt "$(cat agent.md)" --keep
|
|
7103
7688
|
|
|
7104
7689
|
TWO PROMPTS, AND THEY ARE NOT THE SAME THING:
|
|
7105
7690
|
--prompt WHO the agent is — its definition, its standing instructions.
|
|
@@ -7206,17 +7791,17 @@ async function streamAgentTurn(body, isJson, keep) {
|
|
|
7206
7791
|
function registerExecCommand(parent) {
|
|
7207
7792
|
parent.command("exec").description("Run a command in a sandbox — an existing one, or a fresh one").argument("<argv...>", "Command and arguments (argv, not a shell string)").option("--sandbox <id>", "Run in this existing sandbox instead of a fresh one").option("--preset <name>", "Preset for the fresh sandbox (ignored with --sandbox)").option("--keep", "Keep the fresh sandbox afterwards (ignored with --sandbox)").option("--cwd <dir>", "Working directory inside the sandbox").option("--timeout-ms <n>", "Command timeout in milliseconds").option("--no-stream", "Wait for the full result instead of streaming it").addHelpText("after", `
|
|
7208
7793
|
Examples:
|
|
7209
|
-
${
|
|
7210
|
-
${
|
|
7211
|
-
${
|
|
7212
|
-
${
|
|
7794
|
+
${chalk25.dim("$")} mutagent helix exec -- ls -la /workspace
|
|
7795
|
+
${chalk25.dim("$")} mutagent helix exec --sandbox sbx_123 -- bun test
|
|
7796
|
+
${chalk25.dim("$")} mutagent helix exec --sandbox sbx_123 --cwd /src -- git status
|
|
7797
|
+
${chalk25.dim("$")} mutagent helix exec --preset default -- uname -m --json
|
|
7213
7798
|
|
|
7214
|
-
Put ${
|
|
7799
|
+
Put ${chalk25.bold("--")} before the command. Everything after it is argv and is passed through
|
|
7215
7800
|
untouched, so flags meant for your command are not read as flags for this one.
|
|
7216
7801
|
|
|
7217
7802
|
ARGV, NOT A SHELL STRING. There is no shell in the sandbox unless you name one:
|
|
7218
|
-
${
|
|
7219
|
-
${
|
|
7803
|
+
${chalk25.dim("$")} mutagent helix exec -- sh -c 'ls | wc -l' ${chalk25.dim("a shell, explicitly")}
|
|
7804
|
+
${chalk25.dim("$")} mutagent helix exec -- 'ls | wc -l' ${chalk25.dim("looks for a file called 'ls | wc -l'")}
|
|
7220
7805
|
|
|
7221
7806
|
WITH --sandbox runs in that box and streams as output is produced.
|
|
7222
7807
|
WITHOUT --sandbox spawns a fresh box from a preset, runs, tears it down. That
|
|
@@ -7348,17 +7933,17 @@ function reportResult(result, output, isJson, context) {
|
|
|
7348
7933
|
}
|
|
7349
7934
|
|
|
7350
7935
|
// src/commands/helix/inventory.ts
|
|
7351
|
-
import
|
|
7936
|
+
import chalk26 from "chalk";
|
|
7352
7937
|
init_errors();
|
|
7353
7938
|
function registerLsCommand(parent) {
|
|
7354
7939
|
parent.command("ls").description("List your sandboxes").addHelpText("after", `
|
|
7355
7940
|
Examples:
|
|
7356
|
-
${
|
|
7357
|
-
${
|
|
7941
|
+
${chalk26.dim("$")} mutagent helix ls
|
|
7942
|
+
${chalk26.dim("$")} mutagent helix ls --json
|
|
7358
7943
|
|
|
7359
7944
|
Listing is always workspace-scoped — these are the sandboxes in the workspace
|
|
7360
7945
|
you are configured for, not every sandbox on the account. Change it with:
|
|
7361
|
-
${
|
|
7946
|
+
${chalk26.dim("$")} mutagent config set workspace <workspace-id>
|
|
7362
7947
|
|
|
7363
7948
|
An empty list is a normal result, not an error: it means no sandbox is running.
|
|
7364
7949
|
|
|
@@ -7378,7 +7963,7 @@ AI Agent Directive:
|
|
|
7378
7963
|
return;
|
|
7379
7964
|
}
|
|
7380
7965
|
if (sandboxes.length === 0) {
|
|
7381
|
-
console.log(
|
|
7966
|
+
console.log(chalk26.gray("No sandboxes running."));
|
|
7382
7967
|
console.log("");
|
|
7383
7968
|
console.log(" Start one: mutagent helix spawn --image <ref>");
|
|
7384
7969
|
return;
|
|
@@ -7402,8 +7987,8 @@ function toRow(sandbox) {
|
|
|
7402
7987
|
function registerPresetsCommand(parent) {
|
|
7403
7988
|
parent.command("presets").description("List the presets a one-shot run can name").addHelpText("after", `
|
|
7404
7989
|
Examples:
|
|
7405
|
-
${
|
|
7406
|
-
${
|
|
7990
|
+
${chalk26.dim("$")} mutagent helix presets
|
|
7991
|
+
${chalk26.dim("$")} mutagent helix presets --json
|
|
7407
7992
|
|
|
7408
7993
|
A preset is a named definition on the backend. It is what lets 'helix run' and
|
|
7409
7994
|
'helix exec' start something without you knowing an image reference, a registry
|
|
@@ -7424,7 +8009,7 @@ AI Agent Directive:
|
|
|
7424
8009
|
return;
|
|
7425
8010
|
}
|
|
7426
8011
|
if (presets.length === 0) {
|
|
7427
|
-
console.log(
|
|
8012
|
+
console.log(chalk26.gray("No presets configured on this backend."));
|
|
7428
8013
|
console.log("");
|
|
7429
8014
|
console.log(" Spawn with an explicit image: mutagent helix spawn --image <ref>");
|
|
7430
8015
|
return;
|
|
@@ -7444,15 +8029,15 @@ AI Agent Directive:
|
|
|
7444
8029
|
function registerRmCommand(parent) {
|
|
7445
8030
|
parent.command("rm").description("Destroy a sandbox").argument("<id>", "Sandbox ID").option("-f, --force", "Skip confirmation").addHelpText("after", `
|
|
7446
8031
|
Examples:
|
|
7447
|
-
${
|
|
7448
|
-
${
|
|
8032
|
+
${chalk26.dim("$")} mutagent helix rm sbx_123 --force
|
|
8033
|
+
${chalk26.dim("$")} mutagent helix rm sbx_123 --json
|
|
7449
8034
|
|
|
7450
8035
|
This is the ONLY command that destroys a sandbox — detaching, Ctrl-C and a
|
|
7451
8036
|
closed terminal all leave it running. Removing a sandbox that is already gone
|
|
7452
8037
|
succeeds, so a retried teardown is safe.
|
|
7453
8038
|
|
|
7454
|
-
${
|
|
7455
|
-
${
|
|
8039
|
+
${chalk26.dim("Note: --force is required. The CLI is non-interactive — confirm with the user via your native flow, then pass --force. --json auto-confirms.")}
|
|
8040
|
+
${chalk26.dim("Warning: any session running in the sandbox ends immediately and cannot be recovered.")}
|
|
7456
8041
|
|
|
7457
8042
|
AI Agent Directive:
|
|
7458
8043
|
Destroying a sandbox ends any session running in it and cannot be undone.
|
|
@@ -7487,8 +8072,8 @@ Use --force to confirm: mutagent helix rm ${id} --force`);
|
|
|
7487
8072
|
function registerTracesCommand(parent) {
|
|
7488
8073
|
parent.command("traces").description("Show the spans captured for a sandbox").argument("<id>", "Sandbox ID").addHelpText("after", `
|
|
7489
8074
|
Examples:
|
|
7490
|
-
${
|
|
7491
|
-
${
|
|
8075
|
+
${chalk26.dim("$")} mutagent helix traces sbx_123
|
|
8076
|
+
${chalk26.dim("$")} mutagent helix traces sbx_123 --json
|
|
7492
8077
|
|
|
7493
8078
|
Spans are what the session actually did — the record to read when the output
|
|
7494
8079
|
alone does not explain the result. They are called spans, not traces, because
|
|
@@ -7512,7 +8097,7 @@ AI Agent Directive:
|
|
|
7512
8097
|
return;
|
|
7513
8098
|
}
|
|
7514
8099
|
if (spans.length === 0) {
|
|
7515
|
-
console.log(
|
|
8100
|
+
console.log(chalk26.gray(`No spans captured for sandbox ${id}.`));
|
|
7516
8101
|
return;
|
|
7517
8102
|
}
|
|
7518
8103
|
output.output(spans.map((span) => {
|
|
@@ -7532,26 +8117,35 @@ AI Agent Directive:
|
|
|
7532
8117
|
|
|
7533
8118
|
// src/commands/helix/index.ts
|
|
7534
8119
|
function createHelixCommand() {
|
|
7535
|
-
const helix = new
|
|
8120
|
+
const helix = new Command14("helix").description("Run Helix sessions in a cloud sandbox").addHelpText("after", `
|
|
7536
8121
|
Examples:
|
|
7537
|
-
${
|
|
7538
|
-
${
|
|
7539
|
-
${
|
|
7540
|
-
${
|
|
7541
|
-
${
|
|
7542
|
-
${
|
|
7543
|
-
${
|
|
7544
|
-
${
|
|
7545
|
-
${
|
|
8122
|
+
${chalk27.dim("$")} mutagent helix run "fix the failing test" --prompt "$(cat agent.md)"
|
|
8123
|
+
${chalk27.dim("$")} mutagent helix exec -- uname -m ${chalk27.dim("One command, fresh box")}
|
|
8124
|
+
${chalk27.dim("$")} mutagent helix presets ${chalk27.dim("What a one-shot can run on")}
|
|
8125
|
+
${chalk27.dim("$")} mutagent helix spawn --image alpine:3.20 ${chalk27.dim("Persistent box, then attach")}
|
|
8126
|
+
${chalk27.dim("$")} mutagent helix session start sbx_1 --rpc ${chalk27.dim("Start an agent inside that box")}
|
|
8127
|
+
${chalk27.dim("$")} mutagent helix ls ${chalk27.dim("What is running")}
|
|
8128
|
+
${chalk27.dim("$")} mutagent helix exec --sandbox sbx_1 -- bun test ${chalk27.dim("Command in that box")}
|
|
8129
|
+
${chalk27.dim("$")} mutagent helix attach sbx_1 --since 412 ${chalk27.dim("Resume after a sequence number")}
|
|
8130
|
+
${chalk27.dim("$")} mutagent helix send sbx_1 --session s1 "and now" ${chalk27.dim("Talk to a running session")}
|
|
8131
|
+
${chalk27.dim("$")} mutagent helix traces sbx_1 --json ${chalk27.dim("What the session actually did")}
|
|
8132
|
+
${chalk27.dim("$")} mutagent helix rm sbx_1 --force ${chalk27.dim("Destroy it")}
|
|
7546
8133
|
|
|
7547
8134
|
Subcommands:
|
|
7548
|
-
run, exec, spawn, ls, presets, attach, traces, rm
|
|
8135
|
+
run, exec, spawn, session, ls, presets, attach, send, traces, rm
|
|
7549
8136
|
|
|
7550
8137
|
One-shot vs persistent:
|
|
7551
|
-
${
|
|
8138
|
+
${chalk27.bold("run")} and ${chalk27.bold("exec")} (without --sandbox) start from a named PRESET, do one
|
|
7552
8139
|
thing and tear the sandbox down. Nothing to clean up, no image to know.
|
|
7553
|
-
${
|
|
7554
|
-
${
|
|
8140
|
+
${chalk27.bold("spawn")} takes an explicit --image and leaves a box running for you to
|
|
8141
|
+
${chalk27.bold("attach")} and ${chalk27.bold("exec --sandbox")} against until you ${chalk27.bold("rm")} it.
|
|
8142
|
+
|
|
8143
|
+
Boxes vs sessions vs watching vs steering:
|
|
8144
|
+
${chalk27.bold("spawn")} gives you a BOX. ${chalk27.bold("session start")} gives you an AGENT inside that box.
|
|
8145
|
+
${chalk27.bold("attach")} WATCHES that agent; it does not type into one. ${chalk27.bold("send")} TYPES INTO it,
|
|
8146
|
+
from any terminal holding the id — so a session can be redirected without the
|
|
8147
|
+
terminal that started it. A box with no session is normal: that is what
|
|
8148
|
+
${chalk27.bold("exec")} runs against.
|
|
7555
8149
|
|
|
7556
8150
|
Lifecycle:
|
|
7557
8151
|
A spawned sandbox outlives your terminal. Ctrl-C, a dropped connection and a
|
|
@@ -7561,7 +8155,7 @@ Lifecycle:
|
|
|
7561
8155
|
|
|
7562
8156
|
Access:
|
|
7563
8157
|
Sandbox access is minted per WORKSPACE, so a workspace must be configured:
|
|
7564
|
-
${
|
|
8158
|
+
${chalk27.dim("$")} mutagent config set workspace <workspace-id>
|
|
7565
8159
|
|
|
7566
8160
|
Output:
|
|
7567
8161
|
Sandbox stdout goes to stdout, sandbox stderr goes to stderr, and anything
|
|
@@ -7590,6 +8184,8 @@ AI Agent Directive:
|
|
|
7590
8184
|
registerLsCommand(helix);
|
|
7591
8185
|
registerPresetsCommand(helix);
|
|
7592
8186
|
registerAttachCommand(helix);
|
|
8187
|
+
registerSendCommand(helix);
|
|
8188
|
+
registerSessionCommand(helix);
|
|
7593
8189
|
registerTracesCommand(helix);
|
|
7594
8190
|
registerRmCommand(helix);
|
|
7595
8191
|
return helix;
|
|
@@ -7703,7 +8299,7 @@ if (process.env.CLI_VERSION) {
|
|
|
7703
8299
|
} catch {}
|
|
7704
8300
|
}
|
|
7705
8301
|
setCliVersion(cliVersion);
|
|
7706
|
-
var program = new
|
|
8302
|
+
var program = new Command15;
|
|
7707
8303
|
program.name("mutagent").description(`Mutagent CLI - command-line client for the Mutagent platform
|
|
7708
8304
|
|
|
7709
8305
|
Documentation: https://docs.mutagent.io/cli
|
|
@@ -7713,61 +8309,61 @@ program.name("mutagent").description(`Mutagent CLI - command-line client for the
|
|
|
7713
8309
|
});
|
|
7714
8310
|
program.addHelpText("beforeAll", () => renderBanner());
|
|
7715
8311
|
program.addHelpText("after", `
|
|
7716
|
-
${
|
|
7717
|
-
${
|
|
7718
|
-
${
|
|
7719
|
-
${
|
|
8312
|
+
${chalk28.bold.cyan("WORKFLOWS:")}
|
|
8313
|
+
${chalk28.bold("Setup")} mutagent login → mutagent init
|
|
8314
|
+
${chalk28.bold("Lifecycle Tools")} mutagent install helix ${chalk28.dim("(login-gated)")}
|
|
8315
|
+
${chalk28.bold("Feedback")} mutagent feedback send "<what happened>" --category <cli|helix|stage:<x>> ${chalk28.dim("[--session <id>] [--attach-transcript]")}
|
|
7720
8316
|
|
|
7721
|
-
${
|
|
7722
|
-
${
|
|
8317
|
+
${chalk28.dim("For CLI usage guidance for AI agents, see the Skill at")}
|
|
8318
|
+
${chalk28.cyan(".claude/skills/mutagent-cli/SKILL.md")}
|
|
7723
8319
|
|
|
7724
|
-
${
|
|
7725
|
-
-v, --version ${
|
|
8320
|
+
${chalk28.yellow("Global flags:")}
|
|
8321
|
+
-v, --version ${chalk28.dim("Print the CLI version")} ${chalk28.dim("(before a subcommand; after one it is the subcommand's)")}
|
|
7726
8322
|
--json --api-key <k> --endpoint <url> --non-interactive
|
|
7727
8323
|
|
|
7728
|
-
${
|
|
7729
|
-
export MUTAGENT_API_KEY=mt_... ${
|
|
7730
|
-
--json ${
|
|
8324
|
+
${chalk28.yellow("Non-Interactive Mode (CI/CD & Coding Agents):")}
|
|
8325
|
+
export MUTAGENT_API_KEY=mt_... ${chalk28.dim("or")} --api-key mt_...
|
|
8326
|
+
--json ${chalk28.dim("for structured output")} --non-interactive ${chalk28.dim("to disable prompts")}
|
|
7731
8327
|
|
|
7732
|
-
${
|
|
7733
|
-
mutagent login ${
|
|
7734
|
-
mutagent auth status ${
|
|
7735
|
-
mutagent init ${
|
|
7736
|
-
mutagent workspaces list --json ${
|
|
7737
|
-
mutagent config set workspace <id> ${
|
|
7738
|
-
mutagent usage --json ${
|
|
8328
|
+
${chalk28.yellow("Command Navigation:")}
|
|
8329
|
+
mutagent login ${chalk28.dim("Login (browser OAuth — recommended)")}
|
|
8330
|
+
mutagent auth status ${chalk28.dim("Check auth + workspace")}
|
|
8331
|
+
mutagent init ${chalk28.dim("Initialize project (.mutagentrc.json)")}
|
|
8332
|
+
mutagent workspaces list --json ${chalk28.dim("List workspaces (verify ID)")}
|
|
8333
|
+
mutagent config set workspace <id> ${chalk28.dim("Set active workspace")}
|
|
8334
|
+
mutagent usage --json ${chalk28.dim("Show account usage + provider status")}
|
|
7739
8335
|
|
|
7740
|
-
mutagent providers list --json ${
|
|
7741
|
-
mutagent providers list --models ${
|
|
8336
|
+
mutagent providers list --json ${chalk28.dim("List configured BYOK providers")}
|
|
8337
|
+
mutagent providers list --models ${chalk28.dim("See available models per provider")}
|
|
7742
8338
|
|
|
7743
|
-
mutagent helix run "<task>" --prompt "<who>" ${
|
|
7744
|
-
mutagent helix ls --json ${
|
|
7745
|
-
mutagent helix spawn --image <ref> ${
|
|
7746
|
-
mutagent helix rm <id> --force ${
|
|
8339
|
+
mutagent helix run "<task>" --prompt "<who>" ${chalk28.dim("One Helix agent turn in a cloud sandbox")}
|
|
8340
|
+
mutagent helix ls --json ${chalk28.dim("List running sandboxes")}
|
|
8341
|
+
mutagent helix spawn --image <ref> ${chalk28.dim("Persistent sandbox you attach to")}
|
|
8342
|
+
mutagent helix rm <id> --force ${chalk28.dim("Destroy a sandbox (the only thing that does)")}
|
|
7747
8343
|
|
|
7748
|
-
mutagent install helix ${
|
|
7749
|
-
mutagent install helix --version 1.2.3 ${
|
|
7750
|
-
mutagent install --help ${
|
|
8344
|
+
mutagent install helix ${chalk28.dim("Install the ADL lifecycle conductor locally (login-gated)")}
|
|
8345
|
+
mutagent install helix --version 1.2.3 ${chalk28.dim("Pin a version")}
|
|
8346
|
+
mutagent install --help ${chalk28.dim("helix")}
|
|
7751
8347
|
|
|
7752
|
-
mutagent hooks --help ${
|
|
8348
|
+
mutagent hooks --help ${chalk28.dim("Hook setup for Claude Code session telemetry upload")}
|
|
7753
8349
|
|
|
7754
|
-
${
|
|
7755
|
-
Hit a bug? Run: ${
|
|
7756
|
-
Lifecycle-stage feedback: ${
|
|
7757
|
-
Link to a session: ${
|
|
7758
|
-
Attach your coding-agent session transcript: ${
|
|
7759
|
-
${
|
|
8350
|
+
${chalk28.bold.red("Report Issues:")}
|
|
8351
|
+
Hit a bug? Run: ${chalk28.cyan('mutagent feedback send "describe what went wrong" --category cli')}
|
|
8352
|
+
Lifecycle-stage feedback: ${chalk28.cyan('mutagent feedback send "eval gate unclear" --category stage:evaluate')}
|
|
8353
|
+
Link to a session: ${chalk28.cyan('mutagent feedback send "..." --session <session-id>')}
|
|
8354
|
+
Attach your coding-agent session transcript: ${chalk28.cyan('mutagent feedback send "..." --attach-transcript')}
|
|
8355
|
+
${chalk28.dim("--category accepts: cli | helix | stage:<spec|build|evaluate|diagnose|optimize>")}
|
|
7760
8356
|
|
|
7761
|
-
${
|
|
8357
|
+
${chalk28.yellow("Directive System:")}
|
|
7762
8358
|
Every --json response may include:
|
|
7763
|
-
${
|
|
7764
|
-
${
|
|
7765
|
-
${
|
|
7766
|
-
${
|
|
7767
|
-
${
|
|
7768
|
-
${
|
|
7769
|
-
|
|
7770
|
-
${
|
|
8359
|
+
${chalk28.bold("_directive.display")} Type tag — 'status_card' for card-kind directives (drives test/docs guards)
|
|
8360
|
+
${chalk28.bold("_directive.renderedCard")} Pre-formatted card ${chalk28.red("(MUST echo verbatim in chat whenever this field exists — see SKILL.md Verbatim Card Display Protocol)")}
|
|
8361
|
+
${chalk28.bold("_directive.instruction")} Next step for the agent (self-sufficient, no Skill required)
|
|
8362
|
+
${chalk28.bold("_directive.next")} Array of suggested follow-up commands
|
|
8363
|
+
${chalk28.bold("_links")} Dashboard/API URLs (format as markdown links)
|
|
8364
|
+
${chalk28.bold("_compat")} Compat metadata: cliVersion, skillVersion, skillMinCliVersion
|
|
8365
|
+
|
|
8366
|
+
${chalk28.yellow("AI Agent Rules (MANDATORY for coding agents):")}
|
|
7771
8367
|
1. Login (two paths):
|
|
7772
8368
|
- CI / fully automated: export MUTAGENT_API_KEY=mt_... then mutagent login --json
|
|
7773
8369
|
- Helping a user onboard: mutagent login --browser --json
|
|
@@ -7777,13 +8373,13 @@ ${chalk25.yellow("AI Agent Rules (MANDATORY for coding agents):")}
|
|
|
7777
8373
|
2. EVERY command MUST include --json (no exceptions)
|
|
7778
8374
|
3. Run <command> --help BEFORE first use of any command
|
|
7779
8375
|
4. Parse _directive.renderedCard and copy it into your CHAT RESPONSE verbatim
|
|
7780
|
-
${
|
|
8376
|
+
${chalk28.red("HARD STOP")}: do NOT run further commands until the card is rendered in chat
|
|
7781
8377
|
5. After mutagent init, verify workspace: mutagent workspaces list --json
|
|
7782
8378
|
6. ALL user interaction via AskUserQuestion — CLI is non-interactive
|
|
7783
8379
|
${!hasCredentials() ? `
|
|
7784
|
-
` +
|
|
8380
|
+
` + chalk28.yellow(" Warning: Not authenticated. Run: mutagent login") + `
|
|
7785
8381
|
` : ""}${!hasRcConfig() ? `
|
|
7786
|
-
` +
|
|
8382
|
+
` + chalk28.green(" Get started: mutagent init") + `
|
|
7787
8383
|
` : ""}`);
|
|
7788
8384
|
var rawArgs = process.argv.slice(2);
|
|
7789
8385
|
if (isGlobalVersionFlag(rawArgs)) {
|
|
@@ -7824,5 +8420,5 @@ program.addCommand(createFeedbackCommand());
|
|
|
7824
8420
|
program.addCommand(createTraceCommand());
|
|
7825
8421
|
program.parse();
|
|
7826
8422
|
|
|
7827
|
-
//# debugId=
|
|
8423
|
+
//# debugId=9CA63CD3787CB08B64756E2164756E21
|
|
7828
8424
|
//# sourceMappingURL=cli.js.map
|