Skip to content

System Prompt File

Z-M-Huang edited this page Apr 29, 2026 · 4 revisions

SystemPromptFile context provider (bundled)

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.


Role

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.


Shape

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.


Where the file lives

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.


First-run scaffold

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.

Trigger

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
Loading

Failure semantics

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.

Audit

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.

No automatic migration

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.

What the bundled default contains

The bundled default ships these content categories, in order:

  1. Identity. A one-line statement that the model is running inside stud-cli.
  2. Capabilities summary. Generic. Not a tool roster — tool definitions are sent separately as the tools argument and include any MCP-discovered tools.
  3. Output-shape guidance. Concise, structured when appropriate.
  4. 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.


What ends up in the request

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]
Loading
  1. The provider reads each file within its declared scope.
  2. It concatenates global + project, separated by a blank line.
  3. It truncates to maxTokens (tail-clipped with a marker) if the combined text exceeds the budget.
  4. It contributes the result as a system-message.
  5. 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.


Config

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.


What this CP does not do

  • Not env-aware. The file's content is passed through verbatim — if a user writes $MY_SECRET into system.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.md in 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.

File-change detection

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.


File-permission expectations

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.


Extension override

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.


Security notes

  • The file is a system-prompt surface. Anything that reaches system.md reaches the model. A user sharing a project's <cwd>/.stud/system.md is sharing their model-facing instructions.
  • Trust gates the project file. An untrusted project's system.md is 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 ContextContributionMade with 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.

Related pages

Introduction

Reading

Core runtime

Contracts

Category contracts

Context

Security

Runtime behavior

Operations

Providers (bundled)

Integrations

Reference extensions

Tools

UI

Session Stores

Loggers

Providers

Hooks

Context Providers

Commands

Case studies

Flows

Maintainers

Clone this wiki locally