Skip to content

Headless Mode

Run KosmoKrator non-interactively for CI/CD pipelines, shell scripts, git hooks, and automated workflows. Pass a prompt, get output, exit. No TTY required.

The three ways to invoke headless mode:

Terminal window
# Positional prompt — the simplest invocation
kosmokrator "fix the off-by-one error in src/Math.php"
# Explicit -p flag
kosmo -p "list all TODO comments in the codebase"
# Stdin pipe
echo "explain this error" | kosmo

All three produce the same result: the agent runs to completion, writes the final response to stdout, and exits with code 0 on success. Progress and diagnostics go to stderr, so result=$(kosmo -p "task") captures only the response.

This CLI path is backed by the same Agent SDK runtime exposed to PHP applications through Kosmokrator\Sdk\AgentBuilder. Use the SDK when you want headless execution inside a web app, worker, daemon, or another PHP automation surface.

For the complete command matrix used by wrappers and other coding CLIs, see CLI Reference. For every configurable setting and effect timing, see Settings Reference.

Tip: Combine stdin with a positional prompt — stdin is appended after the prompt. Great for injecting file contents into a task:

Terminal window
cat error.log | kosmo -p "explain this error and suggest a fix"

If you do not need an agent turn and only want to call external tools such as Plane, ClickUp, or other OpenCompany integration packages, use the dedicated integrations CLI instead:

Terminal window
# Discover configured providers
kosmo integrations:status --json
# Read the schema for one integration function
kosmo integrations:schema plane.list_issues
# Call an integration directly
kosmo integrations:call plane.list_issues \
--workspace-slug=kosmokrator \
--project-id=PROJECT_UUID \
--json
# Run a multi-step Lua workflow against configured integrations
kosmo integrations:lua workflow.lua --json

This integration surface is designed for scripts and other coding CLIs. It is documented in detail in Integrations CLI.

Integration calls remain governed in headless mode. Read/write permissions configured with integrations:configure --read=..., --write=..., or integrations.permissions_default are checked before execution. A trusted automation job can pass --force to bypass only that integration read/write policy.

MCP servers can also be used without an agent turn. KosmoKrator reads the common project .mcp.json shape, plus VS Code/Cursor servers files, then exposes the servers through mcp:* commands and Lua as app.mcp.*.

Terminal window
# Add a portable project server
kosmo mcp:add github --project --type=stdio \
--command=github-mcp-server --env GITHUB_TOKEN --json
# Review and trust project server command before discovery/execution
kosmo mcp:list --json
kosmo mcp:trust github --project --json
kosmo mcp:tools github --json
kosmo mcp:schema github.search_repositories --json
# Call directly or through a per-server shortcut
kosmo mcp:call github.search_repositories --query="kosmokrator" --json
kosmo mcp:github search_repositories --query="kosmokrator" --json
# Run a Lua workflow against MCP servers
kosmo mcp:lua workflow.lua --json

Headless MCP keeps project trust and read/write policy checks. Project MCP config can start local processes, so normal execution requires mcp:trust. Tool calls also check kosmo.mcp.servers.SERVER.permissions.read or .write; ask fails in pure headless mode because there is no approval modal. Use --force only for trusted automation that should bypass both MCP trust and MCP read/write policy for that single invocation.

See MCP for config compatibility, secrets, resources, prompts, Lua helpers, and the full command reference.

External web providers can be configured and tested without opening a TUI. They are opt-in and do not replace native web_fetch; external fetch and crawl remain separate provider-backed tools.

Terminal window
# Inspect available providers and effective configuration
kosmokrator web:providers --json
kosmokrator web:doctor --json
# Configure provider keys from environment variables
kosmokrator web:configure tavily --api-key-env=TAVILY_API_KEY \
--enable --search --global --json
kosmokrator web:configure firecrawl --api-key-env=FIRECRAWL_API_KEY \
--enable --fetch --crawl --project --json
# Call providers directly from scripts
kosmokrator web:search "latest Symfony TUI changes" --provider=tavily --json
kosmokrator web:fetch https://symfony.com/blog/ --provider=firecrawl --json
kosmokrator web:crawl https://docs.example.com --provider=tavily --max-pages=10 --json

Supported providers are Tavily, Firecrawl, Exa, Brave Search, Parallel, Jina Reader/Search, SearXNG, Perplexity, OpenAI native web search, and Anthropic native web search. Provider API keys can also be stored with web:configure --api-key-stdin or secrets:set provider.PROVIDER.api_key.

