feat(agent): add configurable coding_instructions

agent.coding_instructions (a string or list) is appended to the coding brief as
its own stable system block, so users can pin project-wide workflow rules
without editing the shipped brief. Coding-posture only and cache-safe (resolved
once per session; takes effect next session). Empty by default.
This commit is contained in:
Brooklyn Nicholson 2026-06-30 00:59:59 -05:00
parent a10113658b
commit 821d9f709f
4 changed files with 79 additions and 0 deletions

View file

@ -353,6 +353,29 @@ def _coding_mode(config: Optional[dict[str, Any]]) -> str:
return "auto"
def _coding_instructions(config: Optional[dict[str, Any]]) -> str:
"""Standing operator instructions for the coding posture (config).
``agent.coding_instructions`` a string or list of strings appended to the
coding brief as an extra stable system block, so a user can pin project-wide
coding-workflow rules (e.g. "for UI work don't run tsc/lint until I approve;
clean the diff before committing") without editing the shipped brief.
Cache-safe: resolved once per session into the stable system-prompt tier,
like the rest of the posture.
"""
if config is None:
try:
from hermes_cli.config import load_config
config = load_config()
except Exception:
config = {}
raw = ((config or {}).get("agent", {}) or {}).get("coding_instructions", "")
if isinstance(raw, (list, tuple)):
return "\n".join(str(item).strip() for item in raw if str(item).strip())
return str(raw or "").strip()
def _resolve_cwd(cwd: Optional[str | Path]) -> Path:
if cwd:
return Path(cwd).expanduser()
@ -459,6 +482,9 @@ class RuntimeMode:
# only to steer edit-format guidance toward the model's family — see
# ``_edit_format_line``. Fixed for the session, so cache-safe.
model: Optional[str] = None
# Standing operator instructions (``agent.coding_instructions``), appended
# as an extra stable system block. Empty unless the user configures it.
instructions: str = ""
@property
def kind(self) -> str:
@ -505,6 +531,10 @@ class RuntimeMode:
workspace = build_coding_workspace_block(self.cwd)
if workspace:
blocks.append(workspace)
# Operator instructions ride their own block so the brief (block 0) stays
# byte-stable and cache-keyed independently of user config.
if self.instructions:
blocks.append(f"Operator instructions (from config):\n{self.instructions}")
return blocks
def compact_skill_categories(self) -> frozenset[str]:
@ -557,6 +587,7 @@ def resolve_runtime_mode(
cwd=resolved_cwd,
config_mode=mode,
model=model,
instructions=_coding_instructions(config),
)