feat(prompt): add system prompt builder with context injection#7
Merged
feat(prompt): add system prompt builder with context injection#7
Conversation
Move system prompt ownership out of the transport layer into a dedicated prompt module. The prompt assembles static guidance sections (identity, task, tool, style), runtime environment detection (platform, git, date), and discovered CLAUDE.md files (global + project).
- Fix global CLAUDE.md skipped when cwd is None (candidate_paths now takes Option<&Path> and always includes the global path). - Handle git command failures independently — default to empty branch and assume dirty rather than discarding all git info. - Run environment detection and CLAUDE.md loading concurrently. - Tighten test assertions (identity prefix boundary, candidate_paths length).
At each project location, check CLAUDE.md first, then AGENTS.md as a fallback. The first file found at each location wins. The global ~/.claude/ directory stays CLAUDE.md only.
Discover instruction files at every directory level from the project root down to the working directory, not just at the root. Subdirectory-specific instructions appear later in the prompt (higher priority), matching Claude Code's hierarchical discovery behavior.
- Return GitInfo directly from detect_git_info instead of wrapping in Option — the function never returns None due to fallback logic, so the Option was misleading. The caller already gates on git_root.is_some(). - Replace date subprocess (date +%Y-%m-%d) with the time crate for portability and no subprocess overhead. Uses local time with UTC fallback when local offset detection fails. - Improve render tests to verify exact line ordering and structure instead of loose contains() checks. - Add tests for Environment::detect with various cwd/git_root combinations and strengthen current_date year validation.
Build the system prompt at the start of each user interaction instead of once at startup. This keeps dynamic data (git branch, dirty status, working directory, date) fresh during long sessions. The rebuild happens per user message, not per tool round — one set of git subprocess calls per interaction is acceptable overhead.
Replace Vec<(Vec<PathBuf>, &'static str)> with a named Slot struct for readability — slot.candidates and slot.label are self-documenting compared to tuple field access. Add comment in walk_root_to_cwd explaining the strip_prefix behavior when cwd equals root (empty relative path, loop body never executes). Add tests for load_files: empty results, first-candidate preference, AGENTS.md fallback, whitespace-only skip, multi-slot collection. Extend candidate_slots test to verify .claude/ companion slots. Add render_single_file test.
Add find_git_root tests: success inside a git repo (verifies .git exists), returns None in a temp dir outside any repo. Add build_system_prompt tests: user instructions branch (verifies project CLAUDE.md injection), section join boundary (double newline separator between sections).
…opencode findings Add root-to-CWD walk pattern to CLAUDE.md Loading Hierarchy section with a concrete example showing intermediate directory discovery. Fix opencode section: TypeScript / Bun (not Go), correct the patterns based on actual source investigation — provider-specific templates, AGENTS.md / CLAUDE.md / CONTEXT.md hierarchy with walk-up semantics, per-turn rebuild, 4-level config (not 8), two-phase compaction. Add opencode source paths to the Sources section.
…ance Add "Executing actions with care" section — guidance on reversibility, blast radius, and confirmation for destructive/shared-state operations. Inspired by Claude Code's approach but kept concise for token efficiency. Expand task guidance: stronger "read before modify" language, specific security vulnerability classes (command injection, path traversal, OWASP top 10), scope discipline, and "diagnose before retry" detail. Expand style section: output focus guidance and emoji restriction.
d717327 to
2078d13
Compare
…uctions Create docs/guide/ with three focused pages covering what actually ships today: - quickstart.md — install, first session, tool overview, CLAUDE.md teaser - configuration.md — API key and OAuth auth, env vars, model selection - instructions.md — CLAUDE.md / AGENTS.md discovery hierarchy with walk example, writing tips Slim down the main README: replace inline config details with a documentation table linking to the guide pages. Update docs/README.md with a user guide section above the internal docs index.
2078d13 to
1ea04c0
Compare
The Anthropic API requires the identity prefix to be a separate text block in the system array for OAuth validation. Concatenating it into the prompt body as a single string causes 429 for non-Haiku models. Move the prefix constant and block construction into the client, and change stream_message to accept Option<&str> for the prompt body.
… third-party restrictions
hakula139
added a commit
that referenced
this pull request
May 5, 2026
PR #64 (modal infrastructure) shipped Option C: bare /model opens the combined picker, bare /effort errors with a usage hint pointing at /model. The user guide, design notes, and roadmap still described the older "both bare forms open the picker with different initial focus" shape. Updated: - docs/guide/slash-commands.md — table description, mid-turn classification paragraph, and the "Switching the Effort" / "Switching the Model" sections. - docs/design/slash/commands.md — design decision #5, /effort and /model per-command notes, source list (`agent_loop_task` → `agent_turn`). - docs/design/slash/modals.md — design decisions #4 (`SessionInfo` → `LiveSessionInfo`) and #7 (typed-arg-only contract). - docs/roadmap.md — moved the combined picker out of "Current Focus" (shipped in PR #64) into Working Today; replaced with the deferred /effort slider. - CLAUDE.md — `slash/effort.rs` description updated to match the typed-arg contract.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Add a section-based system prompt builder with runtime environment detection, hierarchical instruction file discovery (CLAUDE.md / AGENTS.md), and static guidance sections. The prompt is rebuilt each turn so git status and branch info stay fresh.
The API client now sends the system prompt as an array of text blocks with the identity prefix in its own block, which is required for OAuth validation on non-Haiku models.
.claude/subdirectory support.TextBlockwith identity prefix in its own block (OAuth requirement).Changes
crates/oxide-code/src/prompt.rsbuild_system_promptentry,assemble(testable core),find_git_rootcrates/oxide-code/src/prompt/environment.rsEnvironmentstruct withdetect/render, parallel git info viatokio::join!,current_dateviatimecrate with local-offset fallbackcrates/oxide-code/src/prompt/instructions.rsSlot-based instruction discovery:load→candidate_slots→walk_root_to_cwd→load_files→rendercrates/oxide-code/src/client/anthropic.rsSystemBlocktype,systemfield changed from&strtoVec<SystemBlock>, prefix sent as separate block,SYSTEM_PROMPT_PREFIXmoved herecrates/oxide-code/src/main.rsbuild_system_promptcall, passSome(&system_prompt)to clientcrates/oxide-code/src/config/oauth.rs_ =style consistencyCargo.tomltimewithlocal-offsetfeatureCLAUDE.mdprompt.rs,prompt/environment.rs,prompt/instructions.rsto crate structuredocs/research/system-prompt.mddocs/research/anthropic-api.mddocs/guide/quickstart.mddocs/guide/configuration.mddocs/guide/instructions.mddocs/guide/README.mddocs/README.mddocs/roadmap.mdREADME.mdTest plan
cargo buildcompiles cleanlycargo clippy --all-targets -- -D warnings— zero warningscargo test— 195 tests passcargo llvm-cov --ignore-filename-regex 'main\.rs'— 89% line coverage (prompt modules 98–99%)oxresponds correctly with full system prompt (CLAUDE.md injected, environment detected)mainbranch works,feat/system-promptworks after block-format fix