feat: 系统提示词改为单一入口统一管理

多个扩展各自整段改写系统提示词并按加载顺序接力,任一扩展定死提示词后
其余只能做字符串手术,且各上游的工具说明与组合包实际工具格局冲突。

- 新增 extensions/system-prompt.ts 作为唯一出口(加载列表最后),按
  config/prompt/(preamble.md、tools.jsonc、routing.jsonc、sections.jsonc)
  组合开场白、工具简介/规则与 <tool_routing> 分区;替代原封面与
  tool-routing 扩展,/dump-system-prompt 输出最终提示词。
- pi-chrome、pi-ssh、pi-hermes-memory 改为写命名分区,pi-rtk-optimizer
  改写 promptGuidelines,pi-permission-system 直接过滤结构化 skills,
  均不再返回 systemPrompt;pi-chrome 未授权时不再每轮冻结提示词。
- 新增 prompt-guard 代理加载第三方扩展(Hashline、FFF、LSP、
  context-mode):拦截直接改写提示词并按目录覆盖工具说明;context-mode
  去掉与组合包路由冲突的锚点段,保留记忆与恢复快照。
- 消除冲突:bash 简介不再建议 grep/find;Hashline edit 不再称可用 grep
  锚点、不再介绍已关闭的 replace_text;context-mode 读写/网页路由提示。
