Skip to main content

Remote Control & Headless Automation

8 min read

What You Will Learn

Claude Code doesn’t have to live inside your terminal window. This chapter shows you how to access your local session from a phone or browser via remote control, run Claude Code programmatically with the -p flag for scripts and CI/CD pipelines, and set up recurring tasks with cron expressions so work happens even when you’re not watching.

Remote Control

Remote control lets you access your local Claude Code session from any device, whether a phone, tablet, or another computer, using just a web browser. Your machine connects outbound to Anthropic’s relay service over HTTPS, so there’s nothing to configure on the network side. No inbound ports get opened, no firewall rules need changing, and no VPN is required.

To get started, fire up a remote control session from your terminal.

Starting remote control
Starting remote control
claude remote-control

Claude Code displays a QR code and a URL. Scan the QR code from your phone or open the URL in any browser, and you’re connected to the same session running on your machine.

Already have an active Claude Code session? You can start remote control from within it using the slash command.

Starting remote control from an existing session
Starting remote control from an existing session
/remote-control

Generates a QR code and URL without starting a new session.

Once you’re connected, everything you type in the remote browser gets sent to your local Claude Code instance and responses appear in real time. Claude Code still runs on your machine, reads your local files, and executes commands in your local environment. The remote connection is really just a display and input relay.

Remote vs Web Sessions

These two might sound similar, but they’re fundamentally different.

Remote control means Claude runs locally on your machine. It has full access to your files, terminal, Git history, and entire development environment. The phone or browser is just a window into that local session. When Claude Code edits a file through remote control, the change hits your disk immediately.

Web sessions at console.anthropic.com run on Anthropic’s cloud infrastructure. Claude operates in a sandboxed cloud environment with no access to your local filesystem. They’re useful for quick questions, exploring ideas, or working when you’re away from your development machine.

Reach for remote control when you need Claude Code to work with your local project: reviewing code from your phone during a commute, pairing with a colleague by sharing the URL, or picking up a task from a different room. Go with web sessions when you don’t need local file access.

Programmatic Usage with -p

The -p flag runs Claude Code with a single prompt and exits when it’s done. This is your entry point for integrating Claude Code into scripts, CI/CD pipelines, and automated workflows.

Running a single prompt
Running a single prompt
claude -p "Explain the main function in src/index.ts"

Claude Code processes the prompt, writes output to stdout, and exits.

Here’s headless mode in action, running prompts, getting JSON output, and piping results to other tools:

Headless mode with -p flag: text output, JSON output, and piping
View as text (accessible transcript)
$ claude -p 'List all exported functions in this project'

Functions: isValidEmail, authMiddleware, default router (auth routes)
Interfaces: CreateUserInput, LoginInput, UserPayload, AuthenticatedRequest

$ claude -p 'How many TypeScript files are in src/?' --output-format json | jq .

{ "result": "There are 6 TypeScript files...", "cost": 0.003, ... }

$ claude -p 'Generate a one-line summary of this project' | cat -n

1  A Node.js REST API with JWT authentication built using Express and TypeScript.
Headless mode with -p flag: text output, JSON output, and piping

Output comes back as plain text by default. You can change that with --output-format.

Output format options
Output format options
# Plain text (default)
claude -p "List all exported functions in src/utils.ts" --output-format text
# Structured JSON with metadata
claude -p "List all exported functions in src/utils.ts" --output-format json
# Streaming JSON (one event per line)
claude -p "List all exported functions in src/utils.ts" --output-format stream-json

The JSON format gives you a structured response with the result text, token usage, cost, and session metadata, making it easy to parse Claude Code output in scripts.

Example JSON output
Example JSON output
{
"result": "The file exports three functions: parseConfig, validateInput, formatOutput...",
"cost": 0.003,
"duration_ms": 2400,
"session_id": "abc123",
"num_turns": 1
}

Exact fields may vary. Use this format for programmatic consumption.

With stream-json, you get one JSON object per line as Claude Code processes the prompt. Each line represents an event, such as an assistant message, a tool call, or a tool result. It’s great for real-time monitoring in pipelines where you’d rather see progress as it happens instead of waiting for the final result.

Structured Output with —json-schema

When you need Claude Code to return data in a specific shape, pass a JSON schema with the --json-schema flag. Claude Code will structure its response to match.

Extracting structured data with a JSON schema
Extracting structured data with a JSON schema
claude -p "Analyze src/auth.ts and extract all function signatures" --output-format json --json-schema '{
"type": "object",
"properties": {
"functions": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": { "type": "string" },
"params": { "type": "array", "items": { "type": "string" } },
"returnType": { "type": "string" }
},
"required": ["name", "params", "returnType"]
}
}
},
"required": ["functions"]
}'

