Skip to content
LoopCodeDocs

Extend LoopCode

Headless mode and CI

Run LoopCode without a screen from scripts, pipelines and CI — prompts in, answers out, JSON output, exit codes, permissions and access tokens.

Run a prompt

bash
loopcode -p "Explain what src/billing/invoice.ts exports"

With -p, LoopCode runs one prompt without a screen. The answer goes to stdout, tool activity to stderr, and the exit code says how the run ended — so it composes with pipes and scripts:

bash
git diff | loopcode -p "Write a commit message for this diff" > message.txt
echo "list the TODOs in src/" | loopcode -p

A prompt can come from standard input when -p is given without one.

Options

FlagMeaning
-p, --prompt <text>The prompt
--output-format textThe answer as text (the default)
--output-format jsonOne result object at the end
--output-format stream-json, --jsonEvery run event as a JSON line, as it happens
-q, --quietNo tool activity on stderr
--max-turns <n>Stop after this many model calls
--append-system-prompt <text>Extra instructions for this run
-c, --continueContinue the most recent conversation in this folder
-r, --resume <id>Continue a particular conversation (loopcode sessions lists them)
-m, --model <id>The model
--mode <mode>The approval mode
--effort <level>Reasoning effort
-C <dir>Run in another folder
-w [name]Run in a new git worktree (kept if it has changes)
-y, --yesApprove every permission the run asks for

JSON output

bash
loopcode -p "How many API routes are there?" --output-format json
json
{
  "type": "result",
  "subtype": "success",
  "is_error": false,
  "result": "There are 37 routes …",
  "session_id": "3f2a9c1e-…",
  "num_turns": 4,
  "duration_ms": 18240,
  "usage": { "total_tokens": 48211, "cached_tokens": 31024 }
}

subtype is success, or error_<status> when the run did not finish. session_id resumes the conversation with -r.

With stream-json, every event the run produces is printed as one JSON line — the same records the desktop app shows.

Exit codes

CodeMeaning
0The run finished successfully
1The run ended without success (stopped, failed, or out of turns)
2It could not start: a bad option, no prompt, or not signed in
130Interrupted with ctrl c

Permissions

Nobody is there to answer in a headless run, so a permission the mode and rules do not cover is denied, and the agent finds another way or reports that it could not. Give a pipeline exactly what it needs with permission rules in the repository's settings:

.loopcode/settings.json
{
  "permissions": {
    "allow": ["Bash(npm ci)", "Bash(npm test:*)", "Bash(npm run lint)", "Edit(src/**)", "Edit(test/**)"],
    "deny": ["Bash(git push:*)", "Read(./.env)"]
  }
}

-y approves everything the run asks for, and --dangerously-skip-permissions stops asking altogether. Use either only in a disposable container with no production credentials.

Access tokens

CI has no browser to sign in with. Create an access token, and set it as LOOPCODE_TOKEN:

bash
loopcode token create --label "CI"

The token is shown once. It draws on your plan, like the app and the CLI.

bash
loopcode token list              # your tokens, with when each was last used
loopcode token revoke <id>       # switch one off; anything using it stops at its next request

A CI job

.github/workflows/explain-failures.yml
name: Explain test failures
on: [workflow_dispatch]
jobs:
  explain:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install LoopCode
        run: |
          curl -fsSL https://loopcode.heksync.in/cli.sh | LOOPCODE_NO_LOGIN=1 LOOPCODE_NO_MODIFY_PATH=1 sh
          echo "$HOME/.local/bin" >> "$GITHUB_PATH"
      - name: Run
        env:
          LOOPCODE_TOKEN: ${{ secrets.LOOPCODE_TOKEN }}
        run: loopcode -p "Run the tests. For each failure, explain the cause in two sentences." --max-turns 30 --output-format json > result.json

For pull request reviews and @loopcode mentions, use the ready-made integration: GitHub reviews and fixes.

Before you rely on it

Test the path where things go wrong as well as the happy one: a permission that is denied, a model or network error, a run that hits --max-turns. Check the exit code and subtype, not just whether text came out.