- 守门测试:工具目录覆盖率、禁用用法检查、context-mode 上游原文锁定;
  AGENTS.md 新增「系统提示词单一所有权」。

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
云服务部-叶林立
2026-10-20 17:53:04 -07:00
co-authored by Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
parent dc4fe92e27
commit ba164896d2
39 changed files with 1863 additions and 742 deletions
+1
View File
@@ -0,0 +1 @@
You are an expert coding assistant operating inside pi, a coding agent harness. You help users by reading files, executing commands, editing code, and writing new files.
+72
View File
@@ -0,0 +1,72 @@
{
// Cross-tool workflow rules rendered by extensions/system-prompt.ts into the
// <tool_routing> section. A rule is included when every tool in "whenAll"
// and at least one tool in "whenAny" is active; a rule with neither is
// always included. Rules render in file order as "- <text>".
"version": 1,
"rules": [
{
"whenAny": ["codegraph_explore"],
"text": "Use codegraph_explore first for architecture, symbol relationships, code flows, and symbols you are about to change. Treat the source it returns as already read for understanding; do not repeat the same exploration with read or search unless information is missing. Before editing, still read the exact target range to get fresh LINE#HASH anchors."
},
{
"whenAny": ["lsp_definition", "lsp_references", "lsp_hover", "lsp_symbols", "lsp_diagnostics"],
"text": "Use LSP for symbol definitions, references, hover/type information, and post-edit diagnostics when the language server supports the file."
},
{
"whenAny": ["find", "grep", "multi_grep"],
"text": "For literal search, narrow in stages instead of requesting a repository-wide dump:\n 1. Use find to identify likely files or directories. Constrain path/glob and keep the result limit small.\n 2. Use grep or multi_grep only inside the narrowed path or file to obtain matching line numbers. Start with a specific or literal pattern, a small context window, and a bounded limit.\n 3. Use read with offset/limit only for the exact matching region when full source is still needed."
},
{
"whenAny": ["find", "grep", "multi_grep"],
"text": "Do not run a broad search expected to return hundreds or thousands of files or matches. If results truncate or hit a limit, narrow the path, glob, or pattern instead of increasing the limit or dumping all results."
},
{
"whenAny": ["bash"],
"text": "Use bash for builds, tests, git, package scripts, and other programs. Never search files by running grep, find, rg, or fd through bash; use the find/grep tools."
},
{
"whenAny": ["ssh_connect"],
"text": "Use ssh_connect only when the user explicitly names an imported host as part of a concrete remote task. Call it as a separate step and wait for success before calling other ssh_* tools; never infer or substitute a different host."
},
{
"whenAny": ["ssh_cd"],
"text": "Treat ssh_cd as a reviewed persistent workspace transition: call it as a separate step and wait for success before issuing ssh_bash or relative ssh_read/ssh_write/ssh_edit/ssh_find/ssh_grep calls that depend on the new remote cwd. Use cd inside ssh_bash only for an intentionally temporary, command-local directory change."
},
{
"whenAny": ["ssh_find", "ssh_grep"],
"text": "For remote SSH searches, use ssh_find to narrow remote file paths before ssh_grep searches file contents. Keep path and limit bounded; when a result says truncated, narrow the path or pattern instead of increasing the limit. Do not run find, fd, grep, or rg through ssh_bash."
},
{
"whenAny": ["web_search", "web_fetch"],
"text": "Use web_search for current, real-world, or citable web information; provider selection is handled internally. Keep queries focused and use site/published_* filters only when they materially narrow results. Read selected pages with web_fetch instead of repeating similar searches."
},
{
"whenAll": ["web_fetch", "ctx_fetch_and_index"],
"text": "Read a web page with web_fetch. Use ctx_fetch_and_index only when the content should be indexed into the context-mode knowledge base for repeated ctx_search queries."
},
{
"whenAny": ["ctx_execute", "ctx_execute_file", "ctx_batch_execute"],
"text": "Use Context Mode for logs, test/build output, generated data, large-file exploration/analysis/summarization, and any command whose output may be large or unpredictable. Prefer one ctx_batch_execute over several ctx_execute calls for multi-command research, and print only the derived answer needed for the task."
},
{
"whenAny": ["ctx_execute_file"],
"text": "ctx_execute_file analyzes a file and never edits it; make changes with read + edit. It is confined to the current project root: do not call it for absolute paths outside the workspace, ../ traversal, or symlinks that resolve outside the workspace; host permission approval does not bypass this boundary. Use another explicitly authorized tool for those files."
},
{
"whenAll": ["chrome_snapshot", "ctx_execute_file"],
"text": "After chrome_snapshot or a Chrome action with includeSnapshot=true, immediately analyze the returned workspace-relative capture path with ctx_execute_file; never use read or cat for the raw JSON. Print only the uids, text, or state needed for the next action, and take a fresh snapshot after any interaction because the previous capture may be stale."
},
{
"whenAny": ["read"],
"text": "Use read only when exact source is needed or the file is small. Before editing, request the smallest useful offset/limit range; do not read a complete large file merely to copy, compare, hash, count, explore, or summarize it."
},
{
"whenAll": ["read", "edit"],
"text": "For existing-file changes, obtain fresh LINE#HASH anchors with a small read (find/grep output has no anchors), then use anchored edit operations. Reuse fresh anchors returned by successful edits for chained changes; re-read only when anchors are stale or the next target region was not shown."
},
{
"text": "Stop searching once the current evidence is sufficient. Avoid repeating the same lookup through CodeGraph, find, grep, and read without a concrete information gap."
}
]
}
+8
View File
@@ -0,0 +1,8 @@
{
// Order of named system-prompt sections contributed through
// systemPromptOptions.sections. extensions/system-prompt.ts reorders the
// contributed sections to this list; contributed sections not listed here
// keep their contribution order after the listed ones.
"version": 1,
"order": ["memory_policy", "remote_ssh", "chrome_profile_bridge", "tool_routing"]
}
+103
View File
@@ -0,0 +1,103 @@
{
// Bundle-authoritative tool guidance, owned by extensions/system-prompt.ts.
//
// Every tool the bundle can expose must have an entry. Per field:
// "snippet" — replaces the tool's one-line <tools> entry (null hides it)
// "guidelines" — replaces the tool's <rules> lines
// "description" — replaces the tool description at registration; only
// applied to third-party tools loaded through a bundle
// adapter (in-repo tools edit their own source instead)
// An absent field keeps the upstream text. "inherit": true means the
// upstream text was reviewed against this bundle and is kept as-is.
"version": 1,
"tools": {
"bash": {
"snippet": "Run shell commands for builds, tests, git, package scripts, and other programs; search files with the find/grep tools, not bash"
},
"read": { "inherit": true },
"edit": {
"guidelines": [
"Use edit with LINE#HASH anchors from the latest read or edit result for all file changes; batch every change to one file into a single edit call.",
"find/grep/multi_grep results carry plain line numbers, not LINE#HASH anchors; read the exact target range before editing.",
"Never reuse anchors from an earlier read or edit response once a newer one exists for the same file; treat only the most recent response as carrying valid anchors.",
"After a successful edit, the returned --- Anchors --- block replaces a re-read for nearby follow-up edits.",
"On [E_STALE_ANCHOR], the error lists the stale refs and any content-matched candidate anchors; re-read the file to get current anchors before retrying."
],
"description": "Patch a text file at `LINE#HASH` anchors copied verbatim from the latest read result or the anchors block of a previous edit. find/grep results have no anchors — read the target range first.\n\nBatch every change to a file into one `edit` call: all operations go in the `edits` array, every edit sets `op`, and all anchors must come from the same pre-edit read. Edits validate against one snapshot and apply together, so line numbers never shift between entries of the same call.\n\nOps:\n- `replace` — replace the single line at `pos`, or the inclusive span `pos`..`end`. `lines` is the complete new content for the whole span; `lines: []` deletes it. Without `end`, exactly one line is replaced no matter how many entries `lines` has.\n- `append` — insert `lines` after `pos`; omit `pos` to append at end of file.\n- `prepend` — insert `lines` before `pos`; omit `pos` to insert at start of file.\n\nExample — single-line and span replace in one call:\n```json\n{ \"path\": \"src/main.ts\", \"edits\": [\n { \"op\": \"replace\", \"pos\": \"12#MQ\", \"lines\": [\"const x = 1;\"] },\n { \"op\": \"replace\", \"pos\": \"5#VR\", \"end\": \"8#QV\", \"lines\": [\n \"function greet(name) {\",\n \" return `Hello, ${name}`;\",\n \"}\"\n ] }\n] }\n```\n\nRules:\n- `lines` is literal file content with exact indentation. Never include `LINE#HASH:` or bare `HH:` prefixes, diff `+`/`-` markers, or a copy of a neighboring line — the `:content` part of an anchor is context for you, not payload, and repeating a boundary line duplicates it in the file.\n- Anchors are opaque: copy them exactly, never compute, shift, or guess one.\n- An anchor may keep the `:content` suffix from read output (`\"12#MQ: const x = 1;\"`). The runtime cross-checks that content against the file — catching hash collisions and recovering whitespace-only drift — so keep it for high-risk anchors such as range endpoints. Copy the content as rendered; if you shorten it, keep the start of the line and mark the cut with `...`.\n- Edits in one call must not overlap or touch adjacent lines — merge such changes into a single edit."
},
"write": { "inherit": true },
"ls": { "inherit": true },
"powershell": { "inherit": true },
"grep": { "inherit": true },
"find": { "inherit": true },
"multi_grep": { "inherit": true },
"tool_search": { "inherit": true },
"codegraph_explore": { "inherit": true },
"lsp_diagnostics": { "inherit": true },
"lsp_definition": { "inherit": true },
"lsp_references": { "inherit": true },
"lsp_hover": { "inherit": true },
"lsp_symbols": { "inherit": true },
"web_search": { "inherit": true },
"web_fetch": { "inherit": true },
"ctx_execute": { "inherit": true },
"ctx_execute_file": { "inherit": true },
"ctx_batch_execute": { "inherit": true },
"ctx_index": { "inherit": true },
"ctx_search": { "inherit": true },
"ctx_fetch_and_index": { "inherit": true },
"ctx_stats": { "inherit": true },
"ctx_doctor": { "inherit": true },
"ctx_insight": { "inherit": true },
"ctx_upgrade": { "inherit": true },
"ctx_purge": { "inherit": true },
"memory_search": { "inherit": true },
"session_search": { "inherit": true },
"memory_add": { "inherit": true },
"memory_replace": { "inherit": true },
"memory_remove": { "inherit": true },
"skill_manage": { "inherit": true },
"context_tree_query": { "inherit": true },
"ask_user_question": { "inherit": true },
"ssh_connect": { "inherit": true },
"ssh_cd": { "inherit": true },
"ssh_read": { "inherit": true },
"ssh_write": { "inherit": true },
"ssh_edit": { "inherit": true },
"ssh_find": { "inherit": true },
"ssh_grep": { "inherit": true },
"ssh_bash": { "inherit": true },
"Agent": { "inherit": true },
"get_subagent_result": { "inherit": true },
"steer_subagent": { "inherit": true },
"chrome_launch": { "inherit": true },
"chrome_tab": { "inherit": true },
"chrome_navigate": { "inherit": true },
"chrome_snapshot": { "inherit": true },
"chrome_find": { "inherit": true },
"chrome_inspect": { "inherit": true },
"chrome_wait_for": { "inherit": true },
"chrome_screenshot": { "inherit": true },
"chrome_click": { "inherit": true },
"chrome_type": { "inherit": true },
"chrome_fill": { "inherit": true },
"chrome_key": { "inherit": true },
"chrome_hover": { "inherit": true },
"chrome_drag": { "inherit": true },
"chrome_tap": { "inherit": true },
"chrome_scroll": { "inherit": true },
"chrome_evaluate": { "inherit": true },
"chrome_list_console_messages": { "inherit": true },
"chrome_list_network_requests": { "inherit": true },
"chrome_get_network_request": { "inherit": true },
"chrome_upload_file": { "inherit": true }
}
}