System Prompt, Skills & Context Files
The system prompt is an assembly pipeline
“AGENTS.md walks up from cwd; skills follow agentskills.io”
Deep Dive
The system prompt is an assembly pipeline; AGENTS.md walks up from cwd
Learning Objectives
After this chapter you will be able to:
- Explain the five-stage pipeline of buildSystemPrompt and its priorities
- Describe the AGENTS.md walk-up mechanism and its design rationale
- Explain how Skills follow the agentskills.io standard and inject into the prompt
- Understand the security implications of .pi/SYSTEM.md wholesale identity replacement
Key Source Files
| File | Lines | Responsibility |
|---|---|---|
packages/coding-agent/src/core/system-prompt.ts | 162 | buildSystemPrompt: five-stage pipeline |
packages/coding-agent/src/core/skills.ts | 487 | Skills loading, formatting, trigger parsing |
packages/coding-agent/src/core/prompt-templates.ts | ~200 | Reusable prompt fragments |
packages/coding-agent/src/core/resource-loader.ts | 1043 | AGENTS.md / context file loading |
Design Motivation: Why Can't the System Prompt Be Hardcoded?
Problem: Different projects need different Agent behaviors — React projects follow component conventions, Rust projects watch lifetimes, enterprise projects have internal API contracts. A hardcoded system prompt means forking pi for every project.
Analogy: Like Nginx config inheritance — global config → virtual host → location block, each layer overriding the previous. pi's system prompt is also multi-source assembly with layered overrides.
Design goals:
- Default behavior works out of the box (zero config)
- Project-level conventions inject naturally (AGENTS.md)
- Power users can fully replace (.pi/SYSTEM.md)
- Skills dynamically extend capabilities (agentskills.io)
The Five-Stage Pipeline
buildSystemPrompt(cwd, tools, skills):
① Identity layer (wholesale replaceable)
Default: pi's built-in DEFAULT_SYSTEM_PROMPT
Replace: .pi/SYSTEM.md or ~/.pi/agent/SYSTEM.md
├── exists → completely replace default identity
└── absent → use default
② Tool instructions
formatTools(tools) → name, params, usage for each tool
Tells the model "what hands you have"
③ Project context
<project_context>
AGENTS.md / CLAUDE.md content
</project_context>
Project conventions injected — model "reads" project norms
④ Skills formatting
formatSkillsForPrompt(skills) → trigger conditions and descriptions
Tells the model "what skills you know"
⑤ Append layer
APPEND_SYSTEM.md content appended at the end
Doesn't change identity, only adds constraints — lightest customization
Priority: ① is "who I am," ⑤ is "also remember." ① is replaceable, ⑤ is append-only — two granularities of customization.
AGENTS.md Walk-Up
// resource-loader.ts (simplified)
function loadProjectContextFiles(cwd: string): string[] {
const results: string[] = []
let dir = cwd
while (dir !== "/" && dir !== "") {
for (const name of ["AGENTS.md", "CLAUDE.md"]) {
const path = join(dir, name)
if (existsSync(path)) results.push(readFileSync(path, "utf-8"))
}
dir = dirname(dir)
}
return results // ordered near to far
}
Example:
cwd = /repo/packages/ai/src
→ /repo/packages/ai/src/AGENTS.md? no
→ /repo/packages/ai/AGENTS.md? no
→ /repo/packages/AGENTS.md? yes → read
→ /repo/AGENTS.md? yes → read
→ / (stop)
Why walk up?
- In monorepos, root AGENTS.md defines global conventions (code style, CI norms)
- Sub-packages can have their own AGENTS.md adding local conventions
- Developers launching pi at any depth inherit project conventions
Analogy: Like .gitignore hierarchical inheritance — root rules apply to all subdirectories; subdirectories can add more.
Skills: The agentskills.io Standard
// skills.ts (conceptual)
interface Skill {
id: string // "add-llm-provider"
name: string // "Add LLM Provider"
description: string // trigger condition description
content: string // SKILL.md body
triggers: string[] // trigger keywords
}
function formatSkillsForPrompt(skills: Skill[]): string {
return skills.map(s =>
`[skill:${s.id}] ${s.name}: ${s.description}`
).join("\n")
}
Workflow:
- Skills loaded from
~/.pi/skills/and.pi/skills/ - Formatted into system prompt — model "sees" its available skills
- When model reply contains
pi.skill_idmarker, parseSkillBlock parses and executes - Follows agentskills.io open standard — portable across Agents
.pi/SYSTEM.md: Security Implications of Wholesale Replacement
If .pi/SYSTEM.md exists:
Default identity is completely replaced
├── Benefit: enterprises can fully customize Agent persona
└── Risk: malicious projects can inject "ignore all safety constraints"
Therefore:
.pi/SYSTEM.md only takes effect in trusted projects (→ Chapter 10 trust gate)
Untrusted project → ignore .pi/SYSTEM.md → use default safe identity
This is yet another reason for the trust gate (→ Chapter 10): Not only extensions can be malicious — system prompt replacement can too.
Engineering Insights
-
system-prompt.ts is only 162 lines: The pipeline logic itself is simple (concatenate 5 sections). Complexity lives in resource-loader.ts (1043 lines) — handling file formats, encoding, size limits, circular references.
-
skills.ts 487 lines: Mostly SKILL.md frontmatter parsing (YAML), trigger matching, permission validation. The agentskills.io standard defines a strict schema.
-
Prompt length budget: System prompt can't grow unbounded — it occupies context window. resource-loader has maxBytes limits; exceeding truncates. AGENTS.md too long = squeezing conversation space.
Cross-Chapter Links
| Direction | Chapter | Relationship |
|---|---|---|
| Prerequisite | Ch01 Agent Loop | systemPrompt is AgentState's first field |
| Prerequisite | Ch10 CLI Modes | Trust gate determines if .pi/SYSTEM.md takes effect |
| Follow-up | Ch13 Extensions | Extensions can append system prompt fragments |
| Follow-up | Ch03 Compaction | System prompt is never compressed |
| Follow-up | Ch18 Settings & Trust | Complete trust mechanism design |
Check Your Understanding
- If AGENTS.md only searched cwd (not walking up), what problems would arise in monorepo scenarios?
- Why is .pi/SYSTEM.md "wholesale replace" while APPEND_SYSTEM.md is "append"? What scenarios suit each granularity?
- If the system prompt had no length limit, what problems would result?
Takeaways
- System prompt is a five-stage assembly pipeline: identity → tools → project context → Skills → append
- AGENTS.md walks up from cwd — monorepo conventions inherit naturally
- Skills follow agentskills.io open standard, portable across Agents
- .pi/SYSTEM.md wholesale replacement requires trust gate protection
This chapter is based on the main-branch source of earendil-works/pi.