Skip to content

CLI in CI ​

The ctrlyoke CLI runs the same workflow files in a pipeline that the extension runs in VS Code. This page covers what's different on a CI runner. ctrlyoke is the full command reference.

Install ​

CI runners have no VS Code, so use the npm package. Pin the version so a pipeline doesn't change underneath you, and match the extension version your team uses:

bash
npx --yes ctrlyoke@0.1.2 --version

or install it once per job:

bash
npm install -g ctrlyoke@0.1.2

The runner needs Node.js 22.12 or later, plus the harness CLI your workflow uses, installed and signed in (see Harness sign-in).

Validate workflows as a gate ​

validate never starts a harness, so it needs no credentials and costs nothing. It fits a pull request check or a pre-commit hook:

bash
ctrlyoke validate workflows/review.ctrlyoke.yaml --json
ctrlyoke validate workflows/review.ctrlyoke.yaml --harness claude-cli --strict lossy

It exits 0 when the file is valid, 2 when it fails schema or semantic validation, and 1 when compatibility analysis fails. --strict lossy|unverified|all also fails on compatibility warnings for the chosen harness. See ctrlyoke validate.

Run headless ​

Without a TTY, ctrlyoke run runs headless automatically; --headless makes that explicit. Add --json for one machine-readable result line on stdout:

bash
ctrlyoke run workflows/fix-tests.ctrlyoke.yaml --headless --json > result.json

With --json, decorative output is suppressed, and diagnostics go to .ctrlyoke/logs/ctrlyoke-cli-<timestamp>.log, never to stdout. Upload that folder as a build artifact when you need to debug a failed run.

Exit codes ​

CodeMeaningTypical CI action
0SuccessContinue
1A step or end condition failed (also: harness sign-in failures)Fail the job; read the JSON result
2Usage or parse error, unapproved external roots or secret grants, newer stateFix the pipeline configuration
3The harness CLI could not be foundInstall it or fix PATH
4A step timed outRaise --timeout or split the job
5Interrupted (SIGINT/SIGTERM, for example a cancelled job)Nothing; the runner lock is freed

A harness that is installed but not signed in fails its step, so the run exits 1, not 3. Exit code 3 means only that the harness's executable wasn't found.

The JSON result ​

run --json writes one object (schemaVersion: 1) when the run ends:

FieldMeaning
successtrue only when the run completed and every step passed
statusFinal execution state, for example completed, error or stopped
workflowFileThe file you ran, or null for --prompt
workflowKey, runRefAddress of this run for history show, rerun, append, continue and step
harness, modelWhat the run used
startedAt, completedAtISO-8601 timestamps; durationMs is the elapsed time
varNamesNames of the workflow variables (never their values)
summaryStep counts: total, completed, failed, skipped
steps[]Per step: status, success, attempts, sessionId, preCondition, endCondition, filesChanged, script output, failureKind and error
finalStepThe final step's result and its last loop, or null
error{ step, loop, attempt, message } for the failure that ended the run, or null

Secret values are redacted from the result. Check success rather than parsing status or error.message, since message text can change.

Example: fail the job with a readable summary.

bash
ctrlyoke run workflows/fix-tests.ctrlyoke.yaml --headless --json > result.json
status=$?
node -e 'const r=require("./result.json"); console.log(r.status, JSON.stringify(r.summary)); if (r.error) console.log(r.error.message)'
exit $status

Tool permissions ​

On a CI runner nobody can answer an approval prompt, so decide permissions up front. The CLI resolves them exactly as the extension does: the step's toolPermissions, then the workflow's, then the defaults.toolPermissions setting.

The setting's default mode is allowAll: every tool is allowed except the deny list. That means the agent can run any shell command you haven't denied, with the job's credentials. For CI:

  • Set toolPermissions in the workflow, so the file carries its own policy wherever it runs.
  • Or override the settings layer for one run with --permission-mode, --allow-tool and --deny-tool. A workflow or step value still wins over these flags.
  • A mode that can still ask for approval can stall an unattended run on that prompt.

See Tool permissions and the defaults.toolPermissions setting. Codex and Antigravity don't enforce allow and deny lists.

Approvals on a fresh runner ​

Trust decisions (external directory roots, secret grants) are recorded per machine, so a fresh runner has none. run exits 2 and names the missing approval. Record approvals in the job before running, with --yes, and only for workflow files your repository controls:

bash
ctrlyoke trust-external-roots --yes
ctrlyoke trust-workflow-external-roots workflows/release.ctrlyoke.yaml --yes
ctrlyoke trust-secret-grants workflows/release.ctrlyoke.yaml --yes

Secrets ​

CI runners usually have no OS credential store, so don't use ctrlyoke secret set there. Export each secret the workflow declares as an environment variable of the same name. ctrlyoke resolves declared names from the environment:

bash
export DEPLOY_TOKEN="$DEPLOY_TOKEN_FROM_CI"
ctrlyoke secret check workflows/deploy.ctrlyoke.yaml   # fails fast if a name is missing
ctrlyoke run workflows/deploy.ctrlyoke.yaml --headless --json

secret check exits 1 when a declared name can't be resolved, so it can run before the expensive part of the job.

Harness sign-in ​

Each harness CLI signs in its own way, and the interactive browser sign-in you use on a workstation doesn't work on a runner. Use the non-interactive credential the harness documents for headless use, usually an API key in an environment variable, stored as a CI secret. See your harness's page under Harnesses.

State on the runner ​

The CLI keeps run history under ~/.ctrlyoke (move it with CTRLYOKE_HOME), and reads ~/.ctrlyoke/settings.json and the repository's .ctrlyoke/settings.json. On an ephemeral runner all of that starts empty, so put the settings a pipeline depends on in the committed .ctrlyoke/settings.json, or in the workflow itself.

GitHub Actions example ​

This job validates every workflow on each pull request, then runs a workflow with Claude Code. Swap in your own harness, its install command and its credential.

yaml
name: ctrlyoke

on:
  pull_request:

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-node@v5
        with:
          node-version: "22"
      - name: Validate workflows
        run: |
          for f in workflows/*.ctrlyoke.yaml; do
            npx --yes ctrlyoke@0.1.2 validate "$f"
          done

  fix-tests:
    needs: validate
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-node@v5
        with:
          node-version: "22"
      - name: Install ctrlyoke and the harness
        run: npm install -g ctrlyoke@0.1.2 @anthropic-ai/claude-code
      - name: Run the workflow
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          ctrlyoke run workflows/fix-tests.ctrlyoke.yaml \
            --harness claude-cli --headless --json > result.json
      - name: Upload logs and result
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: ctrlyoke
          path: |
            result.json
            .ctrlyoke/logs/

A workflow that edits files leaves those edits in the job's checkout. Commit them, open a pull request, or upload a diff as a later step. ctrlyoke doesn't push anything itself.

Source-available under the ctrlyoke Commercial License.