Lesson 23: Headless & CI Mode


What this lesson is about

Headless mode lets Claude run completely on its own — no conversation window, no human present, no back and forth — receiving an instruction and delivering a result automatically. This lesson explains what that means in practice, why it matters for automating repetitive work, and — most importantly — how to get Claude to write all the technical setup for you, so you can benefit from automation without needing to understand how it works under the hood.


Core concept: the vending machine

When you buy a coffee from a vending machine, there is no conversation. You do not explain your morning to it, wait for it to ask a follow-up question, or approve its choice of cup. You press a button — the machine executes — coffee appears. The interaction is entirely one-directional: input in, output out.

Headless mode makes Claude work like this. Instead of sitting in a conversation window waiting for your next message, Claude receives a pre-written instruction, processes it immediately, delivers the result to wherever you specified — a file, a folder, another system — and exits. No interface, no waiting, no human involvement at any point.

This is enormously useful for work that is repetitive, predictable, and happens on a schedule. Summarising a folder of reports every Monday morning. Checking every new product description for tone consistency before it goes live. Generating a daily briefing from your inbox. Tasks like these do not need a human in the loop — they need a reliable machine running quietly in the background.


What “headless” actually means

The word headless comes from the idea of removing the “head” of an application — the visual interface, the window, the thing a human looks at and interacts with. A headless application does its job with no visible face. It runs, it works, it exits.

For Claude Code, headless mode means exactly this: Claude operates as a processing engine, not a conversational partner. A script (a saved list of instructions a computer follows automatically) sends Claude an instruction and a piece of content. Claude produces an output. The script takes that output and does something with it — saves it to a file, sends it somewhere, passes it to the next step.

You are not watching. You are not involved. The work happens without you.


What CI means: automated checks that never sleep

CI stands for Continuous Integration — a term from software development that simply means: every time something changes in a project, a set of automated checks runs immediately and automatically to make sure everything still works.

In a non-technical context, think of it as a quality-control conveyor belt. Every time a product comes off the line, the belt automatically checks it before it moves forward. No human needs to be standing there watching every product — the checking happens continuously, in the background, as part of the process.

When Claude is part of a CI workflow, it might automatically review every piece of new content for tone, flag documents that do not follow the required format, or summarise what changed in a project and send that summary somewhere useful. The trigger is automatic — a new file, a time of day, a change in a folder — and Claude runs without being asked each time.


The key flags: telling Claude how to behave in automation

flag is an instruction added to a command that changes how it behaves. Think of it like a modifier — “do this, but in this specific way.” Two flags are essential when using Claude Code for automation.

--print: deliver the response and exit

The --print flag tells Claude to process the instruction, print the result to the terminal (the text window where commands are typed), and then immediately exit. No session opens. No conversation starts. Input in, output out.

--no-interactive: never pause for human input

The --no-interactive flag tells Claude never to stop and wait for a human response — no matter what. Without this flag, Claude might encounter an ambiguous situation and pause, waiting for clarification. In an automated script running overnight with no one watching, that pause becomes a permanent hang. The script stops. Nothing else runs. You wake up to find nothing was done.

With --no-interactive, Claude makes its best decision and continues rather than waiting. It will not produce perfect output in every edge case, but it will always produce some output and always finish.

Here is what both flags look like together in a command:

claude --print --no-interactive "Summarise this document in three sentences: $(cat report.txt)"

This tells Claude: take the contents of report.txt, summarise them in three sentences, print the result, and exit. No window opens. No human is needed.

What happens with and without --no-interactive

Here is the practical difference when something unexpected occurs mid-script:

Without --no-interactive:

Script starts at 06:00 —
Processing file 1 of 12... done.
Processing file 2 of 12... done.
Processing file 3 of 12...

[Claude encounters an unusual file format and pauses, waiting for input]

[No human is present — nothing happens]

[Script hangs indefinitely]

[You check at 09:00 — files 4–12 were never processed]

With --no-interactive:

Script starts at 06:00 —
Processing file 1 of 12... done.
Processing file 2 of 12... done.
Processing file 3 of 12... [unusual format — Claude notes issue and continues]
Processing file 4 of 12... done.
...
Processing file 12 of 12... done.
All summaries saved. Review flagged items in output-log.txt.

--no-interactive is not optional in automation. It is the flag that ensures the machine keeps running.


Getting structured output other tools can read

What JSON is

JSON (pronounced “jay-son,” standing for JavaScript Object Notation) is a way of formatting information so that computer systems can read it reliably. Instead of a paragraph a human would read, JSON organises information into labelled pairs — a name and a value — in a predictable structure.

Think of it like a well-organised form versus a letter. A letter communicates clearly to a human but is difficult for a computer to extract information from reliably. A form — with named fields and specific values — is easy for a computer to read, process, and pass to another system.

