Custom harnesses
A CLI harness is a command plus an argument template. Jimothy fills in the template for each step, spawns the command in the run’s workspace, and reads its output. You can add any CLI this way, and every built-in harness is defined with the same fields, so the same knowledge lets you fix a built-in when a CLI changes its flags.
The shape of a harness
Section titled “The shape of a harness”This is the built-in Codex harness, as stored:
{ "command": "codex", "args": ["exec", "--json", "--full-auto", "--skip-git-repo-check", "{{options}}", "-"], "modelArgs": ["--model", "{{model}}"], // only added when the step picks a model "promptVia": "stdin", // or "arg" ({{prompt}}) or "file" ({{promptFile}}) "outputFormat": "codex-json", // or "claude-stream-json" / "pi-json" / "text" "versionArgs": ["--version"], "modelListArgs": ["debug", "models"], "models": ["gpt-6-sol", "…"], "defaultModel": "gpt-6-sol"}Adding a harness
Section titled “Adding a harness”- Open Harnesses and click Add harness. The editor opens with Name
My agent, Type Command-line agent, arguments{{prompt}}, prompt delivery argument and output Plain text. - Fill in the fields below. The Preview at the bottom shows the command line that will run for the default model.
- Click Save. Jimothy runs the version check right away; the card shows the version or the error.
- In a pipeline, select the new harness on an agent step and run it.
Editor fields (command-line agent)
Section titled “Editor fields (command-line agent)”| Field | Stored as | Description |
|---|---|---|
| Name | name |
Shown in the step editor and logs. Required. |
| Type | kind |
Command-line agent (cli) or LLM API (api, see API harnesses). |
| Description | description |
Shown on the card and as the hint under the step’s Harness menu. |
| Command | command |
Executable name on PATH, or an absolute path. |
| Prompt delivery | promptVia |
stdin (default), argument ({{prompt}}) or file ({{promptFile}}). |
| Arguments | args |
One argument per line. Each line is one argv entry, so no shell quoting is needed or applied. |
| Model arguments | modelArgs |
Added only when a model is chosen. Placeholder: --model / {{model}}. |
| System prompt arguments | systemPromptArgs |
Added only when the step has a system prompt. If empty, the system prompt is prepended to the prompt instead. |
| Subagent arguments | subagentArgs |
Added only when the step attaches subagents. If empty, subagents are ignored. |
| Output format | outputFormat |
Plain text, Claude Code stream-json, Codex exec –json or Pi –mode json. |
| Model list source | modelListArgs / modelListProvider |
Where Fetch latest gets models: None, Command (with the args to run), Anthropic API or OpenAI API. |
| Model list base URL | baseUrl |
For the API sources. Empty = api.anthropic.com or api.openai.com/v1. |
| Model list API key variable | apiKeyEnv |
Env var holding the key for the API sources. |
| Version check args | versionArgs |
Used for detection. Default --version. Must exit 0 when the CLI works. |
| Models | models, defaultModel |
One-click model choices for steps; ★ marks the default. |
Placeholders
Section titled “Placeholders”Arguments, model arguments, system prompt arguments and subagent arguments are rendered with Jimothy’s template engine, with these values:
| Placeholder | Value |
|---|---|
{{prompt}} |
The rendered prompt (with the system prompt prepended if the harness has no system prompt arguments). |
{{promptFile}} |
Path to a temporary .md file containing that prompt. Written only when Prompt delivery is file or some argument uses {{promptFile}}. |
{{model}} |
The model id for this step: the step’s model, or the harness default. |
{{systemPrompt}} |
The step’s rendered system prompt. |
{{subagents}} |
The attached subagents as one JSON object keyed by name: {"name": {"description", "prompt", "tools"?, "model"?}} (Claude Code’s --agents format). |
{{cwd}} |
The run’s workspace directory (also the process’s working directory). |
{{options}} |
Not a value: a line containing only {{options}} marks where model, system-prompt and subagent arguments are inserted. Without it they’re appended at the end. |
Rules worth knowing:
- An argument that renders to an empty string is dropped. A line
{{systemPrompt}}disappears when there’s no system prompt. - Partially empty arguments are kept.
--model={{model}}becomes--model=when no model is chosen, which most CLIs reject. Put model flags in Model arguments so they’re only added when a model is set. - Template filters work here too, e.g.
{{model | default:"gpt-5"}}. - Prompt delivery only decides whether the prompt is written to stdin. With
argyou must put{{prompt}}in an argument, and withfileyou must use{{promptFile}}, otherwise the CLI never sees the prompt. - With
stdin, Jimothy writes the prompt and closes stdin. With other modes stdin is closed immediately (a CLI that waits for input then sees end-of-file).
How the argument list is assembled
Section titled “How the argument list is assembled”options = modelArgs (if a model is chosen) + systemPromptArgs (if the step has a system prompt and the harness defines them) + subagentArgs (if the step attaches subagents and the harness defines them)
args = your Arguments, with a lone {{options}} line replaced by options (or options appended at the end if there's no {{options}} line)For example, with a step using model gpt-5:
| Harness arguments | Model arguments | Result |
|---|---|---|
run {{options}} {{prompt}} |
--model {{model}} |
run --model gpt-5 "<prompt>" |
-p {{prompt}} |
--model {{model}} |
-p "<prompt>" --model gpt-5 |
--message-file {{promptFile}} |
--model {{model}} |
--message-file /…/prompt-x.md --model gpt-5 |
Output formats
Section titled “Output formats”| Format | Use when |
|---|---|
| Plain text | Any CLI. The step output is the whole of stdout with colours stripped. No tool-call structure, cost or tokens. |
| Claude Code stream-json | The CLI emits Claude Code’s --output-format stream-json --verbose events. |
| Codex exec –json | The CLI emits codex exec --json JSONL events. |
| Pi –mode json | The CLI emits Pi’s JSON events. |
Structured formats give you tool-call logs, thinking, cost and token tracking. Lines that aren’t valid JSON are still shown as plain output lines. See Output formats.
Environment
Section titled “Environment”CLI harnesses receive the step’s environment: the app environment (with the combined PATH), Settings → Environment variables, the project’s env, the step’s Environment, and the FACTORY_* variables. See Environment variables.
A harness definition also has an env field that is merged on top for that harness only, but the harness editor doesn’t expose it. Use Settings → Environment variables or a step’s Environment instead.
Worked examples
Section titled “Worked examples”A CLI that reads the prompt from stdin and prints the answer:
| Field | Value |
|---|---|
| Command | my-agent |
| Prompt delivery | stdin |
| Arguments | --non-interactive |
| Model arguments | --model, {{model}} |
| Output format | Plain text |
A second Claude Code harness with safer permissions (for example, a read-only reviewer):
| Field | Value |
|---|---|
| Command | claude |
| Prompt delivery | stdin |
| Arguments | -p, --output-format, stream-json, --verbose, --permission-mode, plan |
| Model arguments | --model, {{model}} |
| System prompt arguments | --append-system-prompt, {{systemPrompt}} |
| Subagent arguments | --agents, {{subagents}} |
| Output format | Claude Code stream-json |
| Model list source | Anthropic API |
A wrapper script that takes a prompt file:
| Field | Value |
|---|---|
| Command | /Users/me/bin/run-agent.sh |
| Prompt delivery | file |
| Arguments | {{promptFile}}, {{cwd}} |
| Output format | Plain text |
Editing and resetting built-ins
Section titled “Editing and resetting built-ins”Built-in harnesses open in the same editor from their pencil button. Saving stores your version and it replaces the shipped one field by field. Common reasons to edit a built-in:
- A CLI renamed or removed a flag. Update Arguments.
- You’re running as root and Claude Code refuses
--dangerously-skip-permissions. See Claude Code → Running as root. - You want a different default model, or a trimmed model list.
The ↺ button (Reset to built-in defaults) throws your version away, including any stored API key and the disabled flag. Built-in harnesses can’t be deleted; disable them with the toggle instead.