Profile System
Profiles map to Claude Code CLI flags. Each profile is a YAML file that specifies the model, turn limits, permissions, prompt additions, and auto-PR behavior. The repo’s own CLAUDE.md stays untouched; profiles add behavior on top via --append-system-prompt and --settings.
Profile YAML Schema
| Field | Type | Default | CLI Flag | Description |
|---|---|---|---|---|
name | string | file stem | Profile identifier | |
description | string | "" | Human-readable description | |
claude.model | string | sonnet | --model | Claude model |
claude.max_turns | int | 80 | --max-turns | Max agentic turns |
claude.output_format | string | json | --output-format | Output format |
claude.permission_mode | string | dangerously-skip-permissions | --dangerously-skip-permissions | Permission mode |
claude.timeout | int | 3600 | (process timeout) | Max seconds |
claude.worktree | bool | true | --worktree | Use worktree isolation |
append_prompt | string | "" | --append-system-prompt | Additional system prompt (supports templates) |
settings.permissions | object | {} | --settings | Permission allowlist |
claude_md | string/null | null | Custom CLAUDE.md content (reserved) | |
auto_pr | bool | true | Create PR on success |
Bundled Profiles
code
Autonomous coding worker. Default profile for most tasks.
name: code
description: "Autonomous coding worker"
claude:
model: sonnet
max_turns: 80
output_format: json
permission_mode: dangerously-skip-permissions
timeout: 3600
worktree: true
append_prompt: |
## Job Context
Task:
Repository:
Base branch:
## Guidelines
- Commit with descriptive messages
- Do NOT create PRs — the system handles that
settings:
permissions:
allow:
- "Bash(*)"
- "Read(*)"
- "Write(*)"
- "Edit(*)"
auto_pr: true
review
Code review analyst. Read-only, uses a faster model.
name: review
description: "Code review analyst"
claude:
model: haiku
max_turns: 20
output_format: json
permission_mode: dangerously-skip-permissions
worktree: true
append_prompt: |
## Task
## Guidelines
- Do NOT modify files
- Provide structured feedback
auto_pr: false
Custom Profiles
Profiles are loaded from up to three directories, in priority order (highest wins):
defaults/profiles/ <-- bundled (shipped with package)
code/profile.yml
review/profile.yml
{AGENTICORE_AGENTIHOOKS_PATH}/profiles/ <-- external (agentihooks repo)
default/profile.yml <-- loaded only if env var is set
review/profile.yml
~/.agenticore/profiles/ <-- user overrides (always checked)
code/profile.yml <-- overrides bundled code
deploy/profile.yml <-- new custom profile
Loading order: bundled defaults first, then agentihooks (if AGENTICORE_AGENTIHOOKS_PATH is set), then user profiles. Same-name profiles from a higher-priority directory replace the lower-priority version entirely.
Agentihooks Integration
Set AGENTICORE_AGENTIHOOKS_PATH to the root of a cloned agentihooks repo (e.g. /app). Agenticore will load profiles from {path}/profiles/. These profiles can include pre-wired hook events in their settings.json — agenticore materializes settings.json into the working directory as-is, and Claude Code picks up the hooks automatically. No hook-specific logic exists in agenticore.
Template Variables
The append_prompt field supports template variables that are rendered at job execution time:
| Variable | Value | Source |
|---|---|---|
| `` | Task description | job.task |
| `` | Repository URL | job.repo_url |
| `` | Base branch | job.base_ref |
| `` | Job UUID | job.id |
| `` | Profile name | job.profile |
Templates use simple string replacement (`` to value).
Profile to CLI Args
+------------------+
| Profile YAML |
| code.yml |
+--------+---------+
|
v
+--------+---------+
| build_cli_args |
| (profiles.py) |
+--------+---------+
|
v
+--------+------------------------------------------+
| claude --worktree |
| --model sonnet |
| --max-turns 80 |
| --output-format json |
| --dangerously-skip-permissions |
| --append-system-prompt "## Job Context..."|
| --settings '{"permissions":{...}}' |
| -p "fix the auth bug" |
+---------------------------------------------------+
The build_cli_args() function in profiles.py converts a Profile dataclass into a list of CLI arguments. The task prompt is always appended last with -p.
Profile Resolution
Request arrives with profile=""
|
v
+--------+---------+
| router.py |
| route() |
+--------+---------+
|
+-----+-----+
| |
v v
profile no profile
specified? specified
| |
v v
validate use default
exists? (config.claude.default_profile)
| |
+------+------+
|
v
resolved profile name
If an explicit profile is requested but doesn’t exist, the router falls back to the configured default profile (usually code).
See Job Execution for how profiles are used during the runner pipeline.