A simple JSON response from Claude might look like this:

{
  "summary": "The report covers Q1 sales figures for the Western Cape region.",
  "sentiment": "positive",
  "word_count": 847,
  "action_required": false
}

Every piece of information is labelled. Another system receiving this — a spreadsheet, a database, a notification tool — knows exactly where to find each value without any guesswork.

How to request JSON output

Add the --output-format json flag to your command:

claude --print --no-interactive --output-format json \
  "Analyse this report and return: a one-sentence summary, the overall sentiment
  (positive/neutral/negative), and whether any action is required (true/false).
  Report: $(cat monthly-report.txt)"

Claude will return structured JSON rather than prose, ready for the next step in your automated pipeline.


Real example 1: summarising every file in a folder automatically

Here is a complete script that runs Claude on every .txt file in a folder and saves a summary of each one to a new file. Claude Code can write this for you — you do not need to understand every line — but reading the explanations helps you know what to ask for and what to change.

#!/bin/bash
# This line tells the computer this is a bash script (a list of shell commands)

# Set the folder to process and where to save the summaries
INPUT_FOLDER="./reports"
OUTPUT_FOLDER="./summaries"

# Create the output folder if it does not already exist
mkdir -p "$OUTPUT_FOLDER"

# Loop through every .txt file in the input folder
for file in "$INPUT_FOLDER"/*.txt; do

  # Get just the filename without the folder path
  filename=$(basename "$file")

  # Tell the terminal which file is currently being processed
  echo "Processing: $filename"

  # Run Claude on this file and save the result
  # --print: deliver the output and exit
  # --no-interactive: never pause for human input
  claude --print --no-interactive \
    "Summarise the following document in exactly three sentences.
     Use plain, direct language. Do not include any preamble.
     Document: $(cat "$file")" \
    > "$OUTPUT_FOLDER/${filename%.txt}-summary.txt"
    # The > symbol saves the output to a new file in the summaries folder
    # ${filename%.txt} removes the .txt extension before adding -summary.txt

  echo "Saved: ${filename%.txt}-summary.txt"

done

echo "All done. Summaries saved to $OUTPUT_FOLDER"

When this script runs, it works through every report in the reports folder, generates a three-sentence summary of each, and saves each summary as a separate file in the summaries folder. You come back to find the work done, regardless of whether you had ten files or ten thousand.


Real example 2: a daily briefing that runs itself every morning

What a cron job is

cron job (from the Greek word chronos, meaning time) is an instruction that tells a computer to run a specific command automatically at a specific time — daily, weekly, hourly, or any schedule you define. Think of it as setting a repeating alarm, but instead of waking a person up, it wakes a script up.

Setting up a daily briefing

First, here is the script that generates the briefing. Save this as daily-briefing.sh in your project folder:

#!/bin/bash
# Daily briefing script — runs automatically each morning

# Set where to save today's briefing
TODAY=$(date +%Y-%m-%d)
OUTPUT_FILE="./briefings/briefing-$TODAY.txt"

# Create the briefings folder if needed
mkdir -p "./briefings"

# Run Claude to generate the briefing
claude --print --no-interactive \
  "You are generating a daily business briefing for a small business owner.
   Today's date is $TODAY.
   
   Please produce a structured briefing that includes:
   1. A suggested priority focus for the day (based on typical business rhythms
      — end of month, mid-week, etc.)
   2. Three habits or checks worth completing before 10am
   3. A one-line motivational note appropriate to the day of the week
   
   Keep the entire briefing under 200 words. Use plain, direct language." \
  > "$OUTPUT_FILE"

echo "Today's briefing saved to $OUTPUT_FILE"

Next, schedule it to run every morning at 6:30am using a cron job. You do this by editing your computer’s crontab — the file that holds all scheduled jobs. Ask Claude to help you set this up:

Please add a cron job that runs the script at ./daily-briefing.sh
every morning at 6:30am. Walk me through exactly how to do it
on my computer.

Once set up, the cron entry looks like this:

30 6 * * * /bin/bash /path/to/your/project/daily-briefing.sh

Read as: “At minute 30, hour 6, every day of the month, every month, every day of the week — run this script.” Every morning at 6:30, Claude generates a briefing and saves it. You open the file with your morning coffee.


How non-developers use headless mode: Claude writes the scripts

The most important thing to understand about everything in this lesson is this: you do not need to write any of this yourself.

Every script shown above was the kind of output Claude produces when you describe what you want to automate in plain English. Your role is to know what you want done and to describe it clearly. Claude’s role is to write the technical instructions that make it happen.

Here are the kinds of plain English requests that produce useful automation:

Every Monday morning, I want to automatically summarise all the
.txt files added to my "client-notes" folder the previous week
and save each summary alongside the original file.
Can you write a script that does this and tell me how to set it up?
I receive a folder of supplier invoices every Friday afternoon.
I want something that reads each invoice, extracts the supplier name,
amount, and due date, and produces a single summary file listing all
of them in a table. How would I automate this with Claude Code?
Can you write a script that runs every evening and checks all the
product descriptions in my "products" folder for the word "cheap"
or "basic" — words we never use — and flags any files that contain them?

In each case, you describe the outcome you want. Claude produces the script, explains what each part does, and walks you through how to set it up. You do not need to understand bash, cron, or JSON to have these tools working for you.


Practical Exercise

In this exercise you will ask Claude to design and write an automation script for a real, repetitive task in your own work — without writing any code yourself.

a. Think of a task you do repeatedly that involves reading, summarising, checking, or transforming files — something you do weekly or monthly that always follows the same pattern. Describe it to Claude in a new session:

I want to automate a repetitive task using Claude Code in headless mode.
Here is what I currently do manually: [describe the task in plain English —
what files are involved, what you do to them, what the output looks like]

Please design an automated script that does this for me, and include
the --print and --no-interactive flags so it can run without me present.
Explain each part of the script in plain English as you go.

Read through the script Claude produces. Ask it to explain any line you do not understand. You should be able to describe, in plain English, what each section does before you run it.

b. Ask Claude to add a safety feature — a log file that records what was processed and whether anything was flagged or skipped:

Please update the script to also write a log file that records:
- The date and time it ran
- How many files were processed
- The name of any file that was skipped or produced an unexpected result

Save the log to a file called run-log.txt in the same folder.

Review the updated script. Having a log means you can always check what the automation did, even days after it ran.

c. Ask Claude to walk you through running the script for the first time on a small test — a single file or a folder with two or three sample files:

I want to test this script before setting it up to run automatically.
Can you walk me through running it manually on a test folder with
just two or three files, so I can confirm the output looks right
before I rely on it?

Run the test as instructed. Check the output. If anything looks wrong, describe what you expected and what you got — Claude will adjust the script. Once the test output looks correct, the automation is ready to be scheduled.


Common problems and how to fix them

The script hangs and never finishes

This almost always means --no-interactive was not included. Claude reached an ambiguous point and paused for human input — which never came. Stop the script (press Ctrl+C in the terminal), confirm that both --print and --no-interactive are present in the Claude command inside the script, and run it again.

The output file is empty

If the script runs but the output file is empty, Claude likely produced an error message instead of the expected output — and that error was not captured. Ask Claude to add error handling to the script: “Please update the script so that if Claude returns an error for any file, it writes a note in the log file instead of leaving the output file empty.”

The cron job does not seem to be running

Cron jobs fail silently — they simply do not run, with no visible error. The most common causes are a wrong file path (the cron job cannot find the script), missing execute permissions on the script file, or an environment issue. Tell Claude: “My cron job does not appear to be running. Can you walk me through how to check whether it is scheduled correctly and how to test it manually?”

Claude’s output in headless mode is less accurate than in a normal session

Without the ability to ask follow-up questions, Claude must make assumptions when something is ambiguous. If the headless output is consistently missing something or interpreting instructions differently than you intended, the fix is almost always to make the prompt more specific. Tell Claude: “The automated output keeps [describe the problem]. How should I rewrite the prompt inside the script to make this more explicit?”

The JSON output cannot be read by the next tool in the pipeline

If the receiving tool cannot parse Claude’s JSON output, it usually means Claude included some extra text — a preamble, a note, a closing comment — alongside the JSON. Add a clear instruction to the prompt: “Return only valid JSON with no other text, no explanation, and no markdown formatting around it.” This tells Claude the output will be machine-read and must be clean.


What you have learned in this lesson

  • Headless mode lets Claude run without a conversation window, a human present, or any back-and-forth — input goes in, output comes out automatically, like a vending machine
  • CI (Continuous Integration) means automated checks or processes that run every time something changes in a project, without requiring manual triggering
  • The --print flag tells Claude to deliver its response and exit immediately; --no-interactive tells it never to pause waiting for human input — both are essential in any automated script
  • Without --no-interactive, an automated script can hang indefinitely the moment Claude encounters an ambiguous situation, because there is no human to respond
  • --output-format json produces structured, labelled output that other tools and systems can read reliably, rather than prose intended for a human
  • A cron job is a scheduled instruction that runs a script automatically at a specific time — daily, weekly, or any pattern — without any human involvement
  • Non-developers can benefit from all of this without writing a single line of code themselves: describe what you want to automate in plain English and Claude will write the scripts, explain them, and walk you through setting them up