The response conforms to whatever schema you provide.

Structured response matching the provided schema
Structured response matching the provided schema
{
"functions": [
{
"name": "validateToken",
"params": ["token: string", "options?: AuthOptions"],
"returnType": "Promise<TokenPayload>"
},
{
"name": "refreshSession",
"params": ["sessionId: string"],
"returnType": "Promise<Session>"
}
]
}

This opens up a lot of possibilities for building tooling on top of Claude Code. You can extract code metrics, generate documentation, audit dependencies, or build custom analysis pipelines, all with predictable output shapes that your downstream tools can rely on.

System Prompt Customization

Two flags let you control the system prompt Claude Code uses when running with -p.

--system-prompt replaces the default system prompt entirely. Reach for this when you want Claude Code to behave in a completely different way from its defaults.

Replacing the system prompt
Replacing the system prompt
claude -p "Review this PR" --system-prompt "You are a strict code reviewer. Focus only on security issues and performance problems. Ignore style."

--append-system-prompt adds to the default system prompt instead of replacing it. This is what you’ll want when Claude Code should keep its default capabilities but also follow additional project-specific rules.

Appending to the system prompt
Appending to the system prompt
claude -p "Add a new API endpoint for user preferences" --append-system-prompt "Always use the repository's existing error handling patterns. Never add new dependencies without justification."

In most cases, --append-system-prompt is the safer choice. The default system prompt contains important instructions about tool usage, safety, and output formatting. Replacing it entirely with --system-prompt strips all of that out, which can lead to unexpected behavior.

Auto-Approving Tools in Scripts

When you run Claude Code with the -p flag, every tool invocation that would normally need your approval blocks the process. In a script or CI/CD pipeline, there’s nobody around to click “approve.” You need to explicitly declare which tools Claude Code can use without asking.

The --allowedTools flag handles this. It uses the same permission rule syntax described in Models, Cost Economics & Permissions.

Allowing specific tools for headless execution
Allowing specific tools for headless execution
claude -p "Fix the failing tests" --allowedTools "Bash(npm test)" "Bash(npm run lint)" "Edit" "Write"

Without --allowedTools, Claude Code will fail on any action requiring permission.

You can be as specific or as broad as the task calls for.

Permission granularity examples
Permission granularity examples
# Allow all bash commands (broad)
claude -p "Set up the project" --allowedTools "Bash(*)"
# Allow only specific operations (narrow)
claude -p "Run the test suite and fix failures" --allowedTools "Bash(npm test)" "Bash(npx vitest run)" "Edit"
# Allow file operations only (no command execution)
claude -p "Refactor the utils module" --allowedTools "Edit" "Write"

If you run Claude Code with -p and don’t provide --allowedTools, any action that requires approval causes the process to exit with an error. Always specify the tools your script needs.

Session Management

By default, each claude -p invocation starts a fresh session with no prior context. For multi-step workflows where you want to build on what happened before, use session continuation flags.

--continue picks up from the most recent session.

Continuing the most recent session
Continuing the most recent session
# Step 1: Analyze the codebase
claude -p "Analyze the authentication module and identify security issues" --output-format json > analysis.json
# Step 2: Continue the same session to fix the issues found
claude -p "Fix the security issues you identified" --continue --allowedTools "Edit" "Bash(npm test)"

--resume resumes a specific session by ID. This comes in handy when you’re running multiple pipelines in parallel and need to continue a particular session.

Resuming a specific session by ID
Resuming a specific session by ID
# Get the session ID from JSON output
SESSION_ID=$(cat analysis.json | jq -r '.session_id')
# Resume that specific session
claude -p "Now write tests for the fixes" --resume "$SESSION_ID" --allowedTools "Write" "Bash(npm test)"

Here’s --continue in action. The second prompt picks up context from the first without rereading the codebase:

Stateful headless sessions with --continue
View as text (accessible transcript)
$ claude -p 'What authentication method does this project use?'

This project uses JWT (JSON Web Token) authentication...

$ claude --continue -p 'What are the security risks with that approach?'

Claude remembers the previous conversation about JWT auth
and discusses risks without needing to re-read the codebase:
- Token theft if not stored securely
- No built-in revocation mechanism
- Secret key management concerns
Stateful headless sessions with --continue

Session continuation preserves the full conversation history, including which files Claude Code read, what changes it made, and any context it accumulated. This keeps multi-step workflows coherent without you having to repeat instructions.

Scheduled Tasks