You can bootstrap KosmoKrator itself without opening the setup wizard or settings TUI:

Terminal window
# Configure an LLM provider and credentials
printf %s "$OPENAI_API_KEY" | \
kosmo providers:configure openai --model gpt-5.4-mini \
--api-key-stdin --global --json
# Discover and set normal settings
kosmo settings:list --json
kosmo settings:set tools.default_permission_mode guardian --global --json
# Batch apply settings from JSON
jq -n '{settings:{"agent.mode":"plan","context.max_output_lines":1200}}' | \
kosmo settings:apply --stdin-json --global --json
# Store managed secrets without echoing raw values
printf %s "$OPENAI_API_KEY" | \
kosmo secrets:set provider.openai.api_key --stdin --json
# Store MCP server secrets without committing tokens to .mcp.json
printf %s "$GITHUB_TOKEN" | \
kosmo mcp:secret:set github env.GITHUB_TOKEN --stdin --json
# Configure Telegram gateway without the TUI
printf %s "$TELEGRAM_BOT_TOKEN" | \
kosmo gateway:telegram:configure --token-stdin \
--enabled on --session-mode thread_user --global --json

Use settings:doctor --json, providers:list --json, and secrets:list --json as the agent-friendly discovery path. See Configuration and Providers for the full command reference.

Control the output format with -o / --output-format:

Terminal window
# Human-readable text (default)
kosmo -p "list the files in src/"
# Single JSON blob at completion
kosmo -p -o json "what does the AgentLoop class do?"
# Streaming NDJSON events as they happen
kosmo -p -o stream-json "refactor the auth module"

The default. Final response on stdout, progress on stderr:

Terminal window
$ kosmo -p "how many PHP files are in src/?"
[Working] stderr (dimmed)
bash(command: find src/ -name "*.php" | wc -l) stderr
bash: 142 stderr
tokens: 1234→567 cost: $0.03 stderr
There are 142 PHP files in the src/ directory. stdout

Tip: Only stdout contains the result. Stderr is for human eyes only. This makes text mode ideal for scripting:

Terminal window
# Capture just the result
result=$(kosmo -p "generate a migration for users table")
# Show progress live, capture result
kosmo -p "run the test suite" 2>/dev/null

A single structured JSON blob written to stdout when the agent finishes. Useful for programmatic consumption:

Terminal window
$ kosmo -p -o json "list the routes"
{
"type": "result",
"text": "The application defines these routes:\n1. GET / ...",
"duration_ms": 4500,
"turns": 0,
"usage": {
"tokens_in": 1234,
"tokens_out": 567
},
"errors": [],
"tool_calls": [
{"name": "bash", "args": {"command": "php artisan route:list"}, "output": "...", "success": true}
]
}

Emits NDJSON events to stdout as they happen. Each line is a complete JSON object. Designed for IDE integrations, real-time dashboards, and long-running agent monitoring:

Terminal window
$ kosmo -p -o stream-json "analyze the codebase"
{"type":"user_message","text":"analyze the codebase","timestamp":1712570000000}
{"type":"phase","phase":"thinking","timestamp":1712570000100}
{"type":"text_delta","delta":"I'll start by ","timestamp":1712570005000}
{"type":"text_delta","delta":"examining the directory structure.","timestamp":1712570005100}
{"type":"tool_call","name":"bash","args":{"command":"find src/ -type f | head -30"},"timestamp":1712570006000}
{"type":"tool_result","name":"bash","output":"src/Agent/AgentLoop.php\n...","success":true,"timestamp":1712570007000}
{"type":"result","text":"The codebase follows...","duration_ms":8000,"turns":3,"usage":{"tokens_in":2345,"tokens_out":890},"timestamp":1712570010000}

Event types:

EventDescription
user_messageThe prompt sent to the agent
phaseAgent phase transition (thinking, working, idle)
text_deltaIncremental text from the LLM response
reasoningExtended thinking content (if supported by model)
tool_callA tool is being invoked with name and arguments
tool_resultTool execution result with success status
subagent_spawnA child agent was spawned
subagent_batchChild agent(s) completed
stream_endThe current text stream completed
errorAn error occurred during execution
resultFinal result with usage stats and duration

All headless-related flags. Combine freely:

FlagAliasDescription
[prompt]Positional task prompt (enables headless mode)
--print-pExplicit headless mode
--output-format-oOutput format: text, json, stream-json
--model-mOverride model for this run
--modeAgent mode: edit, plan, ask
--yoloAuto-approve governed permission prompts (alias for --permission-mode prometheus)
--permission-modePermission mode: guardian, argus, prometheus
--max-turns-tMaximum agentic turns (LLM call cycles)
--timeoutMaximum runtime in seconds
--continue-cContinue the most recent session
--resumeResume last session (same as --continue)
--sessionResume a specific session by ID
--system-promptReplace the system prompt entirely
--append-system-promptAppend to the default system prompt
--no-sessionDon’t persist the session to disk

In headless mode, there is no terminal dialog for approval. KosmoKrator still evaluates the configured permission mode, denied tools, blocked paths, agent mode, integration read/write policy, and file path validation before executing work. Calls that would require an unavailable prompt fail with a structured error unless you explicitly choose an auto-approval policy.

Use --yolo to explicitly opt into Prometheus-style auto-approval for agent tool calls:

Terminal window
# Auto-approve governed permission prompts
kosmo -p --yolo "run the full test suite and fix any failures"

Or use --permission-mode for explicit control. For direct integration commands, use --force when a trusted automation job should bypass integration read/write policy:

Terminal window
# Use Guardian mode (auto-approve safe tools, deny risky ones)
kosmo -p --permission-mode guardian "add type hints to src/Utils.php"
# Use Argus mode (deny tools that would normally ask)
kosmo -p --permission-mode argus "what files are in the project?"
# Bypass integration read/write policy for this direct call only
kosmo integrations:call plane.create_issue --force --json

See Permissions for details on each mode.

For autonomous runs, protect against runaway agents with --max-turns and --timeout:

Terminal window
# Maximum 10 LLM call cycles
kosmo -p --max-turns 10 "refactor the database layer"
# Maximum 5 minutes
kosmo -p --timeout 300 "implement user authentication"
# Combine both
kosmo -p --max-turns 20 --timeout 600 "rewrite the API module"

Tip: One “turn” is one LLM call cycle. A turn that triggers tool calls counts as one turn — the tool execution and follow-up LLM call is the next turn.

Headless runs create sessions by default, just like interactive mode. This means you can resume them later:

Terminal window
# Run a task (creates a session)
kosmo -p "analyze the authentication flow"
# Continue that session later
kosmo -p -c "now implement the suggested improvements"
# Resume a specific session by ID
kosmo -p --session abc123 "what was my last question about?"

Use --no-session for ephemeral runs where you don’t need session history:

Terminal window
# Quick one-off question, no session persisted
kosmo -p --no-session "what does this regex do? /([A-Z])\w+/"

Override the configured model for a single run:

Terminal window
# Use a specific model
kosmo -p -m sonnet "quick code review of src/Http/"
# Use a cheaper model for simple tasks
kosmo -p -m haiku "generate a .gitignore for a PHP project"

The model name matches your provider’s model identifier. See Providers for available models.

Control which tools the agent can use:

Terminal window
# Full access (default) — all tools available
kosmo -p --mode edit "fix the login bug"
# Read-only — analyze without modifying files
kosmo -p --mode plan "design a caching strategy for the API"
# Ask mode — read files and run bash, but no file writes
kosmo -p --mode ask "what does the QueueWorker do?"
ModeFile ReadFile WriteBashSubagents
edit (default)
plan×
ask××

Customize the agent’s behavior by modifying the system prompt:

