-
Notifications
You must be signed in to change notification settings - Fork 0
System Prompt File
The bundled Context Provider that reads a user-editable Markdown file and contributes its content as a system-message fragment to every turn. This is the simplest mechanism for a user to shape the session's system prompt without writing an extension.
This page is a reference extension description — it documents how the bundled SystemPromptFile CP behaves. Normative rules live at Context Providers. It is bundled because the pattern is universal enough that shipping it avoids every user re-authoring it.
The SystemPromptFile CP lets a user write guidance to the model in a Markdown file they own. A user who wants the session to "always behave like a senior reviewer" writes that instruction into system.md; the file's content is spliced into the system-message layer on every turn.
It is the simplest Context Provider in the bundled set. It does one thing: read a file, contribute its contents. Any richer shaping (conditional content, templating, summarization) is a new extension.
| Field | Value |
|---|---|
| Kind |
ContextProvider. |
contribution.kind |
system-message. |
loadedCardinality |
1. |
activeCardinality |
1. |
reloadBehavior |
between-turns (inherits from the contract). |
The provider reads one file from the user's own scope. It does not call host.env.get() (the API is not present on Context Providers — see Env Provider § Per-category Host-API scoping) and does not call the network.
system.md resolves per scope:
| Scope | Location |
|---|---|
| Global |
~/.stud/system.md (per Platform Integration § Directory resolution for the per-OS path). |
| Project |
<cwd>/.stud/system.md. |
Merge rule: if both files exist and are readable, the contribution is the concatenation — global first, then project — separated by a blank line. This mirrors the general scope-layering policy from Configuration Scopes: global sets the baseline; project layers specifics on top.
If neither file exists, the provider contributes nothing — its turn-by-turn contribution is empty and it consumes no tokens. The session does not error.
A project that is not trusted (per Project Trust) has its project-layer system.md skipped. Trusting a project implicitly trusts any system-prompt text it ships.
Throughout this page,
~/.stud/is the wiki shorthand for the platform-resolved global config directory. The actual path is per-OS — see Platform Integration § Directory resolution.
stud-cli ships a bundled default system.md that lands at the global path the first time stud-cli sets up the global config directory. This is how a user gets a useful system prompt without authoring one — and it is the only path by which stud-cli writes content into system.md.
The scaffold runs only as part of stud-cli creating the global config directory itself for the first time — the same first-run path that bootstraps the provider config. See First Run.
The trigger is directory-first-creation, not file existence. On every subsequent launch the global directory already exists, so the scaffold does not run — even if the user has since deleted system.md. A deleted file is preserved as the user's choice.
stateDiagram-v2
[*] --> CheckDir: stud-cli starts
CheckDir --> Skip: global dir exists
CheckDir --> Create: global dir missing
Create --> Write: directory created
Write --> Skip: scaffold complete
Skip --> [*]: continue startup
| Condition | Behavior |
|---|---|
| File already exists | No-op. The scaffold never overwrites. |
| Parent directory exists but is not writable | Log and skip; the user is not prompted. The session continues without a system prompt. |
| Write fails partway through | Atomic create-or-skip — a partial file is not left on disk. |
A successful scaffold emits a SystemPromptScaffolded audit event with the resolved file path and the byte count of the written default. The content of the default is not duplicated into the audit record. This mirrors how First Run audits provider setup and trust decisions — see Audit Trail.
A user who scaffolded under stud-cli vN keeps their system.md when upgrading to vN+1. The bundled default text in vN+1 only reaches new installs that go through first-run setup against a fresh global directory.
This is intentional — the file is the user's after the first scaffold. A user who wants the new bundled default deletes their global directory and re-runs first-run setup, or copies the new default text manually.
The bundled default ships these content categories, in order:
- Identity. A one-line statement that the model is running inside stud-cli.
-
Capabilities summary. Generic. Not a tool roster — tool definitions are sent separately as the
toolsargument and include any MCP-discovered tools. - Output-shape guidance. Concise, structured when appropriate.
- Reminder that the file is user-editable. A line telling the human reader they can rewrite this freely; stud-cli does not overwrite it on upgrade.
Excluded entirely (per LLM Context Isolation): env vars, settings.json content, credentials, secrets, working-directory absolute paths, hostname, username. Excluded for simplicity in v1: current security mode, current project trust state, current date. A future CP can add any of these as a separate system-message fragment without touching this default.
flowchart LR
Global[~/.stud/system.md] --> Read[read + concat]
Project[cwd/.stud/system.md] --> Read
Read --> Budget[apply maxTokens]
Budget --> Contribute[contribute as<br/>system-message]
Contribute --> Assembly[Context Assembly]
Assembly --> System[system-prompt layer<br/>of the request]
- The provider reads each file within its declared scope.
- It concatenates global + project, separated by a blank line.
- It truncates to
maxTokens(tail-clipped with a marker) if the combined text exceeds the budget. - It contributes the result as a
system-message. - Core's Context Assembly places it in the system-prompt layer of the assembled request.
Other Context Providers that also contribute system-message fragments interleave per contract priority.
| Field | Meaning | Default |
|---|---|---|
enabled |
Whether the provider participates. |
true. |
maxTokens |
Budget ceiling for the combined contribution. |
4096. |
priority |
Assembly tiebreaker. |
50 (mid-range). |
paths.global |
Override the global-scope file path. | Platform default. |
paths.project |
Override the project-scope file path. |
<cwd>/.stud/system.md. |
A user who wants to disable the global layer points paths.global at a non-existent path, or sets enabled: false in the global-scope settings.json.
-
Not env-aware. The file's content is passed through verbatim — if a user writes
$MY_SECRETintosystem.md, that literal string reaches the LLM. There is no variable expansion. This is deliberate: a CP that secretly expanded env would violate LLM Context Isolation, and the underlying Host API does not give Context Providers env access. - Not a template engine. No substitutions, no conditionals. A user who wants "if the session is about security, prepend X" writes a different CP.
-
Not a multi-file aggregator. Exactly
system.mdin each scope, nothing else. A folder of fragments is a different extension. - Not a resource binding. The content enters the system-message directly, not via the Resource Registry.
The provider re-reads the file at turn boundaries, not per-assembly within a turn. A user who edits system.md mid-turn does not see the edit reflected until the next turn. This is consistent with the contract's reloadBehavior: between-turns.
For a project-scope edit: the edit takes effect on the next turn after the edit, provided the project remains trusted.
The provider treats system.md as plaintext content the user owns. It applies no redaction. A user who writes a secret into system.md sends that secret to the LLM on every turn — that is the user's choice, made through their own editor on their own file, not a stud-cli decision.
See Platform Integration § File-permission expectations for the permissions stud-cli enforces on other files in the .stud/ tree.
Another Context Provider that contributes system-message and declares a higher priority takes precedence when the budget is tight. A project that wants to replace the SystemPromptFile CP entirely disables it through settings.json (contextProviders.SystemPromptFile.disable: true) and loads its own.
This is the general extension replacement pattern — see Extension Discovery and Configuration Scopes.
-
The file is a system-prompt surface. Anything that reaches
system.mdreaches the model. A user sharing a project's<cwd>/.stud/system.mdis sharing their model-facing instructions. -
Trust gates the project file. An untrusted project's
system.mdis ignored. A malicious project can't inject system-prompt content without first passing Project Trust. -
No env surfacing. Context Providers do not have
host.env.get()access. See LLM Context Isolation and Context Providers § Hard ban. -
Audit is light. Every turn audits
ContextContributionMadewith the provider id and byte count. The file's content is not duplicated into the audit record — the audit signals "this provider contributed"; the content is in the turn's message history.
- Execution Model
- Message Loop
- Concurrency and Cancellation
- Error Model
- Event and Command Ordering
- Event Bus
- Command Model
- Interaction Protocol
- Hook Taxonomy
- Host API
- Extension Lifecycle
- Env Provider
- Prompt Registry
- Resource Registry
- Session Lifecycle
- Session Manifest
- Persistence and Recovery
- Stage Executions
- Subagent Sessions
- Contract Pattern
- Versioning and Compatibility
- Deprecation Policy
- Capability Negotiation
- Dependency Resolution
- Validation Pipeline
- Cardinality and Activation
- Extension State
- Conformance and Testing
- Providers
- Provider Params
- Tools
- Hooks
- UI
- Loggers
- State Machines
- SM Stage Lifecycle
- Stage Definitions
- Commands
- Session Store
- Context Providers
- Settings Shape
- Trust Model
- Project Trust
- Extension Isolation
- Extension Integrity
- LLM Context Isolation
- Secrets Hygiene
- Security Modes
- Tool Approvals
- MCP Trust
- Sandboxing
- Configuration Scopes
- Project Root
- Extension Discovery
- Extension Installation
- Extension Reloading
- Headless and Interactor
- Determinism and Ordering
- Launch Arguments
- Network Policy
- Platform Integration
Tools
UI
Session Stores
Loggers
Providers
Hooks
Context Providers
Commands
- First Run
- Default Chat
- Tool Call Cycle
- Hook Interception
- Guard Deny Reproposal
- State Machine Workflow
- SM Stage Retry
- Hot Model Switch
- Capability Mismatch Switch
- Session Resume
- Session Resume Drift
- Approval and Auth
- Interaction Timeout
- Headless Run
- Parallel Tool Approvals
- Subagent Delegation
- Scope Layering
- Project First-Run Trust
- Reload Mid-Turn
- Compaction Warning
- MCP Remote Tool Call
- MCP Prompt Consume
- MCP Resource Bind
- MCP Reconnect