Claude Code can run tasks on a recurring schedule. The /loop skill lets you create scheduled tasks conversationally. Just describe what you want to happen and when, and Claude Code sets up the automation for you.

Example: creating a scheduled task with /loop
Example: creating a scheduled task with /loop
Every morning at 9 AM, check for outdated npm dependencies
and create a summary report in reports/deps-check.md

Claude Code parses the schedule and creates the cron job for you.

Behind the scenes, three tools power scheduled task management.

CronCreate registers a new recurring task with a cron expression, a prompt to execute, and optional tool permissions.

CronList shows all your registered scheduled tasks along with their schedules, last run time, and next scheduled run.

CronDelete removes a scheduled task by its ID.

You can interact with these tools directly, but the /loop skill is more natural. Just describe your automation need in plain language and Claude Code translates it into the right cron configuration.

Cron Expressions

Scheduled tasks use standard 5 field cron expressions: minute, hour, day of month, month, and day of week.

Cron expression format and common examples
Cron expression format and common examples
# ┌─── minute (0-59)
# │ ┌─── hour (0-23)
# │ │ ┌─── day of month (1-31)
# │ │ │ ┌─── month (1-12)
# │ │ │ │ ┌─── day of week (0-6, Sunday=0)
# │ │ │ │ │
# * * * * *
# Every weekday at 9 AM
0 9 * * 1-5
# Every 30 minutes
*/30 * * * *
# Every Sunday at midnight
0 0 * * 0
# First day of every month at 8 AM
0 8 1 * *

Scheduled tasks run in your local timezone, not UTC. This is an important detail when you’re scheduling tasks around your work hours.

Critical limitation: tasks expire after 3 days. If Claude Code Desktop isn’t running for 3 days, scheduled tasks created through the CLI expire and won’t resume automatically. This is by design; CLI created cron tasks are meant for short-lived automation, not permanent infrastructure. For something more durable, see the alternatives below.

Durable Scheduling Alternatives

When you need scheduled tasks that survive restarts, machine reboots, or extended offline periods, reach for one of these alternatives instead of CLI cron tasks.

Claude Code Desktop scheduled tasks are persistent. Tasks created through the Desktop app survive restarts and don’t have the 3 day expiry limitation. If your automation needs to keep running reliably for weeks or months, create the schedule through the Desktop app.

GitHub Actions with schedule trigger moves the automation entirely to CI. The task runs on GitHub’s infrastructure regardless of whether your local machine is on.

.github/workflows/scheduled-review.yml
.github/workflows/scheduled-review.yml
name: Weekly Dependency Review
on:
schedule:
- cron: '0 9 * * 1' # Every Monday at 9 AM UTC
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Claude Code
run: curl -fsSL https://claude.ai/install.sh | bash
- name: Run dependency review
run: |
claude -p "Review package.json for outdated or vulnerable dependencies. Create a report." --output-format text --allowedTools "Bash(npm audit)" "Bash(npm outdated)" "Read" > dependency-report.md
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

Runs in CI with no local machine dependency. Note the cron uses UTC.

So which do you pick? CLI cron works well for quick, temporary automation (checking something for the next few days). Desktop scheduled tasks are your go to for persistent local automation. And GitHub Actions is the right call for durable, team visible automation that runs regardless of any individual machine’s state.

Best Practices

  • Use -p for CI/CD integration. It’s the entry point for every automated Claude Code workflow. Pair it with --output-format json for machine readable output and --allowedTools for unattended execution.

  • Prefer --append-system-prompt over --system-prompt. The default system prompt has safety and formatting instructions you almost always want to keep. Append project-specific rules rather than replacing everything.

  • Always specify --allowedTools in scripts. Without explicit tool permissions, Claude Code exits with an error on any action that requires approval. Specify the minimum set of tools your script actually needs.

  • Use JSON output for programmatic consumption. The structured JSON response includes token usage, cost, and session ID alongside the result, making it straightforward to build dashboards, cost tracking, and audit logs.

  • Test cron schedules with short intervals first. Before setting up a daily cron, try */5 * * * * (every 5 minutes) to verify the task works as expected, then switch to the production schedule.

  • Prefer Desktop scheduled tasks or GitHub Actions over CLI cron for anything durable. CLI cron tasks expire after 3 days. If the automation matters, use a scheduling mechanism that persists.

  • Use --continue for multi-step pipelines. Break complex automated workflows into sequential steps that build on each other’s context, rather than cramming everything into one massive prompt.

Further Reading

Up next, learn how to extend Claude Code with external tools and data sources in Model Context Protocol.