Headless & CI
byteask with no arguments opens the interactive TUI, which needs a terminal. For scripts, pipelines
and CI, use byteask exec — it runs one task to completion and exits.
byteask exec "explain what src/parser.rs does"
byteask exec - # read the prompt from stdin
echo "review this diff" | byteask exec -
You do not need script -q /dev/null or a pty wrapper. If you find yourself reaching for one,
you are running the TUI; add exec.
The sandbox — read this first
Shell commands the agent runs are sandboxed read-only by default. That is the single most
important thing to know about headless runs, because a read-only sandbox means the agent cannot
compile anything: cc cannot write its object files, and it cannot even write to TMPDIR. You will
see errors like:
clang++: error: unable to make temporary file: Read-only file system
That is the sandbox working as designed, not a broken toolchain.
| Mode | Flag | What the agent's shell commands can do |
|---|---|---|
read-only | (default) | Read files, run analysis. No writes anywhere, including TMPDIR. |
workspace-write | --sandbox workspace-write | Read anywhere; write inside the working directory. Builds, test runs and generated files work. |
danger-full-access | --sandbox danger-full-access | No sandbox at all. Only for throwaway containers. |
If you want the agent to build, test, or write files, pass --sandbox workspace-write.
byteask exec --sandbox workspace-write "build the project and fix any compile errors"
-s is the short form. Approvals are non-interactive in exec, so there is no prompt to escalate
with — the mode you pass on the command line is the mode you get for the whole run.
Tools that are not affected by the sandbox
The sandbox governs the agent's shell tool. ByteAsk's built-in native tools run as direct
children of the engine and are not subject to it, so they work even under the default
read-only mode — including the ones that compile and execute code:
| Family | Tools |
|---|---|
| Build / analyse / run C and C++ | cpp_sanitize (ASan, UBSan, Valgrind), cpp_check, cpp_tidy, cpp_format, cpp_compile_db, cpp_nav, cpp_layout |
| Performance | cpp_perf, cpp_bench |
| Binaries and debugging | cpp_binary, cpp_symbolize, cpp_decompile, debug, debug_session, debug_attach, debug_record |
| Online services | cpp_godbolt, cpp_insights, cpp_intrinsic |
| Grounded documentation search | autosar_ref, mcu_ref, fpga_ref, hft_ref, grid_ref, scpi_ref, x86_ref |
So "the sandbox is read-only" does not mean "ByteAsk can only do static analysis". A read-only
run can still build your program under AddressSanitizer and run it. What a read-only run cannot do
is let the model hand-roll its own clang++ -fsanitize=address ... through the shell.
In the current release, byteask exec does not print these native tool calls — neither in human
output nor in --json. The tool runs and its result reaches the model, but you do not see it happen,
which makes a successful sanitizer run look like nothing happened. A fix is in progress. Until it
ships, add a line like "name each tool you call and quote its key output" to your prompt; the
agent's narration is rendered.
Machine-readable output
--json
Streams the session as JSON Lines on stdout — one event per line. Use it when a pipeline needs to consume the run rather than a human reading it.
byteask exec --json "list every TODO in src/ with its file and line" > run.jsonl
-o / --output-last-message
Writes just the agent's final message to a file. Usually what you want when the rest of the transcript is noise.
byteask exec -o review.md "review src/parser.rs and list defects with file:line"
cat review.md
--output-schema — structured output
Point at a JSON Schema file and the model's final response conforms to it, so you can parse the result instead of scraping prose.
byteask exec --output-schema schema.json -o result.json "…"
jq . result.json
The schema is sent in strict mode, which imposes two rules your schema must satisfy:
- every object must set
"additionalProperties": false; - every property must be listed in that object's
"required"array — there are no optional fields.
If you want a field to be omittable, model it as an empty string or an empty array instead.
Recipe: read-only review that proposes fix diffs
A common CI shape is "review this code, do not touch it, and tell me exactly what to change". Under the default read-only sandbox the agent cannot write files or apply patches — so ask it to return the diffs as data.
Download the ready-made schema:
curl -fsSLO https://code.byteask.ai/patches.schema.json
It describes:
{
"summary": "…",
"patches": [
{ "file": "src/pool.hpp", "unified_diff": "--- a/…\n+++ b/…\n@@ …", "rationale": "…" }
]
}
Run it:
byteask exec \
--sandbox read-only \
--output-schema patches.schema.json \
-o patches.json \
"Review include/pool.hpp for memory-safety defects. Use cpp_sanitize to confirm each finding
at runtime before reporting it. Do not modify any file. Return one patch per file that needs
changing, as a unified diff I can pass to 'git apply'. If a finding has no safe fix, return no
patch for it and explain why in the summary."
Then apply what you agree with:
jq -r '.patches[].unified_diff' patches.json | git apply --check - # dry run
jq -r '.patches[].unified_diff' patches.json | git apply -
Two honest caveats
1. A schema pulls the model toward answering immediately. This is the important one, and it is
true on every model, not just some. Once a response schema is attached, the model is biased to
produce a conforming answer now rather than to spend turns calling tools first — we have observed a
model return {"patches": [], "summary": ""} without opening the file at all.
So --output-schema works best on a final-answer turn, not spread across a long agentic run. If
the task needs real investigation, split it:
# 1. investigate, no schema — let the agent use its tools freely
byteask exec --sandbox workspace-write -o findings.md \
"Find and confirm memory-safety bugs in include/. Use cpp_sanitize. Write up what you proved."
# 2. convert the findings into structured patches, with the schema
byteask exec --output-schema patches.schema.json -o patches.json \
"Turn these confirmed findings into unified diffs. $(cat findings.md)"
The instruction "use cpp_sanitize to confirm each finding before reporting it" in the single-command form above is doing real work — without it, a schema-constrained run will often skip straight to an answer.
2. --output-schema currently reaches OpenAI models only. On Claude, Gemini and self-hosted
models the flag is accepted and then discarded, so you get ordinary prose back. The fix is written
and tested but not yet deployed. Until it is, use -m gpt-5.4 (or another OpenAI model) for
schema-constrained runs, and check the changelog before assuming otherwise.
Other flags worth knowing headless
| Flag | Why it matters in CI |
|---|---|
--skip-git-repo-check | Required when the working directory is not a Git repository. |
--ephemeral | Writes no session files to disk. Good for ephemeral runners. |
--ignore-user-config | Ignores ~/.byteask/config.toml so a runner's behaviour does not depend on a stray local file. |
--color never | Suppresses ANSI escapes in captured logs. |
-m MODEL | Pins the model. Do this in CI — the default can change between releases. |
See the CLI Reference for the full flag list.