feat(prompt): 搜索引导改为意图→工具的正向指引,multi_grep 按上游保持关闭

真实会话中模型仍用 bash grep -o | sort -u 做抽取,被拒后直接放弃核对;
原提示词只说"不要",没告诉它抽取/计数、给程序传文件清单、过滤命令输出时该用什么。

- routing:按已知信息选 grep/find(多变体用 grep 正则 alternation),
  codemode + tools.grep 做聚合,bash 文件清单用 git ls-files,
  overflow_query 过滤输出,被拒后改用替代方案继续
- 权限策略:rg/grep/git grep/find/fd 的 deny 附带指向替代工具的 reason
- tools.jsonc:grep/find 新 snippet,补上 codegraph_explore snippet
- FFF 上游标注 multi_grep 有害:wrapper 删除 PI_FFF_MULTIGREP,清理全部引用
- JSONC 测试改用字符串感知的注释剥离(reason 中含 'src/**/*.ts')

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-22 00:14:49 -07:00
co-authored by Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
parent 833b9514d4
commit dae1a01f23
16 changed files with 68 additions and 39 deletions
+1 -1
View File
@@ -20,7 +20,7 @@
| `pi-notify/` | Kitty OSC 99 完成与权限确认通知,精确聚焦来源窗口 | | `pi-notify/` | Kitty OSC 99 完成与权限确认通知,精确聚焦来源窗口 |
| `pi-compaction/` | 压力触发的观察遮蔽(Pi context_edit)、阈值整段摘要、effective token、native compaction | | `pi-compaction/` | 压力触发的观察遮蔽(Pi context_edit)、阈值整段摘要、effective token、native compaction |
| `pi-overflow/` | 新工具结果统一截断:超阈值原文落盘 + 有界视图 + `overflow_query` | | `pi-overflow/` | 新工具结果统一截断:超阈值原文落盘 + 有界视图 + `overflow_query` |
| `extensions/fff-override.ts` | FFF `override` 模式接管 `find`/`grep`/`multi_grep` 与 `@` 补全 | | `extensions/fff-override.ts` | FFF `override` 模式接管 `find`/`grep` 与 `@` 补全,`multi_grep` 按上游保持关闭 |
| `extensions/codegraph.ts` | 原生 `codegraph_explore`,自有 stdio JSON-RPC 直连 `codegraph serve`(`codegraph-client.ts`) | | `extensions/codegraph.ts` | 原生 `codegraph_explore`,自有 stdio JSON-RPC 直连 `codegraph serve`(`codegraph-client.ts`) |
| `extensions/hashline.ts` | 部署 `config/pi-hashline-edit.json` 后加载 pi-hashline-edit 覆盖 `read`/`edit`(`hashline-config.ts`) | | `extensions/hashline.ts` | 部署 `config/pi-hashline-edit.json` 后加载 pi-hashline-edit 覆盖 `read`/`edit`(`hashline-config.ts`) |
| `extensions/permission-system.ts` | 直接加载仓库内 `pi-permission-system/src/index.ts` | | `extensions/permission-system.ts` | 直接加载仓库内 `pi-permission-system/src/index.ts` |
+4 -3
View File
@@ -2,7 +2,7 @@
个人维护的 Pi 扩展组合包。安装一次即可加载并配置: 个人维护的 Pi 扩展组合包。安装一次即可加载并配置:
- `@ff-labs/pi-fff@0.10.5`:使用官方 `override` 模式接管 `find`、`grep` 和 `multi_grep`。 - `@ff-labs/pi-fff@0.10.5`:使用官方 `override` 模式接管 `find` 和 `grep`;上游默认关闭的 `multi_grep` 保持关闭。
- 本仓库维护的 `pi-rtk-optimizer`:只处理 Bash/read 等输出压缩,默认不改写命令,也不处理搜索工具结果。 - 本仓库维护的 `pi-rtk-optimizer`:只处理 Bash/read 等输出压缩,默认不改写命令,也不处理搜索工具结果。
- 本仓库自维护的 `pi-web-search`:对模型只暴露 `web_search` / `web_fetch` 两个工具;`web_search` 由当前主模型在 Tavily / Exa / Keenable 间路由,`web_fetch` 固定走 Exa,三家均以纯 `fetch` 直连(Exa 为 `mcp.exa.ai` 的 JSON-RPC,不经 MCP adapter)。 - 本仓库自维护的 `pi-web-search`:对模型只暴露 `web_search` / `web_fetch` 两个工具;`web_search` 由当前主模型在 Tavily / Exa / Keenable 间路由,`web_fetch` 固定走 Exa,三家均以纯 `fetch` 直连(Exa 为 `mcp.exa.ai` 的 JSON-RPC,不经 MCP adapter)。
- `extensions/codegraph.ts`:注册原生 `codegraph_explore` 工具,按索引根惰性启动常驻 `codegraph serve --mcp` 子进程,由组合包自有 stdio JSON-RPC 客户端直连(无 MCP adapter/SDK);serve 的文件监听自动同步索引,无结果缓存;未初始化项目经确认后才 `codegraph init`,拒绝记入会话不再重复询问。 - `extensions/codegraph.ts`:注册原生 `codegraph_explore` 工具,按索引根惰性启动常驻 `codegraph serve --mcp` 子进程,由组合包自有 stdio JSON-RPC 客户端直连(无 MCP adapter/SDK);serve 的文件监听自动同步索引,无结果缓存;未初始化项目经确认后才 `codegraph init`,拒绝记入会话不再重复询问。
@@ -138,7 +138,7 @@ CodeGraph 的更新或遥测设置。
### 工具与搜索路由 ### 工具与搜索路由
`config/prompt/routing.jsonc` 定义跨工具路由规则,由 `extensions/system-prompt.ts` 在每轮开始前按当前激活工具渲染为 `<tool_routing>` 分区。代码结构、调用关系和待修改 symbol 优先使用 CodeGraph,定义、引用、类型和修改后诊断优先使用 LSP;仓库字面搜索先用 FFF `find` 缩小文件或目录范围,再在已收敛的路径中用 `grep`/`multi_grep` 获取行号。宽泛搜索命中大量结果、发生截断或达到上限时,应继续缩小路径、glob 或 pattern,而不是提高 limit 或输出全部结果。 `config/prompt/routing.jsonc` 定义跨工具路由规则,由 `extensions/system-prompt.ts` 在每轮开始前按当前激活工具渲染为 `<tool_routing>` 分区。代码结构、调用关系和待修改 symbol 优先使用 CodeGraph,定义、引用、类型和修改后诊断优先使用 LSP;本地字面搜索按已知信息直接选工具:确切标识符或文本用 `grep` 字面模式并限定 path/exclude,多个命名变体合并为一次 `grep` 正则 alternation,文件名、路径片段或功能概念用 FFF `find` 模糊全路径匹配,然后只 `read` 命中区域。对搜索结果做提取、去重、计数或聚合时,在 codemode 脚本中调用 `tools.grep` / `tools.find` 并用 JavaScript 后处理;bash 中不得运行 `grep`/`rg`/`find`/`fd`/`git grep`(策略 deny 的 reason 指明替代工具),程序需要文件清单时传 `$(git ls-files '<glob>')`。宽泛搜索命中大量结果、发生截断或达到上限时,应继续缩小路径、glob 或 pattern,而不是提高 limit 或输出全部结果。
在线搜索默认按任务只选一个服务:关键词不确定、新闻/近期话题或需要较多候选来源时优先 Tavily;官方/API 文档、版本信息、论文和高确定性事实确认优先 Exa;中文政策、政府公告、中文网页、站点限定和日期筛选优先 Keenable。只有来源质量或不确定性确实需要时才跨服务复核,避免默认把三套结果全部塞进上下文。 在线搜索默认按任务只选一个服务:关键词不确定、新闻/近期话题或需要较多候选来源时优先 Tavily;官方/API 文档、版本信息、论文和高确定性事实确认优先 Exa;中文政策、政府公告、中文网页、站点限定和日期筛选优先 Keenable。只有来源质量或不确定性确实需要时才跨服务复核,避免默认把三套结果全部塞进上下文。
@@ -211,7 +211,8 @@ Hashline 不负责压缩大文件;每行锚点本身还会增加少量 token
`extensions/fff-override.ts` 强制设置 FFF 官方的 `PI_FFF_MODE=override`(显式传入 `extensions/fff-override.ts` 强制设置 FFF 官方的 `PI_FFF_MODE=override`(显式传入
`--fff-mode` 仍按 FFF 官方优先级覆盖它)。FFF 替换 `--fff-mode` 仍按 FFF 官方优先级覆盖它)。FFF 替换
Pi 的 `find` 和 `grep`,并提供 `multi_grep`;RTK 对所有 `grep` 工具结果直接跳过, Pi 的 `find` 和 `grep`;`multi_grep` 上游标注为有害、默认关闭,wrapper 在加载前
删除 `PI_FFF_MULTIGREP` 环境变量,使其永远无法注册。RTK 对所有 `grep` 工具结果直接跳过,
也默认关闭 Bash 命令改写,因此不会把 `rg`、`grep`、`find` 或 `fd` 改写到 RTK。 也默认关闭 Bash 命令改写,因此不会把 `rg`、`grep`、`find` 或 `fd` 改写到 RTK。
### 输出压缩归 RTK ### 输出压缩归 RTK
+5 -6
View File
@@ -37,7 +37,6 @@
"edit": "allow", "edit": "allow",
"grep": "allow", "grep": "allow",
"find": "allow", "find": "allow",
"multi_grep": "allow",
"ls": "allow", "ls": "allow",
"codegraph_explore": "allow", "codegraph_explore": "allow",
"lsp_diagnostics": "allow", "lsp_diagnostics": "allow",
@@ -103,10 +102,10 @@
}, },
"bash": { "bash": {
"*": "allow", "*": "allow",
"rg*": "deny", "rg*": { "action": "deny", "reason": "Bash content search is disabled. Locally use the grep tool (pattern + path/exclude; regex alternation for several names); to extract/dedupe/count matches, call tools.grep in codemode and process in JS; to filter command output, use overflow_query on the stored result. Over SSH use ssh_grep." },
"grep*": "deny", "grep*": { "action": "deny", "reason": "Bash content search is disabled. Locally use the grep tool (pattern + path/exclude; regex alternation for several names); to extract/dedupe/count matches, call tools.grep in codemode and process in JS; to filter command output, use overflow_query on the stored result. Over SSH use ssh_grep." },
"find*": "deny", "find*": { "action": "deny", "reason": "Bash file search is disabled. Locally use the find tool (fuzzy path, or a glob in path such as 'src/**/*.ts'); to pass a file list to a program use $(git ls-files '<glob>'). Over SSH use ssh_find." },
"fd*": "deny", "fd*": { "action": "deny", "reason": "Bash file search is disabled. Locally use the find tool (fuzzy path, or a glob in path such as 'src/**/*.ts'); to pass a file list to a program use $(git ls-files '<glob>'). Over SSH use ssh_find." },
"git *": "ask", "git *": "ask",
"git status*": "allow", "git status*": "allow",
"git diff*": "allow", "git diff*": "allow",
@@ -114,7 +113,7 @@
"git show*": "allow", "git show*": "allow",
"git rev-parse*": "allow", "git rev-parse*": "allow",
"git ls-files*": "allow", "git ls-files*": "allow",
"git grep*": "deny", "git grep*": { "action": "deny", "reason": "Bash content search is disabled. Locally use the grep tool (pattern + path/exclude; regex alternation for several names); to extract/dedupe/count matches, call tools.grep in codemode and process in JS; to filter command output, use overflow_query on the stored result. Over SSH use ssh_grep." },
"git branch": "allow", "git branch": "allow",
"git branch --show-current*": "allow", "git branch --show-current*": "allow",
"git remote -v": "allow", "git remote -v": "allow",
+17 -5
View File
@@ -16,16 +16,25 @@
"text": "Use LSP for symbol definitions, references, hover/type information, and post-edit diagnostics when the language server supports the file." "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"], "whenAny": ["find", "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." "text": "Pick the local search tool by what you already know:\n - Exact identifier or text: grep with a literal pattern, constrained by path/exclude. Do not run find first just to locate it.\n - Several identifiers or naming variants: one grep with a regex alternation (e.g. user_id|userId|UserId) instead of several greps.\n - A filename, path fragment, or feature concept: find (fuzzy whole-path match; use a glob in path for exact names).\n - Then read only the matching region with offset/limit."
}, },
{ {
"whenAny": ["find", "grep", "multi_grep"], "whenAny": ["find", "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." "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."
}, },
{
"whenAll": ["codemode"],
"whenAny": ["find", "grep"],
"text": "To extract, deduplicate, count, or aggregate across search matches (what grep -o | sort -u | wc would do), call tools.grep / tools.find inside one codemode script and post-process the results in JavaScript."
},
{ {
"whenAny": ["bash"], "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." "text": "Use bash for builds, tests, git, package scripts, and other programs. Never run grep, rg, find, fd, or git grep in bash — standalone, piped, inside a loop, or in $(…). Search files with the grep/find tools; when a program needs a file list (wc, node, tsc), pass $(git ls-files '<glob>')."
},
{
"whenAll": ["bash", "overflow_query"],
"text": "To filter a command's output, run it without | grep; if the result carries a [pi-overflow id=…] marker, search it with overflow_query instead of re-running."
}, },
{ {
"whenAny": ["ssh_connect"], "whenAny": ["ssh_connect"],
@@ -59,12 +68,15 @@
}, },
{ {
"whenAny": ["read"], "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." "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 source file merely to copy, compare, hash, count, or explore it. Reading project overview docs such as a README in full is fine."
}, },
{ {
"whenAll": ["read", "edit"], "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": "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": "When a tool call is denied by policy, use the alternative named in the denial and continue the subtask; do not drop it or present unverified facts as checked."
},
{ {
"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." "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."
} }
+1 -1
View File
@@ -14,7 +14,7 @@ You locate code and answer "where is X / what references Y" questions for the de
## Hard limits ## Hard limits
- Do not create, modify, move, copy, or delete files, including temporary files anywhere (also /tmp). - Do not create, modify, move, copy, or delete files, including temporary files anywhere (also /tmp).
- Use bash only for read-only commands such as git status, git log, git diff, git show, and ls. Never use output redirection, heredocs, or any command that changes system state. - Use bash only for read-only commands such as git status, git log, git diff, git show, and ls. Never use output redirection, heredocs, or any command that changes system state.
- Never search through bash (grep, rg, find, fd); use the find and grep tools. - Never search through bash (grep, rg, find, fd); use the find and grep tools. For several names use one grep with a regex alternation (a|b); when a program needs a file list, pass $(git ls-files '<glob>').
## Approach ## Approach
- Match the requested breadth: "quick" is one targeted lookup, "medium" covers a few related locations, "very thorough" searches multiple locations and naming conventions. - Match the requested breadth: "quick" is one targeted lookup, "medium" covers a few related locations, "very thorough" searches multiple locations and naming conventions.
+1 -1
View File
@@ -14,7 +14,7 @@ You explore the codebase and return an implementation plan to the delegating age
## Hard limits ## Hard limits
- Do not create, modify, move, copy, or delete files, including temporary files anywhere (also /tmp). - Do not create, modify, move, copy, or delete files, including temporary files anywhere (also /tmp).
- Use bash only for read-only commands such as git status, git log, git diff, git show, and ls. Never use output redirection, heredocs, or any command that changes system state. - Use bash only for read-only commands such as git status, git log, git diff, git show, and ls. Never use output redirection, heredocs, or any command that changes system state.
- Never search through bash (grep, rg, find, fd); use the find and grep tools. - Never search through bash (grep, rg, find, fd); use the find and grep tools. For several names use one grep with a regex alternation (a|b); when a program needs a file list, pass $(git ls-files '<glob>').
## Process ## Process
1. Restate the goal and the constraints you were given. Record anything ambiguous as an open question instead of guessing. 1. Restate the goal and the constraints you were given. Record anything ambiguous as an open question instead of guessing.
+5 -6
View File
@@ -18,7 +18,7 @@
"edit": { "edit": {
"guidelines": [ "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.", "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.", "find/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.", "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.", "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." "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."
@@ -28,19 +28,18 @@
"write": { "inherit": true }, "write": { "inherit": true },
"ls": { "inherit": true }, "ls": { "inherit": true },
"powershell": { "inherit": true }, "powershell": { "inherit": true },
"grep": { "inherit": true }, "grep": { "snippet": "Search file contents via the FFF index (git-aware, smart-case, frecency-ranked, regex or literal); replaces bash grep/rg" },
"find": { "inherit": true }, "find": { "snippet": "Find files by fuzzy path or glob via the FFF index; replaces bash find/fd" },
"multi_grep": { "inherit": true },
"tool_search": { "inherit": true }, "tool_search": { "inherit": true },
"codemode": { "codemode": {
"guidelines": [ "guidelines": [
"Use codemode to batch two or more independent tool calls (Promise.allSettled), chain dependent lookups, or filter large output down to what you need; call a single tool directly instead of wrapping it in a script.", "Use codemode to batch two or more independent tool calls (Promise.allSettled), chain dependent lookups, or filter large output down to what you need; call a single tool directly instead of wrapping it in a script.",
"Call edit, write, ssh_connect, ssh_cd, ask_user_question, and Agent directly, never from a codemode script.", "Call edit, write, ssh_connect, ssh_cd, ask_user_question, and Agent directly, never from a codemode script.",
"Inside scripts, search with tools.grep, tools.find, or tools.multi_grep; never run grep, find, rg, or fd through tools.bash." "Inside scripts, search with tools.grep or tools.find and do extraction, dedup, and counting in JavaScript; never run grep, find, rg, or fd through tools.bash."
] ]
}, },
"codegraph_explore": { "inherit": true }, "codegraph_explore": { "snippet": "Explore code structure from the CodeGraph index: symbols, relationships, call flows, and their source" },
"lsp_diagnostics": { "inherit": true }, "lsp_diagnostics": { "inherit": true },
"lsp_definition": { "inherit": true }, "lsp_definition": { "inherit": true },
"lsp_references": { "inherit": true }, "lsp_references": { "inherit": true },
+1 -1
View File
@@ -24,7 +24,7 @@
## 各 wrapper 约定 ## 各 wrapper 约定
- `fff-override.ts`:强制 FFF 官方 `override` 模式,统一接管 `find`、`grep`、`multi_grep` 和 `@` 补全;FFF 独占本地字面搜索,远端服务器搜索由 `ssh_find` / `ssh_grep` 负责,RTK 不得接管搜索工具(见 `pi-rtk-optimizer/AGENTS.md`)。显式 CLI flag 仍遵循 FFF 官方优先级。 - `fff-override.ts`:强制 FFF 官方 `override` 模式,统一接管 `find`、`grep` 和 `@` 补全;`multi_grep` 按上游保持关闭(加载前删除 `PI_FFF_MULTIGREP` 环境变量)。FFF 独占本地字面搜索,远端服务器搜索由 `ssh_find` / `ssh_grep` 负责,RTK 不得接管搜索工具(见 `pi-rtk-optimizer/AGENTS.md`)。显式 CLI flag 仍遵循 FFF 官方优先级。
- `codegraph.ts` + `codegraph-client.ts`:注册原生 `codegraph_explore` 工具,按索引根惰性常驻 `codegraph serve --mcp -p <root>` 子进程,经组合包自有 stdio 逐行 JSON-RPC 客户端直连(不经 shell、不带 pi-mcp-adapter 或 MCP SDK);serve 进程内置文件监听自动同步索引,无结果缓存。子进程 10 分钟无调用自动关闭、会话结束统一关闭;调用中死亡换新客户端透明重试一次;其启动的上游 daemon(`.codegraph/daemon.sock`)退出后可多驻留 ≤5 分钟,属上游行为,组合包不管理。 - `codegraph.ts` + `codegraph-client.ts`:注册原生 `codegraph_explore` 工具,按索引根惰性常驻 `codegraph serve --mcp -p <root>` 子进程,经组合包自有 stdio 逐行 JSON-RPC 客户端直连(不经 shell、不带 pi-mcp-adapter 或 MCP SDK);serve 进程内置文件监听自动同步索引,无结果缓存。子进程 10 分钟无调用自动关闭、会话结束统一关闭;调用中死亡换新客户端透明重试一次;其启动的上游 daemon(`.codegraph/daemon.sock`)退出后可多驻留 ≤5 分钟,属上游行为,组合包不管理。
- CodeGraph 是机器级依赖,`install.sh`/`update.sh` 经官方安装器管理 `codegraph` CLI,组合包不内置其运行时。安装、升级和普通启动流程不得执行 `codegraph init`;只有 `codegraph_explore` 在未初始化项目上被调用、且用户在 `ctx.ui.confirm` 中明确确认后才可 `codegraph init` 当前目录,拒绝记入会话级 declined 不再重复询问;init 失败必须如实报告并让模型回退内置工具;不得改动 CodeGraph 更新或遥测设置。CLI 缺失、init 被拒或服务调用失败都不得抛出,必须以文本结果打回并引导模型改用 `find`/`grep`/`read`/LSP。已移除的 `plugin-init.ts`(原 `/plugin_init`)职责已并入本文件的惰性确认,不得恢复。 - CodeGraph 是机器级依赖,`install.sh`/`update.sh` 经官方安装器管理 `codegraph` CLI,组合包不内置其运行时。安装、升级和普通启动流程不得执行 `codegraph init`;只有 `codegraph_explore` 在未初始化项目上被调用、且用户在 `ctx.ui.confirm` 中明确确认后才可 `codegraph init` 当前目录,拒绝记入会话级 declined 不再重复询问;init 失败必须如实报告并让模型回退内置工具;不得改动 CodeGraph 更新或遥测设置。CLI 缺失、init 被拒或服务调用失败都不得抛出,必须以文本结果打回并引导模型改用 `find`/`grep`/`read`/LSP。已移除的 `plugin-init.ts`(原 `/plugin_init`)职责已并入本文件的惰性确认,不得恢复。
- `hashline.ts` + `hashline-config.ts`:部署组合包权威 `config/pi-hashline-edit.json` 后加载 `pi-hashline-edit`;默认以 2 字符行哈希覆盖内置 `read` / `edit`,`grep` 与 `replaceText` 均关闭。它保证锚定编辑而不负责压缩,大文件上下文节省必须来自先定位/隔离分析、再做最小范围 `offset/limit` 读取。 - `hashline.ts` + `hashline-config.ts`:部署组合包权威 `config/pi-hashline-edit.json` 后加载 `pi-hashline-edit`;默认以 2 字符行哈希覆盖内置 `read` / `edit`,`grep` 与 `replaceText` 均关闭。它保证锚定编辑而不负责压缩,大文件上下文节省必须来自先定位/隔离分析、再做最小范围 `offset/limit` 读取。
+1
View File
@@ -5,5 +5,6 @@ import { guardExtensionApi } from "./prompt-guard.ts";
/** Load FFF as the bundle's canonical `find` and `grep` implementation. */ /** Load FFF as the bundle's canonical `find` and `grep` implementation. */
export default function fffOverrideExtension(pi: ExtensionAPI): void { export default function fffOverrideExtension(pi: ExtensionAPI): void {
process.env.PI_FFF_MODE = "override"; process.env.PI_FFF_MODE = "override";
delete process.env.PI_FFF_MULTIGREP;
fffExtension(guardExtensionApi(pi)); fffExtension(guardExtensionApi(pi));
} }
@@ -7,6 +7,7 @@ import {
autoReviewConfigFileSchema, autoReviewConfigFileSchema,
normalizeAutoReviewConfig, normalizeAutoReviewConfig,
} from "#src/auto-review/config"; } from "#src/auto-review/config";
import { stripJsonComments } from "#src/config-loader";
import { unifiedConfigSchema } from "#src/config-schema"; import { unifiedConfigSchema } from "#src/config-schema";
/** The bundle's authoritative policy seed, as shipped. */ /** The bundle's authoritative policy seed, as shipped. */
@@ -16,10 +17,10 @@ const SEED_PATH = fileURLToPath(
function readSeed(): Record<string, unknown> { function readSeed(): Record<string, unknown> {
const raw = readFileSync(SEED_PATH, "utf8"); const raw = readFileSync(SEED_PATH, "utf8");
const stripped = raw // Parse with the same string-aware stripper the loader uses: a naive regex
.replace(/^\s*\/\/.*$/gm, "") // strips `/*`-looking runs inside string values (e.g. a `'src/**/*.ts'`
.replace(/\/\*[\s\S]*?\*\//g, ""); // glob in a deny reason) and corrupts the JSON.
return JSON.parse(stripped) as Record<string, unknown>; return JSON.parse(stripJsonComments(raw)) as Record<string, unknown>;
} }
describe("bundled policy seed", () => { describe("bundled policy seed", () => {
+1
View File
@@ -3,6 +3,7 @@
## Unreleased — my-pi maintained source ## Unreleased — my-pi maintained source
### Changed ### Changed
- Removed `multi_grep` from the `filesystem-navigation` group: the bundle wrapper keeps FFF's upstream `PI_FFF_MULTIGREP` opt-in disabled, so the tool is never registered and the group now covers only `ls`.
- Pinned Pi's built-in `codemode` (exposure `model-only`) in `alwaysEnabled` so batching several tool calls through one script never depends on a `tool_search` activation; `platformToolPolicy` now also drops tools with `hidden` or `codemode` (script-only) exposure so they can never enter the catalog or the unknown-tools path. - Pinned Pi's built-in `codemode` (exposure `model-only`) in `alwaysEnabled` so batching several tool calls through one script never depends on a `tool_search` activation; `platformToolPolicy` now also drops tools with `hidden` or `codemode` (script-only) exposure so they can never enter the catalog or the unknown-tools path.
- Removed the `PI_TOOL_SEARCH_CONFIG_PATH` environment override; the extension reads the bundle `config/tool-search.json`, and tests inject a path through the `createToolSearchExtension({ configPath })` factory. - Removed the `PI_TOOL_SEARCH_CONFIG_PATH` environment override; the extension reads the bundle `config/tool-search.json`, and tests inject a path through the `createToolSearchExtension({ configPath })` factory.
- Removed the retired context-mode integration: the `ctx_execute` / `ctx_execute_file` / `ctx_batch_execute` pins left `alwaysEnabled` and the four `context-*` bundle groups were dropped. Large tool results are now bounded by pi-overflow and recoverable through the pinned `overflow_query`. - Removed the retired context-mode integration: the `ctx_execute` / `ctx_execute_file` / `ctx_batch_execute` pins left `alwaysEnabled` and the four `context-*` bundle groups were dropped. Large tool results are now bounded by pi-overflow and recoverable through the pinned `overflow_query`.
+1 -1
View File
@@ -26,7 +26,7 @@ Run `/tool-search-status` to inspect the current catalog source, pinned count, d
| Group | Bundle tools | | Group | Bundle tools |
|---|---| |---|---|
| `filesystem-navigation` | `ls`, `multi_grep` | | `filesystem-navigation` | `ls` |
| `ssh-connection` | `ssh_connect` | | `ssh-connection` | `ssh_connect` |
| `ssh-remote-files` | `ssh_read`, `ssh_write`, `ssh_edit` | | `ssh-remote-files` | `ssh_read`, `ssh_write`, `ssh_edit` |
| `ssh-remote-search` | `ssh_find`, `ssh_grep` | | `ssh-remote-search` | `ssh_find`, `ssh_grep` |
+4 -4
View File
@@ -25,11 +25,11 @@ export const BUNDLE_GROUP_DEFINITIONS: BundleGroupDefinition[] = [
{ {
id: "filesystem-navigation", id: "filesystem-navigation",
title: "Filesystem navigation", title: "Filesystem navigation",
summary: "Inspect directory layouts and run bounded multi-pattern literal searches.", summary: "Inspect directory layouts.",
useWhen: ["You need directory entries or several literal searches after narrowing a path"], useWhen: ["You need the alphabetical directory entries of an unknown path"],
avoidWhen: ["Code relationships or types are the real question"], avoidWhen: ["Code relationships or types are the real question"],
keywords: ["files", "directory", "list", "multi grep", "目录", "文件", "多模式搜索"], keywords: ["files", "directory", "list", "目录", "文件"],
tools: ["ls", "multi_grep"], tools: ["ls"],
}, },
{ {
id: "ssh-connection", id: "ssh-connection",
+6 -3
View File
@@ -16,7 +16,10 @@ import {
import { CONFIG_PATH as RTK_CONFIG_PATH } from "../pi-rtk-optimizer/src/constants.ts"; import { CONFIG_PATH as RTK_CONFIG_PATH } from "../pi-rtk-optimizer/src/constants.ts";
import { CONFIG_PATH as COMPACTION_CONFIG_PATH } from "../pi-compaction/src/config.ts"; import { CONFIG_PATH as COMPACTION_CONFIG_PATH } from "../pi-compaction/src/config.ts";
import { WORKTREE_CONFIG_PATH } from "../pi-worktree/src/settings.ts"; import { WORKTREE_CONFIG_PATH } from "../pi-worktree/src/settings.ts";
import { resolvePreamblePath } from "../extensions/system-prompt-config.ts"; import {
resolvePreamblePath,
stripJsoncComments,
} from "../extensions/system-prompt-config.ts";
const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const bundleConfigDir = join(repositoryRoot, "config"); const bundleConfigDir = join(repositoryRoot, "config");
@@ -48,9 +51,9 @@ const BUNDLE_CONFIG_FILES = [
"p10k.zsh", "p10k.zsh",
]; ];
/** Strip JSONC comments (line and block) — same approach as the seed test. */ /** Strip JSONC comments (line and block) without touching string literals. */
function stripJsonc(raw: string): string { function stripJsonc(raw: string): string {
return raw.replace(/^\s*\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, ""); return stripJsoncComments(raw);
} }
test("every bundle config file exists and is non-empty", async () => { test("every bundle config file exists and is non-empty", async () => {
+8
View File
@@ -343,4 +343,12 @@ test("all package extensions load together without global registration conflicts
const codemode = probe.all.find((tool) => tool.name === "codemode"); const codemode = probe.all.find((tool) => tool.name === "codemode");
assert.equal(codemode?.exposure, "model-only", "Pi's built-in codemode must load with model-only exposure"); assert.equal(codemode?.exposure, "model-only", "Pi's built-in codemode must load with model-only exposure");
assert.ok(probe.active.includes("codemode"), "Tool Search must pin codemode into the active set"); assert.ok(probe.active.includes("codemode"), "Tool Search must pin codemode into the active set");
assert.ok(
probe.all.every((tool) => tool.name !== "multi_grep"),
"FFF's upstream-flagged multi_grep must stay unregistered (fff-override deletes PI_FFF_MULTIGREP)",
);
assert.ok(
!probe.active.includes("multi_grep"),
"multi_grep must not appear in the active tool set",
);
}); });
+7 -3
View File
@@ -255,7 +255,7 @@ test("bundled routing rules select the same guidance the retired tool-routing ex
const routing = loadRoutingRules(); const routing = loadRoutingRules();
const literal = renderRouting(routing.rules, ["find", "grep", "read"]); const literal = renderRouting(routing.rules, ["find", "grep", "read"]);
assert.match(literal, /Use find to identify likely files or directories/); assert.match(literal, /Pick the local search tool by what you already know/);
assert.match(literal, /narrow the path, glob, or pattern instead of increasing the limit/); assert.match(literal, /narrow the path, glob, or pattern instead of increasing the limit/);
const ssh = renderRouting(routing.rules, ["ssh_connect", "ssh_cd", "ssh_bash"]); const ssh = renderRouting(routing.rules, ["ssh_connect", "ssh_cd", "ssh_bash"]);
@@ -267,7 +267,7 @@ test("bundled routing rules select the same guidance the retired tool-routing ex
const minimal = renderRouting(routing.rules, ["read"]); const minimal = renderRouting(routing.rules, ["read"]);
assert.doesNotMatch(minimal, /codegraph_explore first/); assert.doesNotMatch(minimal, /codegraph_explore first/);
assert.doesNotMatch(minimal, /For literal search/); assert.doesNotMatch(minimal, /Pick the local search tool/);
assert.doesNotMatch(minimal, /ssh_connect only when/); assert.doesNotMatch(minimal, /ssh_connect only when/);
assert.match(minimal, /Stop searching once the current evidence is sufficient/); assert.match(minimal, /Stop searching once the current evidence is sufficient/);
}); });
@@ -397,7 +397,6 @@ test("composer splices the preamble, applies the catalog, and orders sections",
read: "reads files", read: "reads files",
bash: "runs commands", bash: "runs commands",
edit: "edits", edit: "edits",
codegraph_explore: "explores",
tool_search: "searches", tool_search: "searches",
}, },
toolGuidelines: { edit: ["upstream edit rule"] }, toolGuidelines: { edit: ["upstream edit rule"] },
@@ -414,6 +413,11 @@ test("composer splices the preamble, applies the catalog, and orders sections",
"the bundle preamble replaces Pi's default preamble", "the bundle preamble replaces Pi's default preamble",
); );
assert.match(output, /<tool_routing>\n- Use codegraph_explore first/); assert.match(output, /<tool_routing>\n- Use codegraph_explore first/);
assert.match(
output,
/<tools>[\s\S]*?- codegraph_explore: Explore code structure from the CodeGraph index/,
"tools.jsonc must add a snippet for codegraph_explore, which has no upstream <tools> entry",
);
assert.match(output, /search files with the find\/grep tools, not bash/); assert.match(output, /search files with the find\/grep tools, not bash/);
assert.match(output, /LINE#HASH anchors/); assert.match(output, /LINE#HASH anchors/);
assert.ok( assert.ok(