# Prompt construction
[Feature docs index](README.md) · [Repository README](../../README.md)
## Purpose
Explain how `magi-code` builds provider-visible prompt context: static templates, runtime facts, `AGENTS.md` files, tools, skills, subagents, provider request mapping, and customization points.
## Prompt assembly
`AgentSession::new_with_prompt_dir_and_subagents` builds one assistant system prompt before each agent session starts. `src/agent/prompt.rs` renders sections in this order:
1. `prompts/system.md`
2. `prompts/tools.md`
3. optional generated subagent identity section
4. `prompts/skills.md`
5. generated dynamic `AGENTS.md` context files
Empty sections are skipped. Non-empty sections are joined with one blank line and the final prompt ends with `\n`.
Progressive shape:
```xml
<SYSTEM>
Core magi-code behavior rules.
</SYSTEM>
<USER-SYSTEM>
- ripgrep availability
- operating system
- terminal indicators
</USER-SYSTEM>
<Tools>
General tool-use guidance; provider-native tool definitions are supplied separately.
</Tools>
<Subagent-Identities>
Optional identity metadata when configured.
</Subagent-Identities>
<Available-Skills>
- skill-name: frontmatter description
</Available-Skills>
<Additional-Context-Files>
These files are automatically injected and MUST ALWAYS be remembered and considered in every action, do NOT read these files again:
<Context File=/Users/example/.magi-code/AGENTS.md>
raw user instruction file
</Context>
<Context File=/repo/AGENTS.md>
raw repo instruction file
</Context>
</Additional-Context-Files>
```
## Template fragments
Bundled defaults live in `prompts/`. User overrides live in `$MC_HOME/prompts/` or `~/.magi-code/prompts/` and are selected per fragment; missing overrides fall back independently to bundled defaults.
| `system.md` | Core assistant identity, global behavior, bounded runtime facts. | `{{has_ripgrep}}`, `{{operating_system}}`, `{{terminal_environment}}` |
| `tools.md` | General built-in tool-use guidance; it does not enumerate tool definitions. | runtime fact variables, `{{SKILLS_LIST}}` |
| `subagents.md` | Template for generated subagent identity metadata. | `{{LIST_SUBAGENTS}}` at generation site |
| `skills.md` | Human-readable enabled skill list and skill-use rule. | `{{SKILLS_LIST}}` |
| `compact.md` | `/compact` summary instruction prompt, not part of normal system prompt. | runtime fact variables |
`{{TOOLS_LIST}}` is unsupported. A custom prompt fragment containing it fails fast as an unsupported variable.
Template variables are exact and case-sensitive. Extra whitespace inside braces is invalid. Unknown or malformed variables fail prompt construction with fragment name and offending token.
Runtime facts are local and bounded:
- `{{has_ripgrep}}`: result of a timed `rg --version` probe.
- `{{operating_system}}`: friendly Rust target OS name.
- `{{terminal_environment}}`: sanitized `TERM_PROGRAM`, `TERM`, and `COLORTERM` values only.
## Dynamic `AGENTS.md` context
Instruction discovery is intentionally narrow and ordered:
1. user file: `$MC_HOME/AGENTS.md` / `~/.magi-code/AGENTS.md`
2. repository file: active project root `AGENTS.md`
Discovered files are appended after tool, subagent, and skill sections inside `<Additional-Context-Files>`. Each file uses:
```xml
<Context File=PATH>
raw file content
</Context>
```
Why delimiter matters:
- `Additional-Context-Files` separates injected file material from static product prompt.
- `Context File=PATH` preserves source path directly beside raw content.
- Raw content remains unescaped, so author `AGENTS.md` as trusted instruction text, not data.
- Prompt explicitly tells model not to read same files again because content is already injected.
If no files are discovered, prompt contains `No AGENTS.md instruction files were discovered.`
## Tools in prompt vs provider schema
Tool-use guidance and tool definitions are separate surfaces:
| Prompt text | Bundled or overridden `prompts/tools.md` guidance | Explain how to use the bounded tool interfaces. |
| Provider-native tool descriptions and JSON schemas | `mvp_tool_definitions_json_with_subagents(...)` | Let the provider emit validated function/tool calls. |
Tool names, descriptions, and schemas come from `src/tools/capability.rs` for provider requests. They are not copied into prompt text. Disabled tools are enforced in provider schemas and dispatch; the shared `<Tools>` guidance is not filtered.
## Skills
Skill discovery loads `SKILL.md` frontmatter and bodies locally, but the normal system prompt includes only enabled skill names and non-empty `description` frontmatter:
```text
- review: Review code changes for correctness.
- zed-themes
```
Full skill content enters context only when model calls `read` on `skill://<name>` or `skill://<name>/<relative-reference>`. This keeps the base prompt compact while advertising available capabilities.
Skill root priority, highest last-write wins:
1. legacy `~/.agents/skills`
2. `$MC_HOME/skills`
3. absolute configured `knowledge.skills.additional_paths` in listed order
4. repo `.agents/skills`
Discovery accepts direct `<skill>/SKILL.md` and one-level grouped `<group>/<skill>/SKILL.md` layouts.
## Subagents
Subagent identity metadata is optional. When available, generated identity text is inserted between tools and skills. Child agent sessions receive the same prompt guidance; disabled tools narrow their provider schemas and dispatch without changing that guidance.
A selected subagent profile appends its trimmed body in the same exact `<Your-Role Persona=...>` wrapper as a primary profile. The filename stem remains the selection/list id, while frontmatter `name` supplies `Persona`; nested selected profiles append after inherited ancestor wrappers.
`subagents` tool calls return bounded aggregate parent-visible output; raw child transcript details stay in child sessions.
## Provider request mapping
The constructed system prompt is stored as a system message in `ProviderRequest` conversation items, then mapped per OpenAI-compatible API mode:
| Provider API mode | Mapping |
| --- | --- |
| Chat completions | System prompt stays in `messages[]` as role `system`; non-system messages, tool calls, and tool results also remain in `messages[]`. Tool schemas use Chat format: `{ "type": "function", "function": { ... } }`. |
| Responses API, custom providers | All system messages are joined with blank lines into top-level `instructions`; non-system conversation items go into top-level `input`. Tool schemas use Responses format: `{ "type": "function", "name": ..., "parameters": ... }`. |
| OpenAI Codex Responses | Same `instructions` + `input` split, with Codex defaults such as `text.verbosity=low`, reasoning insertion when supported, and `parallel_tool_calls=true` when tools are enabled. |
Prompt cache material includes provider id, model, system prompt, and conversation items, so changing templates, `AGENTS.md`, tools/skills text, or provider model changes cache identity.
## Customization points
- Edit `$MC_HOME/AGENTS.md` for user-wide behavior.
- Edit repo `AGENTS.md` for project behavior.
- Override prompt fragments in `$MC_HOME/prompts/<fragment>.md`.
- Add skills under `$MC_HOME/skills`, configured absolute paths, or repo `.agents/skills`.
- Add subagent identity profiles under `$MC_HOME/subagents`.
- Add primary-agent profiles under `$MC_HOME/primary-agents`; selected primary-agent body is appended only to main assistant system prompt, not child subagent prompts.
## Invariants
- Prompt section order is stable: system → tools → subagents → skills → additional context files.
- Dynamic context uses `<Additional-Context-Files>` and `<Context File=PATH>` delimiters.
- `<Tools>` contains guidance only; tool descriptions and schemas are supplied separately as provider-native definitions.
- Skills list only names/descriptions until `read skill://...` loads full content.
- Runtime facts are bounded, local, and non-secret by design.
- `CLAUDE.md` and arbitrary parent `AGENTS.md` files are not discovered.
- Provider mapping preserves system instructions outside normal user input for Responses APIs.
## Source map
| Behavior | Source |
| --- | --- |
| Prompt section order, template loading, variable rendering, dynamic context delimiters | `src/agent/prompt.rs` |
| Agent session prompt creation and primary-agent append hook | `src/agent/mod.rs` |
| `AGENTS.md` discovery order | `src/instructions/mod.rs` |
| Bundled prompt text | `prompts/system.md`, `prompts/tools.md`, `prompts/skills.md`, `prompts/subagents.md`, `prompts/compact.md` |
| Tool names, descriptions, provider schemas | `src/tools/capability.rs` |
| Skill discovery, frontmatter parsing, priority | `src/skills/mod.rs` |
| Chat/Responses/Codex request bodies | `src/providers/openai.rs` |
| Related feature overview | `docs/features/instructions-prompts-skills-and-agents.md` |
## Related docs
- [Instructions, prompts, skills, and primary agents](instructions-prompts-skills-and-agents.md)
- [Tools and safety model](tools-and-safety.md)
- [Parallel subagents](parallel-subagents.md)
- [Sessions, context, and cache](sessions-context-cache.md)
---
[Back to feature docs](README.md) · [Back to repository README](../../README.md)