Terminal window
# Append instructions to the default prompt
kosmo -p --append-system-prompt "Always use PSR-12 coding style" "refactor src/"
# Replace the entire system prompt
kosmo -p --system-prompt "You are a security auditor. Find vulnerabilities." "audit src/Auth/"
CodeMeaning
0Success — agent completed the task
1Error — agent encountered an error or returned an error response
2Limit exceeded — max turns or timeout reached
130Cancelled — interrupted by SIGINT (Ctrl+C)
143Cancelled — interrupted by SIGTERM
Terminal window
# Use in shell scripts
kosmo -p "run tests"
if [ $? -eq 0 ]; then
echo "Tests passed!"
elif [ $? -eq 2 ]; then
echo "Agent hit a guardrail limit"
fi
name: AI Code Review
on: [pull_request]
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup KosmoKrator
run: |
curl -sSL https://kosmokrator.dev/install.sh | bash
printf %s "${{ secrets.ANTHROPIC_API_KEY }}" | \
kosmo setup --provider anthropic --api-key-stdin --global --json
- name: Run AI Review
run: |
kosmo -p --no-session --max-turns 5 \
"Review the changed files in this PR. Focus on security,
performance, and code style. Output a summary." \
2>review-progress.txt >review-result.txt
- name: Post Review Comment
if: success()
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const review = fs.readFileSync('review-result.txt', 'utf8');
github.rest.issues.createComment({
...context.repo,
issue_number: context.issue.number,
body: review
});
#!/bin/bash
# .git/hooks/pre-commit — AI-assisted commit message validation
# Requires: kosmokrator in PATH
staged=$(git diff --cached --name-only)
if [ -z "$staged" ]; then exit 0; fi
issues=$(kosmo -p --no-session --max-turns 3 --timeout 30 \
"Check these staged files for obvious bugs, debugging leftovers
(dd, dump, console.log), and TODO comments that should block commit.
Files: $staged
Output ONLY 'PASS' if everything looks good, or list the issues." \
2>/dev/null)
if [ "$issues" != "PASS" ]; then
echo "AI pre-commit check found issues:"
echo "$issues"
exit 1
fi
Terminal window
# Makefile
.PHONY: ai-review ai-docs ai-test
ai-review:
@kosmo -p --max-turns 10 --timeout 300 \
"Review the codebase for security issues, performance problems, \
and code style violations. Prioritize actual bugs over style nits."
ai-docs:
@kosmo -p --mode plan \
"Generate API documentation for all public methods in src/. \
Output markdown suitable for a docs site."
ai-test:
@kosmo -p --yolo \
"Run the test suite. If any tests fail, fix them and re-run."

Process multiple files or tasks sequentially:

#!/bin/bash
# Add type hints to all PHP files in a directory
for file in src/Service/*.php; do
echo "Processing $file..."
kosmo -p --no-session --max-turns 3 \
"Add strict type declarations and parameter type hints to $file. \
Only modify the file if types are missing."
done
#!/bin/bash
# Get structured results with jq
result=$(kosmo -p -o json --no-session "list all PHP classes in src/")
echo "Tokens used: $(echo "$result" | jq '.usage.tokens_in') in, $(echo "$result" | jq '.usage.tokens_out') out"
echo "Duration: $(echo "$result" | jq '.duration_ms')ms"
echo "Tool calls: $(echo "$result" | jq '.tool_calls | length')"
echo "Response:"
echo "$result" | jq -r '.text'
#!/usr/bin/env python3
"""Real-time agent monitoring via stream-json."""
import sys, json, subprocess
proc = subprocess.Popen(
['kosmokrator', '-p', '-o', 'stream-json', 'refactor the auth module'],
stdout=subprocess.PIPE, stderr=subprocess.DEVNULL,
text=True
)
for line in proc.stdout:
event = json.loads(line)
match event['type']:
case 'tool_call':
print(f" → {event['name']}({list(event['args'].keys())[0]}: ...)")
case 'tool_result':
status = '' if event['success'] else ''
print(f" {status} {event['name']}")
case 'result':
print(f"\nDone in {event['duration_ms']}ms")
print(event['text'])
proc.wait()
sys.exit(proc.returncode)
FeatureInteractiveHeadless
Input methodREPL promptCLI arg, stdin, or -p
OutputTUI / ANSI rendererstdout (text, JSON, stream-json)
Tool permissionsInteractive promptsPolicy evaluated; use --yolo for agent auto-approval
Plan approvalInteractive dialogPlans output but not auto-implemented
Ask user/choiceInteractive promptsReturns empty/dismissed
Slash commandsAvailableNot available
Session persistenceYesYes (disable with --no-session)
Session resume--resume--continue, --resume, --session
SubagentsFull supportFull support
Stuck detectionNoYes (auto-nudge and force-return)
Max turns / timeoutNot available--max-turns, --timeout

If you’re coming from another coding agent, here’s how the headless flags map:

You’re used toKosmoKrator equivalent
Claude Code -pkosmo -p (identical)
Claude Code --output-format jsonkosmo -o json
Claude Code --output-format stream-jsonkosmo -o stream-json
Claude Code --dangerously-skip-permissionskosmo --yolo
Claude Code --continuekosmo -c (identical)
Claude Code --max-turnskosmo --max-turns (identical)
Claude Code --modelkosmo -m
Codex CLI codex exec "task"kosmo "task"
Codex CLI --full-autokosmo --yolo
Codex CLI -qkosmo -p
Aider --message "task"kosmo -p "task"
Aider --yes-alwayskosmo --yolo
Goose goose run -t "task"kosmo "task"
OpenCode opencode run "task"kosmo "task"