Scripting & CI

Add -p (--print) and Syntic Code runs one task without the interactive screen, prints the result and exits. That makes it a building block for shell scripts, git hooks, CI jobs and your own tools.

syntic -p "explain what src/queue/retry.ts does in five bullet points"

Feed it input

The prompt comes from the arguments. Anything piped on standard input is added after it, or is the whole prompt when there are no arguments:

git diff --staged | syntic -p "write a conventional commit message for this diff"
cat build.log | syntic -p "why did this build fail? one paragraph"
syntic -p < task.md

Flags can go before or after the prompt.

Choose the output

--output-formatYou get
text (default)The final answer as plain text.
jsonOne JSON object with the answer and run details.
stream-jsonOne JSON message per line as the run happens. Needs --verbose.

A json result looks like this:

{
  "type": "result",
  "subtype": "success",
  "is_error": false,
  "result": "The retry helper backs off exponentially…",
  "session_id": "4fb72789-…",
  "num_turns": 1,
  "duration_ms": 577,
  "stop_reason": "end_turn",
  "total_cost_usd": 0.0478,
  "cost_basis": "retail_estimate",
  "cost_note": "Retail-price estimate. Usage covered by your plan isn't charged.",
  "usage": { "input_tokens": 17274, "output_tokens": 42, "cache_read_input_tokens": 0 }
}
syntic -p "count the TODOs in src" --output-format json | jq -r .result

When the run has a check result, the result also has a verification object: verified (true or false) and line (the turn’s Checked: or Verified: line), plus the checker’s verdict, summary, evidence, trigger, files, checks and duration_ms when the end-of-turn check ran. A script can fail a CI step on .verification.verified == false.

Nobody can answer a permission question in a -p run. By default the question goes to the host program (or the background approver); --permission-prompts none refuses every question at once so the run never waits.

Streaming

--output-format stream-json --verbose prints, one per line: an init record (model, tools, slash commands, MCP servers, permission mode, session id), status records, each assistant message, and a final result. Extra switches:

FlagAdds
--include-partial-messagesText and reasoning as they stream, token by token.
--include-hook-eventsA record for each hook that runs.
--replay-user-messagesEchoes each user message before the reply.
--forward-subagent-textSubagents’ text and reasoning (their tool calls are always included).

--input-format stream-json reads the prompt from standard input as the same kind of JSON messages, so a program can drive Syntic Code in its own wire format.

Structured answers

Ask for JSON that matches a schema, and Syntic Code checks it for you:

syntic -p "list the public functions in src/lib.rs" \
  --json-schema '{"type":"object","properties":{"functions":{"type":"array","items":{"type":"string"}}},"required":["functions"]}'

The schema can be inline or a file path. The answer is validated, retried once on a mismatch, and printed as JSON. If it still does not match, the run exits non-zero.

Permissions in scripts

Nobody is there to answer a prompt, so anything that would ask is refused instead, and the agent is told why. Decide up front what the run may do:

# allow the test runner and file edits, nothing else that asks
syntic -p "fix the failing unit tests" \
  --permission-mode acceptEdits \
  --allowed-tools "Bash(npm test:*)"
OptionUse it to
--allowed-tools, --disallowed-toolsAllow or block specific tools and commands for this run.
--permission-modeplan for read-only reports, acceptEdits to let edits through, dontAsk to run only what allow rules cover.
--tools "Read,Grep,Glob"Offer only these built-in tools ("" for none).
--permission-prompt-tool <mcp tool>Let your own MCP tool answer approval questions.
--dangerously-skip-permissionsSkip prompts entirely. Only in throwaway environments such as containers. Deny rules still apply.

Inside an untrusted folder, -p runs in restricted mode (no project settings, hooks, MCP servers or agents) and prints a note when the folder has project configuration.

Keep runs bounded

OptionEffect
--max-turns <n>Stop after n agent turns.
--max-budget-usd <amount>Stop when the run’s estimated cost passes this amount; the result says error_max_budget_usd.
--task-budget <amount>A per-task cap, applied with --max-budget-usd (the tighter wins).
--model, --fallback-modelPick the model, and one to use if it is unavailable.
--effort lowLess reasoning for simple jobs.

Continue a conversation

syntic -p "find where we validate email addresses" --session-id 11111111-2222-4333-8444-555555555555
syntic -p --resume 11111111-2222-4333-8444-555555555555 "now add a check for plus-addressing"
syntic -p -c "and update the changelog"          # latest session in this folder

--no-session-persistence keeps a run from being saved at all.

Make runs repeatable

OptionEffect
--bareNo settings hooks, plugins, skill discovery, auto-memory or background work; only MCP servers you pass with --mcp-config.
--setting-sources user,localRead only these settings files.
--settings '{"model":"Syntic Code Fast"}'Layer extra settings (inline JSON or a file) for this run.
--mcp-config servers.json --strict-mcp-configUse exactly these MCP servers.
--system-prompt-file, --append-system-prompt-fileReplace or add to the system prompt from a file.
--exclude-dynamic-system-prompt-sectionsKeep machine-specific details out of the system prompt so caching works across machines.
--dump-system-promptPrint the system prompt your settings produce, and exit.
MCP_CONNECTION_NONBLOCKING=trueDo not wait for slow MCP servers: give them a short window (5 s, or MCP_CONNECT_TIMEOUT_MS) and leave out any that are not ready (MCP servers).

Exit codes

CodeMeaning
0The run finished.
1The run failed: an API error, not signed in, a budget limit, or a --json-schema mismatch. With json output, is_error is true.
2Bad usage, such as an unknown flag value.

Run in CI

Any CI system that can run a shell command can run Syntic Code. Make the syntic binary available on the runner (on its PATH, or cached between jobs), and sign in with a refresh token stored as a CI secret (Sign in):

# GitHub Actions example
- name: Review the change
  env:
    SYNTIC_OAUTH_REFRESH_TOKEN: ${{ secrets.SYNTIC_TOKEN }}
  run: |
    syntic -p "review the diff against main for bugs; reply LGTM if none" \
      --permission-mode plan --output-format json > review.json
# GitLab CI example
review:
  script:
    - syntic -p "summarize the changes in this merge request" --permission-mode plan
  variables:
    SYNTIC_OAUTH_REFRESH_TOKEN: $SYNTIC_TOKEN

Complete pipelines for both, with review comments, scheduled jobs and cost limits, are in Run in CI pipelines.

Commands that only make sense on the interactive screen, such as /compact and /rename, are refused in -p runs with a short message. Background helpers and update checks stay quiet when CI is set. Commands left running in the background are stopped when the run ends.

Slash commands in -p runs

A prompt that starts with / is handled before anything reaches the model:

The prompt isWhat happens
/goal <condition>The run works toward the goal, checking after each round.
A built-in command that stands for a request, such as /review, /code-review, /security-review or /commitExpanded into that request and run.
A custom command from .syntic/commands/ or ~/.syntic/commands/Its body is expanded with your arguments and sent as the prompt.
The name of a skillThe prompt is sent as is, and the agent loads the skill with its Skill tool.
A command that needs the interactive screenRefused with a short message; nothing is sent.
Anything elseReported as an unknown command; nothing is sent, and the run exits with 2 (or 1 with a JSON result in json and stream-json modes).

--disable-slash-commands turns this off, so a leading / is just text.

Drive it from code

syntic mcp serve exposes Syntic Code’s built-in tools to any MCP client over standard input and output. See MCP servers.