Automate & Script the CLI

3xcode is built to run unattended: every flag that could prompt has a non-interactive equivalent, and a machine-readable command manifest means scripts, CI pipelines, and AI agents never have to hardcode a command list.

Overview

The interactive shell and file pickers are there for a human at a terminal, but nothing in 3xcode requires them. Every command that would otherwise prompt has a flag to supply the answer up front, every long-running conversion can run detached and be polled from a script, and 3xcode commands --json exposes the full command surface as structured data instead of parsed --help text.

Non-Interactive Use

Anything that would normally open an interactive picker or confirmation prompt accepts the same information as a flag instead.

SituationInteractive defaultNon-interactive flag
Logging inBrowser device flow3xcode login --email you@co.com --password ***
Picking a file to convertInteractive file pickerPass the file path as a positional argument
Destructive actionsConfirmation prompt--force / -f (session, batch, workspace) or --yes / -y (update)
Choosing a BYOK providerInteractive menu--provider anthropic (or bedrock, vertex, azure)
A long-running conversionStreams to your terminal, blocks it--detach (pyspark convert) or batch run (always detached)
Terminal
# Non-interactive login for a CI runner or service account
3xcode login --email ci-bot@company.com --password "$CI_3XCODE_PASSWORD"

# No pickers, no prompts: every value supplied up front
3xcode workspace init ./migration --name "nightly-run"
3xcode session create --name "nightly-run" --dir ./sql/
3xcode batch create <session_id> --name "batch-1" --dir ./sql/ --sort simple-first
3xcode batch seal <session_id> <batch_id>
3xcode batch run <session_id> <batch_id> --workers 4

A Machine-Readable Command Manifest

3xcode commands lists every command 3xcode has. Add --json and it emits the same information as a structured manifest instead: every command's full path, summary, arguments, and options, with types, defaults, and aliases. This is the surface to build against if you're wiring 3xcode into a script, a CI pipeline, or an AI agent that needs to discover what the CLI can do without parsing --help output.

Terminal
3xcode commands --json
Shape of the manifest (abridged)
{
  "program": "3xcode",
  "version": "0.2.1",
  "schema_version": "1",
  "global_options": [ { "name": "--verbose", "aliases": ["-v"], "type": "boolean", ... } ],
  "commands": [
    {
      "path": "batch run",
      "summary": "Run a sealed batch as a detached background worker.",
      "arguments": [ { "name": "session_id", "required": true, ... } ],
      "options": [ { "name": "--workers", "aliases": ["-w"], "type": "integer", "default": 0, ... } ]
    }
    // ...every other command, across all eight command groups
  ]
}

Built for tooling, not just humans

Every argument and option carries its type, default, required flag, and aliases, enough structure to generate a wrapper, validate a call before running it, or build a UI on top of the CLI without hand-maintaining a command list.

A stable contract for AI agents

An agent that needs to drive 3xcode can call commands --json once, learn the full command surface, and construct valid invocations from it, rather than being hardcoded to a specific CLI version's flags.

Exit Codes

3xcode doesn't define a large table of specific exit codes: scripts should treat 0 as success and any non-zero code as failure, and check the printed error message (or the session/batch state) for the specific cause.

Exit codeMeaning
0Success (including an intentional early exit, like answering "no" to a confirmation prompt)
1A command failed: a validation error, an unreachable backend, a failed conversion, etc.
130Interrupted with Ctrl-C (SIGINT), the standard convention for a keyboard-interrupted process

Environment Variables

VariablePurpose
THREEXCODE_DISABLE_UPDATE_CHECKSet to disable the background update-check notice entirely, useful for keeping CI logs clean. See Installation & Updates.

Provider credentials for BYOK (Bedrock, Vertex, Azure) are also resolved through environment variables under the hood once you run keys store, but those are managed for you rather than something you typically set by hand in a script. See Multi-Provider & BYOK.

A CI Example

A minimal pipeline step that converts a directory of SQL and fails the build if the batch doesn't complete cleanly:

ci-convert.sh
#!/usr/bin/env bash
set -euo pipefail

export THREEXCODE_DISABLE_UPDATE_CHECK=1

3xcode login --email "$THREEXCODE_EMAIL" --password "$THREEXCODE_PASSWORD"
3xcode workspace init ./migration --name "ci-run"

SESSION_ID=$(3xcode session create --name "ci-run" --dir ./sql/ | grep -oE 'session-[0-9a-z-]+')
BATCH_ID=$(3xcode batch create "$SESSION_ID" --name "ci-batch" --dir ./sql/ | grep -oE 'batch-[0-9a-z-]+')

3xcode batch seal "$SESSION_ID" "$BATCH_ID"
3xcode batch run "$SESSION_ID" "$BATCH_ID" --workers 4

# Poll until the batch is no longer running, then check its final state
until 3xcode batch list "$SESSION_ID" | grep -q "$BATCH_ID.*completed\|$BATCH_ID.*failed"; do
  sleep 15
done

3xcode batch list "$SESSION_ID" | grep "$BATCH_ID.*completed"

Adjust the ID-parsing lines to match your CLI version's actual output format (or prefer commands --json plus your own wrapper for anything more than a quick shell script); the pattern, non-interactive flags, a detached run, and polling for terminal state, stays the same.

Troubleshooting

A script hangs instead of failing

Something is waiting on an interactive prompt or picker. Double check every command in the script supplies its inputs as flags or positional arguments, and that destructive actions pass --force / -y.

CI logs are noisy with update notices

Set THREEXCODE_DISABLE_UPDATE_CHECK=1 in the CI environment to remove the background update-check line from every command's output.

An agent-driven call fails with an unexpected flag error

Re-fetch 3xcode commands --json rather than relying on a cached copy: flags and defaults can change between versions, and the manifest is always generated from the CLI version actually installed.