Customise formatting
Change the layout of the bash output, hide or restyle notes, change their wording, or write your own formatter.
A formatter turns the result of a call into result.content, the parts that go to the model. The default is defaultShellFormatter(). The formatter changes only content. status, run, output, notes, and the other fields stay the same.
The default layout
defaultShellFormatter() prints three parts:
- A status line:
Exit code 2 · 0 s,Ended by SIGTERM · 0.2 s, orTimed out after 2 min. The process tree was stopped. - The output. When the output is too long, a line such as
[… 4995 lines (183.7 KB) not shown]is between the first part and the last part. With no output, the line is(no output). - A blank line, then one line for each note:
[bash:code] message.
A call that did not start a command prints only the notes.
Hide or restyle notes
defaultShellFormatter() takes two options:
| Option | Default | Sets |
|---|---|---|
notes | show all | A filter for notes. Return null to hide a note |
noteLine | [bash:code] message | How each note is printed |
Set noteLine to change how each note is printed:
import { nodeCommandRunner } from "@better-fs-tools/node";
import { createBashTool, defaultShellFormatter, shellEnv } from "@better-fs-tools/shell";
const bash = createBashTool({
runner: nodeCommandRunner(),
env: shellEnv(() => process.env),
formatter: defaultShellFormatter({
noteLine: (note) => `<note code="${note.code}">${note.message}</note>`,
}),
});Exit code 0 · 0 s
hi
<note code="clamped">The requested timeout of 15 min is over the maximum, so 10 min was used.</note>Set notes to hide a note from the text, or to change it. The function gets the note. It returns the note to show, a changed note, or null. A note filter changes only the text. The note stays in result.notes:
const bash = createBashTool({
runner: nodeCommandRunner(),
env: shellEnv(() => process.env),
formatter: defaultShellFormatter({
notes: (note) => (note.code === "clamped" ? null : note),
}),
});For the same call, the text now ends after the output:
Exit code 0 · 0 s
hiThe bash reference lists every note code.
Change the wording
Set messages to change the text of a status line or a note. Each message is a function that gets the details and returns a string. The messages you leave out keep their default text:
const bash = createBashTool({
runner: nodeCommandRunner(),
env: shellEnv(() => process.env),
messages: {
exited: ({ code, durationMs }) => `exit=${code} time=${durationMs}ms`,
noOutput: () => "(the command printed nothing)",
},
});exit=0 time=1ms
(the command printed nothing)The bash reference lists every message and its arguments.
When your tool definition uses other parameter names or a timeout in seconds, give the messages the same names and unit with bashSignatureMessages(). The provider integrations do this for you:
import { bashSignatureMessages, defaultBashSignature } from "@better-fs-tools/shell/signature";
const signature = defaultBashSignature({ timeoutUnit: "s" });
const bash = createBashTool({
runner: nodeCommandRunner(),
env: shellEnv(() => process.env),
messages: bashSignatureMessages(signature),
});
await bash(signature.toInput({ command: "true", timeout: 900 }));[bash:clamped] The requested timeout of 900 seconds is over the maximum, so 600 seconds was used.Write your own formatter
A formatter is an object with an id and a format(report, ctx) function. format returns a string, or a list of content parts. It must be synchronous:
import type { ShellFormatter } from "@better-fs-tools/shell";
const json: ShellFormatter = {
id: "json",
format(report) {
if (report.status === "error")
return JSON.stringify({ status: report.status, error: report.error.message });
return JSON.stringify({
status: report.status,
exitCode: report.run.exitCode,
output: report.output.head,
});
},
};{"status":"ok","exitCode":0,"output":"greet.ts"}report is the result without content. When the output is cut, report.output.head holds the first part and report.output.tail the last part. ctx holds the limits, the messages, the digest, and the call context in call.
If your formatter throws, the tool uses defaultShellFormatter() for that result. The status does not change, and the tool adds no note.