From 6d74d31cea4805874d983e70e246bb69e80b0688 Mon Sep 17 00:00:00 2001 From: worldmozara Date: Mon, 20 Jul 2026 18:21:27 +0800 Subject: [PATCH] core/config: default system prompt for coding agent tools --- CLAUDE.md | 2 +- README.md | 5 +++- doc/plyngent.example.toml | 7 ++++- src/plyngent/cli/editor.py | 3 +- src/plyngent/config/__init__.py | 1 + src/plyngent/config/models.py | 40 ++++++++++++++++++++++++- tests/test_config/test_agent_section.py | 18 +++++++++-- 7 files changed, 69 insertions(+), 7 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 41d6f8f..e6ec26d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -72,7 +72,7 @@ Async SQLAlchemy + aiosqlite. `MemoryStore`: schema init (+ lightweight SQLite ` - **`/compact`**: soft-compact tool dumps → model summary (no tools) → **new** session seeded with summary message. Soft-compact keeps the last **12** tool results full-size by default (`DEFAULT_RECENT_TOOL_RESULTS`); older tool payloads shrink first. - Events: text_delta, **reasoning_delta**, assistant_message, tool_call/result, max_rounds, **error** (`retryable`/`source`), **cancelled** (`reason`), **usage** (`TokenUsage`). - Usage: API `usage` from completions (stream with `include_usage`); **char≈token fallback** (~4 chars/token) when omitted; **context size** = last request ``prompt_tokens`` (API preferred); `last_turn_usage` / `session_usage` are **billed sums** (tool rounds re-send history); CLI end-of-turn + `/status`. -- Config ``[agent]``: `system_prompt`, `max_tool_result_chars`, `parallel_tools`, `confirm_destructive`, `path_denylist`, `max_context_tokens` (default 200k est. tokens). +- Config ``[agent]``: `system_prompt` (default: built-in coding-agent guide `DEFAULT_SYSTEM_PROMPT`; `""` disables), `max_tool_result_chars`, `parallel_tools`, `confirm_destructive`, `path_denylist`, `max_context_tokens` (default 200k est. tokens). ### Tools (`tools/`) diff --git a/README.md b/README.md index 5466c73..6047e71 100644 --- a/README.md +++ b/README.md @@ -136,7 +136,10 @@ access_key_or_token = "sk-..." "gpt-4o-mini" = { text = true } [agent] -system_prompt = "You are a careful coding assistant." +# system_prompt defaults to a built-in coding-agent guide (omit to use it). +# system_prompt = "" # disable +# Or multi-line override (prefer '''...'''): +# system_prompt = '''Your custom prompt...''' confirm_destructive = true max_context_tokens = 200000 ``` diff --git a/doc/plyngent.example.toml b/doc/plyngent.example.toml index 684eb23..3f36f1a 100644 --- a/doc/plyngent.example.toml +++ b/doc/plyngent.example.toml @@ -11,7 +11,12 @@ # # url = ":memory:" [agent] -system_prompt = "You are a careful coding assistant. Prefer small, verified edits." +# system_prompt defaults to the built-in coding-agent guide when omitted. +# Override with a multi-line literal (prefer ''' so nested " is fine): +# system_prompt = ''' +# Your custom system prompt... +# ''' +# system_prompt = "" # disable system prompt entirely max_tool_result_chars = 32000 parallel_tools = true confirm_destructive = true diff --git a/src/plyngent/cli/editor.py b/src/plyngent/cli/editor.py index 04ccfd0..0f1681c 100644 --- a/src/plyngent/cli/editor.py +++ b/src/plyngent/cli/editor.py @@ -29,7 +29,8 @@ _MINIMAL_CONFIG = """\ # # url = ":memory:" # [agent] -# system_prompt = "You are a careful coding assistant." +# # system_prompt defaults to the built-in coding-agent guide when omitted. +# # system_prompt = "" # disable; or multi-line '''...''' to override # max_tool_result_chars = 32000 # parallel_tools = true # confirm_destructive = true diff --git a/src/plyngent/config/__init__.py b/src/plyngent/config/__init__.py index bbbfa70..fe8ddad 100644 --- a/src/plyngent/config/__init__.py +++ b/src/plyngent/config/__init__.py @@ -5,6 +5,7 @@ from typing import TYPE_CHECKING import tomlkit from tomlkit.exceptions import TOMLKitError +from .models import DEFAULT_SYSTEM_PROMPT as DEFAULT_SYSTEM_PROMPT from .models import AgentConfig as AgentConfig from .models import AnthropicProvider as AnthropicProvider from .models import DatabaseConfig as DatabaseConfig diff --git a/src/plyngent/config/models.py b/src/plyngent/config/models.py index 8e59492..7b29a97 100644 --- a/src/plyngent/config/models.py +++ b/src/plyngent/config/models.py @@ -2,6 +2,44 @@ from typing import Any from msgspec import Struct, field +# Built-in agent system prompt when ``[agent].system_prompt`` is omitted. +# Set ``system_prompt = ""`` in TOML to disable. Override with a multi-line +# TOML literal (prefer '''...''' so nested " quotes are fine). +DEFAULT_SYSTEM_PROMPT = """\ +You are a professional coding agent in a workspace-bound tool environment. + +### Workspace +- Explore with `tree`/`listdir`/`glob_paths`/`grep_files`/`read_file` when unsure. +- Stay under the workspace (or a path from `new_temporary_workspace`). Respect path denylists. + +### Files +- Prefer file tools over shell. `edit_replace` fails usually means a bad match — \ +fix `old_string` or `max_replaces`; shell workarounds rarely help. +- `edit_replace` defaults to the first match; if remaining matches are reported, \ +raise `max_replaces` or narrow `old_string`. +- Before `edit_lineno`, `read_file` with `with_lineno=true`. + +### Commands +- Prefer `run_command` / `run_command_batch` (argv, no shell) over `bash -c` or similar. +- Several `run_command` calls in one step may run in parallel; use `run_command_batch` \ +for ordered pipelines (`pipe_out` / `mix_stderr` as needed). +- Prefer `vcs_*` for status/diff/log/branch when enough. + +### Humans & safety +- Prefer `ask_user_line` / `ask_user_choice` / `ask_user_form` over waiting for the next free-form turn. +- Overwrites, deletes, shells, and risky ops may require human confirm; hard denylists are not skipped by YOLO. +- Temporary scratch: `new_temporary_workspace`. + +### PTY +- Use PTY for interactive/TUI, sudo, ssh, or live servers. +- Passwords and other secret input: only `ask_into_pty` (never echo secrets into `write_pty`). +- Control keys: `write_pty_keys`, not literal `write_pty` data. + +### Todos +- Use the todo stack for multi-step work (LIFO groups: push related items, finish/update, \ +pop the group). Open items mean unfinished work. +""" + class DatabaseConfig(Struct, omit_defaults=True): """Database connection configuration. @@ -20,7 +58,7 @@ class DatabaseConfig(Struct, omit_defaults=True): class AgentConfig(Struct, omit_defaults=True): """Single-user agent profile defaults.""" - system_prompt: str = "" + system_prompt: str = DEFAULT_SYSTEM_PROMPT max_tool_result_chars: int = 32_000 parallel_tools: bool = True confirm_destructive: bool = True diff --git a/tests/test_config/test_agent_section.py b/tests/test_config/test_agent_section.py index 51f9c1e..054cc39 100644 --- a/tests/test_config/test_agent_section.py +++ b/tests/test_config/test_agent_section.py @@ -2,7 +2,7 @@ from __future__ import annotations from typing import TYPE_CHECKING -from plyngent.config import load +from plyngent.config import DEFAULT_SYSTEM_PROMPT, load if TYPE_CHECKING: from pathlib import Path @@ -12,7 +12,8 @@ def test_agent_section_defaults(tmp_path: Path) -> None: path = tmp_path / "c.toml" _ = path.write_text("", encoding="utf-8") store = load(path) - assert store.agent_config.system_prompt == "" + assert store.agent_config.system_prompt == DEFAULT_SYSTEM_PROMPT + assert "professional coding agent" in store.agent_config.system_prompt assert store.agent_config.max_tool_result_chars == 32_000 assert store.agent_config.parallel_tools is True assert store.agent_config.confirm_destructive is True @@ -41,3 +42,16 @@ max_context_tokens = 5000 assert store.agent_config.confirm_destructive is False assert store.agent_config.path_denylist == ["/secrets/", ".ssh/"] assert store.agent_config.max_context_tokens == 5000 + + +def test_agent_system_prompt_empty_disables(tmp_path: Path) -> None: + path = tmp_path / "c.toml" + _ = path.write_text( + """ +[agent] +system_prompt = "" +""", + encoding="utf-8", + ) + store = load(path) + assert store.agent_config.system_prompt == ""