From a20c4efb7c9cc963b98e0982f141eab53d947cee Mon Sep 17 00:00:00 2001 From: roboomp Date: Tue, 2 Jun 2026 00:41:40 +0000 Subject: [PATCH] docs: documented SYSTEM.md / APPEND_SYSTEM.md customization contract - Added docs/system-prompt-customization.md covering precedence, replace-vs-append semantics, the verbatim insertion rule for template syntax, deduplication, and discovery behavior across the primary findConfigFile path and the capability layer. - Linked the new doc from docs/config-usage.md so the existing SYSTEM.md reference points at the full contract. Fixes #1671 --- docs/config-usage.md | 2 +- docs/system-prompt-customization.md | 153 ++++++++++++++++++++++++++++ 2 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 docs/system-prompt-customization.md diff --git a/docs/config-usage.md b/docs/config-usage.md index 130ea2223..f2d778214 100644 --- a/docs/config-usage.md +++ b/docs/config-usage.md @@ -217,7 +217,7 @@ Native provider (`id: native`) reads native config from: - Slash commands, rules, prompts, instructions, hooks, tools, extensions, extension modules, and settings use a project/user root only when the root directory exists and is non-empty. - Skills scan `/.omp/skills` for each ancestor from the current working directory up to the repo root/home boundary, plus `~/.omp/agent/skills`, without requiring the root `.omp` directory itself to be non-empty. -- `SYSTEM.md` and `AGENTS.md` read user-level files directly and use nearest-ancestor project `.omp` lookup for project files, but the project `.omp` directory must be non-empty. +- `SYSTEM.md` and `AGENTS.md` read user-level files directly and use nearest-ancestor project `.omp` lookup for project files, but the project `.omp` directory must be non-empty. See [`docs/system-prompt-customization.md`](./system-prompt-customization.md) for the full `SYSTEM.md` / `APPEND_SYSTEM.md` contract (replace vs. append, templating). ### Scope-specific loading diff --git a/docs/system-prompt-customization.md b/docs/system-prompt-customization.md new file mode 100644 index 000000000..bbbcc4b25 --- /dev/null +++ b/docs/system-prompt-customization.md @@ -0,0 +1,153 @@ +# System Prompt Customization + +How the coding-agent assembles the system prompt sent to the model, and what users can control via `SYSTEM.md`, `APPEND_SYSTEM.md`, and the matching CLI flags. + +Primary implementation: + +- `packages/coding-agent/src/system-prompt.ts` (`buildSystemPrompt`, `loadSystemPromptFiles`) +- `packages/coding-agent/src/main.ts` (`discoverSystemPromptFile`, `discoverAppendSystemPromptFile`) +- `packages/coding-agent/src/prompts/system/system-prompt.md` (default template) +- `packages/coding-agent/src/prompts/system/custom-system-prompt.md` (override template) +- `packages/coding-agent/src/prompts/system/project-prompt.md` (project/environment footer) + +--- + +## 1) Inputs + +Four user-controllable inputs feed prompt assembly. All four resolve a value as either a literal string or, if the argument looks like a file path, the contents of that file (`resolvePromptInput`). + +| Input | Source | Effect | +|---|---|---| +| `--system-prompt ` | CLI flag | Replaces the default prompt. Highest precedence. | +| `SYSTEM.md` | `/.omp/SYSTEM.md` (walk-up), then `~/.omp/agent/SYSTEM.md` (and equivalent paths under `.claude`, `.codex`, `.gemini`) | Same effect as `--system-prompt`; used when the flag is absent. | +| `--append-system-prompt ` | CLI flag | Appended after the (default or custom) prompt. | +| `APPEND_SYSTEM.md` | Same discovery as `SYSTEM.md` | Same effect as `--append-system-prompt`; used when the flag is absent. | + +Discovery for `SYSTEM.md` / `APPEND_SYSTEM.md` uses `findConfigFile` (`packages/coding-agent/src/config.ts`): the first existing file across the ordered bases (`.omp`, `.claude`, `.codex`, `.gemini` — project-level first, then user-level) wins. See [`docs/config-usage.md`](./config-usage.md) for the full discovery contract. + +Precedence (highest first): + +1. `--system-prompt` +2. project `SYSTEM.md` +3. user `SYSTEM.md` + +For append, the same precedence applies between `--append-system-prompt`, project `APPEND_SYSTEM.md`, and user `APPEND_SYSTEM.md`. + +--- + +## 2) Replace vs. append + +Two templates exist: + +- `system-prompt.md` (default) — full staff-engineer preamble, env/workstation info, tool inventory, skills/rules, exploration rules, etc. +- `custom-system-prompt.md` (override) — minimal wrapper: user content, then optional context blocks (AGENTS.md files, skills list, always-apply rules, domain rules). + +`buildSystemPrompt` picks the template based on whether a custom prompt was supplied: + +```ts +const rendered = prompt.render( + resolvedCustomPrompt ? customSystemPromptTemplate : systemPromptTemplate, + data, +); +``` + +Consequences: + +- Providing `--system-prompt` or `SYSTEM.md` **replaces** the default prompt entirely. The default "staff engineer" preamble, the `ENV` / `Tools` / `Exploration` / `Tool Priority` / `Workflow` sections, the workstation info, the workspace tree, the today's-date/cwd footer (`project-prompt.md`), and the dir-context list are NOT injected. The override template only adds: context files (AGENTS.md), skills list, always-apply rules, and domain rules. +- Providing `--append-system-prompt` or `APPEND_SYSTEM.md` **appends** to whichever template was selected. The default prompt and its project footer remain intact. + +If you want to keep the default prompt and add to it, use `--append-system-prompt` / `APPEND_SYSTEM.md`. If you want to start from scratch, use `--system-prompt` / `SYSTEM.md`. + +--- + +## 3) Templating contract + +**Contents of `SYSTEM.md`, `APPEND_SYSTEM.md`, `--system-prompt`, and `--append-system-prompt` are treated as plain text.** They are interpolated verbatim into the parent template. + +The parent template is Handlebars (`packages/utils/src/prompt.ts`), but a `{{value}}` reference in Handlebars does not recursively render its substituted contents — the value is emitted as a string. Concretely: + +```handlebars +{{! parent template — handled by Handlebars }} +{{#if systemPromptCustomization}} +{{systemPromptCustomization}} +{{/if}} +``` + +If `SYSTEM.md` contains: + +```handlebars +Working in {{cwd}} on {{date}}. +{{#if hasMemoryRoot}}Memory enabled.{{/if}} +``` + +the rendered output contains those characters verbatim — `{{cwd}}`, `{{#if hasMemoryRoot}}`, etc. are NOT substituted. They will be shown to the model as literal Handlebars syntax. + +This is by design. The internal template variables (`cwd`, `date`, `environment`, `workspaceTree`, `skills`, `rules`, `toolRefs`, `hasMemoryRoot`, `hasObsidian`, `mcpDiscoveryServerSummaries`, ...) are not a supported public surface — they change between releases as the prompt is rewritten, and they would couple user configs to internals. Treat them as private. + +If a future release exposes a templating surface for `SYSTEM.md`, it will be opt-in (e.g. via a settings flag or a different filename) and documented here. + +--- + +## 4) Recommended patterns + +### "Tweak the default" — keep default, add a few rules + +Use `APPEND_SYSTEM.md` (or `--append-system-prompt`). The default prompt — including environment info, workspace tree, and the dated project footer — stays intact; your text is appended at the very end. + +```text +# ~/.omp/agent/APPEND_SYSTEM.md +Prefer Bun APIs over Node APIs in this project. +When you change a public function, run `bun check` before yielding. +``` + +### "Replace the default entirely" — bring your own prompt + +Use `SYSTEM.md` (or `--system-prompt`). You own everything except the auto-appended context blocks (AGENTS.md, skills list, always-apply rules, domain rules). You will NOT get the default tool guidance, exploration rules, or environment-aware footer — copy what you need from `packages/coding-agent/src/prompts/system/system-prompt.md` and adjust. + +```text +# ~/.omp/agent/SYSTEM.md +You are a code reviewer. Read diffs, surface issues, never edit files. +- Cite paths with backticks. +- Prefer concrete fixes over abstract advice. +``` + +If you do this and want environment info (cwd, date, GPU, etc.) anyway, paste a snapshot or read it from the tooling at conversation time — there is currently no way to reuse the default template's rendering pieces from `SYSTEM.md`. + +### "Replace, but keep one section of the default" — not directly supported + +There is no built-in way to inherit specific blocks of the default prompt while overriding the rest. The two supported modes are full-replace (`SYSTEM.md`) and append (`APPEND_SYSTEM.md`). If you need this, file a feature request describing the section you want to inherit. + +--- + +## 5) Deduplication + +To avoid double-injecting the same content, `buildSystemPrompt` deduplicates: + +- If both `SYSTEM.md` (via `loadSystemPromptFiles`) and `--system-prompt` / discovered `SYSTEM.md` (via `discoverSystemPromptFile`) resolve to the same path, the `systemPromptCustomization` block is dropped because its blocks already appear in `customPrompt` (`dedupePromptSource`). +- Always-apply rules whose body appears verbatim in any of `{customPrompt, appendPrompt, systemPromptCustomization}` are omitted from the rules block (`dedupeAlwaysApplyRules`). + +These passes work on **whitespace-normalized blocks separated by blank lines**, so trivial reformatting will not defeat them, but semantic restatements will not be matched. + +--- + +## 6) Discovery and the empty-directory rule + +Two code paths exist for reading `SYSTEM.md` / `APPEND_SYSTEM.md`: + +- The primary path (`discoverSystemPromptFile` in `main.ts`, which feeds `customPrompt` / `appendPrompt`) calls `findConfigFile` and only checks file existence. It works even if `.omp/` contains only the `SYSTEM.md` file itself. +- The secondary capability path (`loadSystemPromptFiles` → builtin discovery) requires the project `.omp/` directory to be non-empty (the same admission rule applied to every other config file under `.omp/`). When this path skips the file, the primary path's copy already populated `customPrompt`, so deduplication leaves user-facing behavior unchanged. + +Net effect: `SYSTEM.md` and `APPEND_SYSTEM.md` are picked up even from an otherwise empty `.omp/`. The non-empty rule documented in [`docs/config-usage.md`](./config-usage.md) applies to the capability layer specifically. + +--- + +## 7) Quick reference + +| Goal | Use | +|---|---| +| Add an instruction on top of the default prompt | `APPEND_SYSTEM.md` or `--append-system-prompt` | +| Replace the prompt entirely | `SYSTEM.md` or `--system-prompt` | +| Use `{{cwd}}` / `{{date}}` / other internals in my file | Not supported. Files are inserted verbatim. | +| Inherit specific parts of the default prompt | Not supported; use append, or copy what you need into `SYSTEM.md`. | +| Override at a per-repo level | Project `.omp/SYSTEM.md` or `.omp/APPEND_SYSTEM.md` | +| Override globally | `~/.omp/agent/SYSTEM.md` or `~/.omp/agent/APPEND_SYSTEM.md` |